Redis Hash 存购物车的核心是采用 key→field→value 三层结构:key 为 cart:{userId},field 为商品 SKU ID,value 存数量或 JSON 字符串,避免序列化整个购物车带来的读改写、并发覆盖和性能问题。

Java 中用 Redis Hash 存购物车,核心是把“用户-商品-属性”映射成 key → field → value 的三层结构,而不是把整个购物车序列化成一个 JSON 字符串存 String 类型。这样做能避免读改写、并发覆盖、性能瓶颈等问题。
Key 和 field 的设计要简洁明确
Key 表示谁的购物车,field 表示哪件商品,value 存具体信息:
-
Key:固定格式
cart:{userId},比如cart:1001;未登录用户可用cart:temp_{sessionId} -
Field:推荐用商品 ID 或 SKU ID,如
sku:88921,不要拼接空格、斜杠或复杂字符串,避免解析歧义 -
Value:必须是字符串。简单场景直接存数量(如
"3");需要扩展属性(价格、选中状态、规格)就存 JSON 字符串,例如:{"count":3,"selected":true,"price":29.9}
用 RedisTemplate 操作 Hash 的常用方式
Spring Boot 项目中通常用 RedisTemplate 或 StringRedisTemplate,推荐绑定操作提升可读性:
- 获取绑定对象:
BoundHashOperations<String, Object, Object> ops = stringRedisTemplate.boundHashOps("cart:1001"); - 添加或更新单个商品:
ops.put("sku:88921", "{\"count\":1,\"selected\":true}"); - 原子增减数量(适合“+1”“-1”):
ops.increment("sku:88921", 1L);—— 注意:这个只对纯数字 value 有效,JSON 不支持 - 批量写入多个商品:
ops.putAll(Map.of("sku:88921", "...", "sku:77230", "...")); - 查单个商品:
ops.get("sku:88921") - 查多个指定商品:
ops.multiGet(Arrays.asList("sku:88921", "sku:77230")) - 删某个商品:
ops.delete("sku:88921") - 清空购物车:
stringRedisTemplate.delete("cart:1001")
别踩这些常见坑
实际开发中容易忽略但影响明显的细节:
立即学习“Java免费学习笔记(深入)”;
-
HGETALL 要慎用:购物车商品一多(比如超 50 件),
HGETALL返回数据量大、网络开销高、客户端可能 OOM;首页/列表页优先用HMGET拉指定几个 SKU - value 类型别混淆:Hash 的 value 只能是字符串,不能直接塞 Java 对象;存 JSON 就统一序列化(如 Jackson),取出来再反序列化
- Redis 版本兼容注意:HSET 多字段写入在 Redis 4.0+ 才支持;老版本要用 HMSET(但已废弃),建议升级或封装兼容逻辑
-
并发更新要分场景处理:单纯数量增减用
HINCRBY是原子的;涉及多个字段(如同时改数量+选中状态)就得用 Lua 脚本或应用层加锁
扩展建议:什么时候该存 JSON,什么时候只存数字?
取决于业务复杂度和操作频率:
- 如果购物车只管“加减数量”“勾选结算”,且前端自己维护其他信息(如价格从商品服务实时拉),value 存纯数字最轻量、最高效
- 如果要支持“规格组合”“优惠价快照”“跨端一致性”,value 存 JSON 更灵活,但每次修改都要完整序列化,且无法用 HINCRBY 原子增减数量
- 折中方案:数量单独用一个 Hash(
cart:count:1001),其他元数据另存一个(cart:item:1001),按需组合


















