如何并发安全地实现1-[最大N]关联限制?信号量表方案是否合理?
关于用Person表维护关联计数的并发方案评估
你的方案不是不良实践,反而在高并发场景下是一种合理且高效的解决方案,具体分析如下:
方案的合理性
- 彻底解决竞态问题:通过
SELECT num_widgets FROM Person WHERE id=X FOR UPDATE锁定Person行,不管该用户有没有关联过Widget,都能拿到行锁,从根源上避免了两个事务同时读取到旧计数的问题。 - 性能更优:省去了每次操作都要对
Person_Widget表执行COUNT(*)的开销——尤其是当关联数据量较大时,COUNT(*)会扫描大量行,直接读取维护的字段能大幅提升操作效率。
必须注意的细节
- 强事务绑定:所有修改
Person_Widget关联表的操作(插入/删除),必须和更新num_widgets的操作放在同一个事务中执行,绝对不能拆分,否则会出现计数与实际关联数不一致的情况。 - 初始化与校验机制:
- 新增Person记录时,
num_widgets字段必须默认设为0; - 定期做数据校验:比如写个定时任务,对比
num_widgets的值和Person_Widget表中对应用户的实际关联数,一旦发现不一致就修复——避免因异常事务回滚、程序bug等导致的数据偏差。
- 新增Person记录时,
- 权限约束:限制直接修改
num_widgets字段的权限,只能通过指定的业务逻辑接口来更新,防止人为误改破坏数据一致性。
其他可选方案参考(供对比)
如果不想在Person表加字段,也可以尝试带条件的插入语句:
INSERT INTO Person_Widget (person_id, widget_id) SELECT X, Y FROM DUAL WHERE (SELECT COUNT(*) FROM Person_Widget WHERE person_id=X) < 5;
但这种方式在InnoDB默认的可重复读隔离级别下,COUNT(*)是快照读,依然可能出现竞态,可靠性不如你提出的方案。
内容的提问来源于stack exchange,提问作者JMC
相关产品推荐
相关产品推荐

