
业务场景是这样的,单一用户的账号,可能会牵扯到并发问题。 就是在同一秒内,该用户的余额会频繁更新。 首先加锁这种情况不太适合。 单纯从 mysql 自身这个问题能否解决? 或者说把金额存入内存。
这是知乎的一个提问
1 fkdog 2021-12-25 11:44:21 +08:00 单个用户频繁更新算不上并发。直接加行锁完事了。 就算一秒钟更新 20 下,行锁都是绰绰有余。 你觉得不合适大概率只是你自己觉得,跑一下压测就知道了,大多数情况只是杞人忧天。。 |
2 0ZXYDDu796nVCFxq 2021-12-25 11:48:56 +08:00 每次的变动还得计流水吧 |
3 Saxton 2021-12-25 11:57:30 +08:00 “就是在同一秒内,该用户的余额会频繁更新。 首先加锁这种情况不太适合。” 你这种情况就适合的就是行锁,为什么不太适合 |
4 notejava 2021-12-25 12:12:00 +08:00 乐观锁 |
5 swcat 2021-12-25 12:30:32 +08:00 加个版本, update xx_table set balance = :balance where id = :id and version_id = :version_id 根据实际情况修改 或者加个 先锁住: select for update 再判断: 这个也得判断下, 判断状态能否更新, 结果得大于 0 吧, 状态可以改吧.... 后更新: 更新数据 id 为为主键 |
6 mitu9527 2021-12-25 13:52:18 +08:00 一般代码都是先读后写,所以除非你代码就是纯更新,否则先去开事务。 搞清楚并发问题所竞争的资源到底是啥。到底是这条记录还是这条记录里面的金额。 如果只是金额,看看能不能用 update ... set mOney= money + n 解决。 如果上面的方式不行,可以考虑用悲观并发控制策略和乐观并发控制策略,也就是大家常说的悲观锁和乐观锁。选那种,取决于发生的频率和引发的后果到底有多大。 悲观锁最简单的就是用 select for update 或者 redis 来实现,自己根据情况选,两种方式也都不是完美的,引入那种你认为合适的就可以了。 乐观锁一般就是用版本号来实现。 根据情况看看是否有必要做幂等判断,比如状态判断什么的。 |
7 7911364440 2021-12-25 14:36:11 +08:00 把金额也作为更新条件呢? update t set mOney= money+n where id = :id and mOney= :money |
8 awanganddong OP 业务场景就是用户之间赠送礼物,比如同一秒多个用户给一个用户 A 送礼物。 如果这时候加行锁的话,用户这条记录就被锁定了。 问题就在于用户这张数据表属于热表,该条记录的其他字段在其他业务也有更新操作。 担心行锁的话,就会出现死锁的情况。 更可怕的一点,我们这边是主从库配置,现在我暂时把代码切换成查主库,可以降低该概率的发生。 现在对用户操作,都有日志记录。 既然如此,现在我能发现问题,但是对于怎么成本最小的解决这个问题,或者优化这个问题,现在没有太大思路。 |
9 awanganddong OP 类似于客观锁这种机制,所带来问题就是用户操作的失败。 这从代码层面其实没有太大问题,但是对于产品侧,用户体验方面,这个问题就比较严重。 如果乐观锁校验失败,这个就更新失败。 |
10 awanganddong OP 我先压测下关于频繁更新的行锁情况吧。这个很靠谱的。 |
11 sagaxu 2021-12-25 17:06:12 +08:00 行锁每秒 10W 次是扛得住的,高配机器能做到 20W 次 |
12 fiypig 2021-12-25 19:04:46 +08:00 via iPhone 行锁就可以,又不是表锁 |
13 git00ll 2021-12-25 20:21:04 +08:00 for update 没问题的,每秒 4000 次 |