开篇同一个用户并发扣款时,有一定概率出现数据不一致,可以使用CAS乐观锁的方式,在不降低吞吐量,保证数据的一致性:
UPDATE t_yue SET money=$new_money
WHERE uid=$uid AND money=$old_money;
不能采用直接扣减的方式:
UPDATE t_yue SET money=money-$diff WHERE uid=$uid;
当然,更通用的方式,可以使用版本号来实现CAS乐观锁:
UPDATE t_yue SET money=$new_money,ver=$ver_new
WHERE uid=$uid AND ver=$ver_old;
对于这个CAS乐观锁方案,很有朋友有疑问:当并发量高时,版本号比对会导致大量的更新失败,这个方案不适用于高并发场景吗? 究竟是不是这样呢?大家对高并发是不是有什么误解呢?今天来聊一聊这个话题。
先分析三个业务场景。 一、社交通讯社交通讯的一些核心业务有:
个人:user(uid, user_info, …)
好友:user_friends(uid, friend_id, …)
加入的群:user_groups(uid, group_id, …)
群:group(gid, group_info, …)
群成员:group_members(gid, uid, …)
个人消息:msgs_user(msg_id, uid, …)
群消息:msgs_group(msg_id, gid, …)
select friend_id from user_friends where uid=$uid;
在用户量很大,并发量很大时,不同用户/群/消息数据的读写并没有锁冲突。画外音:10W个用户同时读写,彼此没有锁冲突。 只有当,同一个用户,很短的时间内,有大量并发时,才可能存在锁冲突。画外音:例如,1个用户,1秒钟读写1W次。 二、微博微博的核心业务是feed流:发消息,写操作
刷消息,读操作
在用户量很大,并发量很大时,会有一定数据的读写锁冲突。
画外音:不像社交通讯,基本是读写自己的数据,微博要写自己的数据,读别人的数据。 三、1230612306的核心业务是:查票,读操作
买票,写操作
stock(id, num) // 某一列车有多少张余票
在用户量很大,并发量很大时,有极大的锁冲突。画外音:这个业务,数据量并不大。 这类“秒杀”业务,如果不做特殊的优化,数据库很容易死锁卡死,没有任何人能买票成功。画外音:要做什么特殊的优化呢? 收尾社交通讯,微博、12306,同样是高并发业务,就数据存储锁冲突来说,各自的难度,数据不一致的概率是不同的。画外音:你不能说,社交通讯不是高并发业务吧。 回到开篇,使用CAS乐观锁进库存扣减:UPDATE t_yue SET money=$new_money,ver=$ver_new
WHERE uid=$uid AND ver=$ver_old;
高并发的扣款场景,可以使用CAS乐观锁,采用select&set方式进行扣款,既能够保证吞吐量,又能够保证一致性。
本文转自“架构师之路”公众号,58沈剑提供。