购物车不该直接用 MySQL 存临时数据,未登录用户应使用 localStorage + Redis,仅登录用户的长期购物车或下单前确认才写 MySQL;表设计需用 session_id 替代 user_id,建联合索引 (session_id, product_id),价格实时读取,禁用临时表和内存表。

购物车该不该用 MySQL 存?先看场景
绝大多数中小型电商的「未登录用户购物车」不适合直接写 MySQL,因为会带来大量短生命周期的 INSERT/DELETE,拖慢主库、撑爆连接数、增加锁竞争。真正该进 MySQL 的,只有登录用户的长期购物车(比如「收藏夹式购物车」),或订单生成前最后一步落库确认。
临时购物车更合理的路径是:前端 localStorage + 后端内存缓存(如 Redis)兜底,MySQL 仅作为最终持久化层——不是“存购物车”,而是“存下单依据”。
如果硬要用 MySQL 存临时购物车,怎么设计表结构?
常见错误是建一张 cart_items 表,用 user_id 字段区分登录/未登录用户,结果未登录用户乱填 0 或 NULL,导致索引失效、统计不准、清理困难。
- 未登录用户必须用唯一标识:前端生成 UUID 或设备指纹(如
navigator.userAgent + screen.width拼接哈希),存在session_id字段里,**不能依赖 user_id** -
cart_items表必须有联合索引:(session_id, product_id),避免查某用户某商品时全表扫 - 加
created_at和updated_at,配合定时任务清理 30 分钟无更新的记录(别用ON DELETE CASCADE清 session,容易锁表) - 不要在购物车表里存价格——价格应实时从
products表读,否则下单时发现价格已变,又得回滚
MySQL 临时表 or 内存表?别踩这两个坑
有人想用 CREATE TEMPORARY TABLE 或 ENGINE=MEMORY 来“加速”,实际几乎没用,还埋雷:
-
TEMPORARY TABLE只对当前连接可见,HTTP 请求是短连接,下一次请求就找不着表了,根本没法跨请求存购物车 -
MEMORY表重启即丢,且不支持TEXT/BLOB,无法存复杂 SKU 属性;更关键的是,它用的是固定长度行存储,空字段也占空间,内存浪费严重 - 真正需要“临时性”的场景,应该交给应用层控制生命周期(比如 Redis 的
EXPIRE),而不是靠 MySQL 强撑
什么时候必须把购物车写进 MySQL?
只有两个刚性需求绕不开 MySQL:
- 用户登录瞬间合并本地购物车:此时要把前端传来的
session_id对应的所有项,INSERT ... ON DUPLICATE KEY UPDATE到用户真实cart_items表,注意用product_id + user_id做唯一键,避免重复插入 - 下单前最后一次校验库存和价格:必须查 MySQL 主库(不能读从库),且要加
SELECT ... FOR UPDATE锁住相关product_id行,防止超卖——但这步不是“存购物车”,是“锁库存”
其他所有中间状态,包括加减数量、选中状态、优惠券绑定,都不该强依赖 MySQL 实时写入。数据库不是缓存,也不是消息队列。










