(0) 如题

(1) 比如转账/扣款相关的业务,推荐用悲观锁还是乐观锁?

(2) 脑补了如下的乐观锁实现,可以满足并发转账和取钱需求吗?

(3) 如果能,那么和悲观锁 select for update 作对比哪个更好?

# 数据库初始状态
TRUNCATE account;
INSERT INTO account (id, name, balance, version)
VALUES (1, 'ichigo', 10, 1),
       (2, 'rukia', 10, 1);

# 并发事务 1 转出全部余额
START TRANSACTION;
SELECT balance, version FROM account WHERE id = 1;
SELECT version FROM account WHERE id = 2;
UPDATE account SET balance = balance - 10, version = 2 WHERE id = 1 AND version = 1;
UPDATE account SET balance = balance + 10, version = 2 WHERE id = 2 AND version = 1;
COMMIT;

# 并发事务 2 取出全部余额
START TRANSACTION;
SELECT balance, version FROM account WHERE id = 1;
UPDATE account SET balance = balance - 10, version = 2 WHERE id = 1 AND version = 1;
COMMIT;
举报· 522 次点击
登录 注册 站外分享
4 条回复  
yidinghe 小成 前天 17:20
select for update 只适用于一次操作一条记录的情况,千万不要用于一次更新一大堆记录。转账扣款相关大多数情况下是适合用 select for update 的,因为每次只操作一条记录。这个是我工作当中用过的,互联网电商平台。比如有几个操作来自不同的服务,同时更新一条记录,但各自更新不同的字段。这个时候用 version 其实没必要,因为业务上它们并无冲突,所以还不如老老实实排队拿锁,依次做各自的事,反正几个操作加起来也不到 0.1 秒。
xianyukang 楼主 初学 前天 18:31
@yidinghe 确实,网络上的资料也经常告诫人们,不要用 select for update 对一大片记录加锁,要走「 唯一索引 」对指定记录加「 行锁 」确保锁的范围很小。另外你说的场景中,为啥更推荐排队拿锁啊? 比如做全量更新时用 version 不是能确保查出来的数据没有过期吗?
enchilada2020 小成 前天 18:52
避免使用 select for update ,MySQL 在这块有大坑,07 年的 bug 到现在还没修,报错信息压根驴唇不对马嘴,害我排查好几周: https://stackoverflow.com/questions/44949940/solution-for-insert-intention-locks-in-mysql
ChoateYao 小成 前天 20:06
一个是高并发解决方案,一个是数据过期解决方案。 比如用户 A 、B 在同一时间读取记录 A ,用户 A 在 3 秒后提交数据;用户 B 在 10 秒后提交数据,这时候用户 B 提交的数据是过期数据,则不能让他提交成功。
返回顶部