Redis 存购物车优于 Session,因其支持分布式、毫秒响应;应使用 Hash 结构(如 cart:1024),field 为 goods_{id}_attr_{attr_id},配合 Lua 脚本保证并发安全,并预留 ext 字段兼容扩展。

购物车用 Redis 存,为什么不能只存 Session?
Session 存购物车在小流量、单机部署时能跑通,但一上生产就容易出问题:session_start() 阻塞、跨请求丢失、负载均衡下会话不一致。Redis 是共享存储,天然支持分布式,且 SET/HGETALL 等操作毫秒级响应,适合高频读写的购物车场景。
关键点在于:不是“能不能用 Redis”,而是“怎么避免用错结构”。比如把整个购物车 JSON 字符串塞进一个 string 键里,后续增删改都得全量读写解码,性能差还容易并发覆盖。
推荐用 Hash 结构存单用户购物车
ThinkPHP 的 Cache::store('redis')->handler() 返回原生 phpredis 实例,可直接调用 hSet、hGetAll 等方法。每个用户对应一个 Hash 键,如 cart:1024,商品 ID + 属性 ID 拼成 field,值为 JSON 字符串或序列化数组。
- 添加商品:
$redis->hSet('cart:1024', 'goods_1001_attr_5', json_encode($item)) - 更新数量:
$redis->hGet('cart:1024', 'goods_1001_attr_5')→ 解码 → 修改goods_number→ 重新hSet - 批量查所有:
$redis->hGetAll('cart:1024')→ 一次拿到全部,比多次GET省连接开销 - 删除某项:
$redis->hDel('cart:1024', 'goods_1001_attr_5')
注意:Hash 的 field 名要唯一且可预测,推荐用 goods_{id}_attr_{attr_id} 格式,避免用自增 ID 或随机字符串,否则无法精准定位。
立即学习“PHP免费学习笔记(深入)”;
过期时间必须设,但别设太短
用户未登录时购物车存在 Cookie 或本地缓存,登录后应合并到 Redis;此时若直接 setex 覆盖旧键,可能清掉正在结算的订单数据。更稳妥的做法是:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 登录后先
$redis->ttl('cart:1024')查剩余有效期,若大于 1 小时,保留原过期时间 - 新用户首次加购,用
$redis->expire('cart:1024', 43200)(12 小时),比默认 24 小时更可控 - 用户主动清空购物车时,用
$redis->del('cart:1024'),而不是设为 0 秒过期——后者仍会短暂残留键
别依赖 Redis 的 maxmemory 策略自动淘汰:购物车属于用户关键数据,该删就得立刻删,不能等 LRU。
并发修改商品数量时,得加锁或用 Lua 原子脚本
两个请求同时对同一商品执行“+1”操作,仅靠 PHP 层读-改-写会丢数据。简单方案是用 hIncrBy,但只适用于整型字段(如数量);复杂结构(含价格、运费、规格名)必须原子更新。
推荐用 Lua 脚本封装逻辑,例如:
local cart_key = KEYS[1]
local item_field = ARGV[1]
local new_num = tonumber(ARGV[2])
local item_data = cjson.decode(redis.call("hGet", cart_key, item_field))
item_data.goods_number = new_num
item_data.subtotal = new_num * item_data.price
redis.call("hSet", cart_key, item_field, cjson.encode(item_data))
return 1调用方式:$redis->eval($lua_script, 1, 'cart:1024', 'goods_1001_attr_5', $newNumber)。Lua 在 Redis 单线程中执行,天然原子性,且避免网络往返。
真正容易被忽略的是:购物车数据结构一旦定型(比如用了 Hash),后续加新字段(如优惠券信息、赠品标记)就得兼容旧数据,最好一开始就预留 ext 字段存 JSON 对象,别硬拆成多个 field。


















