You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Lettuce客户端读取AWS ElastiCache(Valkey引擎)缓存数据而无需指定键名?

如何使用Lettuce客户端读取AWS ElastiCache(Valkey引擎)缓存数据而无需指定键名?

嘿,针对你的场景我给你梳理下具体的实现方案哈~首先得明确:Valkey和Redis是兼容的,要“不指定具体键名”读取数据,本质是先拿到符合规则的键集合,再批量拉取对应的值。结合你的情况,缓存键都是user:xxx的格式,咱们可以用前缀匹配来过滤,而且绝对要优先用SCAN命令,别用KEYS——KEYS会一次性遍历所有键,在生产环境尤其是无服务器实例上很容易阻塞服务,影响性能,这一点一定要记牢!

方案一:用Spring Boot整合的RedisTemplate实现(推荐,因为Spring Boot已经帮你封装了Lettuce的连接配置)

假设你已经在Spring Boot里配置好了RedisTemplate<String, String>(用Lettuce作为底层客户端),可以直接写一个服务类来实现:

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

// 假设你已经定义好对应的User实体类
public class UserCacheService {
    private final RedisTemplate<String, String> redisTemplate;
    private final ObjectMapper objectMapper;

    // 构造注入Spring容器里的RedisTemplate和ObjectMapper
    public UserCacheService(RedisTemplate<String, String> redisTemplate, ObjectMapper objectMapper) {
        this.redisTemplate = redisTemplate;
        this.objectMapper = objectMapper;
    }

    public List<User> getAllUsersFromCache() throws Exception {
        // 1. 用SCAN迭代获取所有匹配`user:*`前缀的键
        ScanOptions scanOptions = ScanOptions.scanOptions()
                .match("user:*") // 精准匹配你的键前缀
                .count(100) // 每次迭代建议返回的键数量,不是精确值,可根据实际调整
                .build();

        List<String> userKeys = new ArrayList<>();
        // 执行SCAN迭代逻辑
        redisTemplate.execute((RedisCallback<Void>) connection -> {
            RedisKeyCommands keyCommands = connection.keyCommands();
            Cursor<byte[]> cursor = keyCommands.scan(scanOptions);
            while (cursor.hasNext()) {
                String key = new String(cursor.next(), StandardCharsets.UTF_8);
                userKeys.add(key);
            }
            cursor.close();
            return null;
        });

        // 2. 批量获取所有匹配键对应的值
        List<String> userJsonList = redisTemplate.opsForValue().multiGet(userKeys);

        // 3. 把JSON字符串反序列化为User对象列表
        List<User> userList = new ArrayList<>();
        for (String json : userJsonList) {
            if (json != null) {
                userList.add(objectMapper.readValue(json, User.class));
            }
        }
        return userList;
    }
}

方案二:用原生Lettuce客户端实现(如果没用到Spring的封装)

如果是直接用原生Lettuce客户端连接ElastiCache Valkey,代码大概是这样的:

import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisCommands;
import io.lettuce.core.ScanArgs;
import io.lettuce.core.ScanCursor;
import io.lettuce.core.api.StatefulRedisConnection;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;

public class LettuceValkeyReader {
    public static void main(String[] args) throws Exception {
        // 替换成你的AWS ElastiCache Valkey实例的端点和端口
        String valkeyUri = "redis://your-elasticache-endpoint:6379";
        RedisClient redisClient = RedisClient.create(valkeyUri);
        
        // 用try-with-resources自动管理连接关闭
        try (StatefulRedisConnection<String, String> connection = redisClient.connect()) {
            RedisCommands<String, String> syncCommands = connection.sync();
            ObjectMapper objectMapper = new ObjectMapper();

            List<String> userKeys = new ArrayList<>();
            ScanCursor cursor = ScanCursor.INITIAL;

            // 循环执行SCAN,直到遍历完所有匹配的键
            do {
                ScanArgs scanArgs = ScanArgs.Builder.matches("user:*").count(100);
                io.lettuce.core.ScanResult<String> scanResult = syncCommands.scan(cursor, scanArgs);
                userKeys.addAll(scanResult.getKeys());
                cursor = scanResult.getCursor();
            } while (!cursor.isFinished());

            // 批量拉取所有键对应的值
            List<String> userJsonList = syncCommands.mget(userKeys.toArray(new String[0]));
            
            // 反序列化为User对象
            List<User> userList = new ArrayList<>();
            for (String json : userJsonList) {
                if (json != null) {
                    userList.add(objectMapper.readValue(json, User.class));
                }
            }

            // 这里就可以处理拿到的userList了,比如打印或者业务逻辑处理
            for (User user : userList) {
                System.out.printf("用户ID: %d, 姓名: %s %s%n", user.getUserId(), user.getFirstName(), user.getLastName());
            }
        } finally {
            // 关闭客户端
            redisClient.shutdown();
        }
    }
}

几个生产环境的注意事项

  • 关于SCAN的count参数:这个只是给Valkey服务器的建议值,实际返回的键数量可能多也可能少,不用纠结具体数值,根据你的缓存键总数调整就行,比如键多的话可以设为500或者1000。
  • 避免全量遍历:如果不是必须,尽量不要用*匹配所有键,用精准的前缀(比如user:*)可以减少遍历的键数量,提升效率。
  • 反序列化问题:要确保你的User实体类和缓存里的JSON结构完全匹配,比如字段名大小写、数据类型(比如userId是Integer还是Long),不然会出现反序列化失败的情况,可以在实体类上用@JsonProperty注解来对齐字段名。
  • 无服务器版的特殊注意:AWS ElastiCache无服务器版有并发和资源限制,别在业务高峰期执行大规模的SCAN操作,尽量选低峰期或者拆分多次执行。

备注:内容来源于stack exchange,提问作者alex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 16:14:50