Spring Boot中如何为Redis键存储List/Set类型数据并保证线程安全?
方案实现与解答
完全可以使用Redis的List类型实现该需求,且Redis原生操作的原子性可以天然保证线程安全,比你当前逗号拼接字符串的实现方案性能、并发安全性都更高。
原方案的问题
你当前的实现存在两个明显缺陷:
- Java层面的
synchronized锁只能保证单JVM实例内的线程安全,如果是多实例部署的场景,不同实例同时读写Redis的USERS键依然会出现并发覆盖的问题 - 每次操作都需要全量读取字符串、拼接、再全量写回,随着存储的ID数量增多,操作开销会线性上涨
List类型实现方案
Redis原生的RPUSH命令天然匹配你的需求:当目标键不存在时,会自动创建空列表再追加元素;当目标键已经是列表类型时,会直接在列表尾部追加元素,且Redis单线程处理命令的特性保证了单个RPUSH操作的原子性,全局线程安全,不需要在应用层额外加锁。
代码示例
Spring Data Redis 版本
public void pushIdInRedis(String id) { // rightPush 对应Redis的RPUSH命令 redisTemplate.opsForList().rightPush("USERS", id); }
Jedis 版本
public void pushIdInRedis(String id) { try (Jedis jedis = jedisPool.getResource()) { jedis.rpush("USERS", id); } }
方案优势
- 操作性能更高:
RPUSH是O(1)时间复杂度的操作,无论列表存储的ID数量有多少,追加操作的耗时都保持稳定 - 全局线程安全:Redis命令的原子性不受应用实例部署数量影响,多实例并发操作也不会出现数据覆盖、丢失的问题
- 后续维护更方便:如果后续需要遍历ID、弹出头部ID、删除指定ID等操作,直接调用
LRANGE/LPOP/LREM等Redis原生命令即可,不需要自行拆分处理逗号拼接的字符串。
如果你的业务要求存储的用户ID不能重复,可以替换使用Redis的Set类型,调用
SADD命令,同样支持原子操作、不存在自动创建的特性,还能自动实现ID去重。
内容的提问来源于stack exchange,提问作者imsaiful
相关产品推荐
相关产品推荐

