同樣是高并發,QQ/微博/12306的架構難度一樣嗎?
開篇同一個用戶并發扣款時,有一定概率出現數據不一致,可以使用CAS樂觀鎖的方式,在不降低吞吐量,保證數據的一致性:
- UPDATE t_yue SET money=$new_money
- WHERE uid=$uid AND money=$old_money;
更詳細的描述,詳見《并發扣款,如何保證數據的一致性?》。 不能采用直接扣減的方式:
- UPDATE t_yue SET moneymoney=money-$diff WHERE uid=$uid;
更詳細的描述,詳見《并發扣款一致性,冪等性問題》。 當然,更通用的方式,可以使用版本號來實現CAS樂觀鎖:
- UPDATE t_yue SET money=$new_money,ver=$ver_new
- WHERE uid=$uid AND ver=$ver_old;
更詳細的描述,詳見《并發扣款一致性優化,CAS下ABA問題》。
對于這個CAS樂觀鎖方案,很有朋友有疑問:當并發量高時,版本號比對會導致大量的更新失敗,這個方案不適用于高并發場景嗎?
究竟是不是這樣呢?大家對高并發是不是有什么誤解呢?今天來聊一聊這個話題。
先分析三個業務場景。
一、QQ
QQ的一些核心業務有:
- 個人: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, …)
這些信息的讀寫有一個特點,都會帶上uid/gid/msgid屬性。
例如,拉取好友列表:
- select friend_id from user_friends where uid=$uid;
在用戶量很大,并發量很大時,不同用戶/群/消息數據的讀寫并沒有鎖沖突。
畫外音:10W個用戶同時讀寫,彼此沒有鎖沖突。
只有當,同一個用戶,很短的時間內,有大量并發時,才可能存在鎖沖突。
畫外音:例如,1個用戶,1秒鐘讀寫1W次。
二、微博
微博的核心業務是feed流:
- 發消息,寫操作
- 刷消息,讀操作
微博業務顯然是讀多寫少的,在用戶刷消息時,自己feed流里的消息,是由別人發出的。
查看自己主頁feed流,最樸素的實現方法是:
- 拉取自己關注的用戶id_list;
- 拉取這些用戶最近N條消息;
- 將這N*id_list條消息排序;
- 返回第一頁消息,得到自己主頁feed流;
在用戶量很大,并發量很大時,會有一定數據的讀寫鎖沖突。
畫外音:不像QQ,基本是讀寫自己的數據,微博要寫自己的數據,讀別人的數據。
三、12306
12306的核心業務是:
- 查票,讀操作
- 買票,寫操作
- stock(id, num) // 某一列車有多少張余票
在用戶量很大,并發量很大時,有極大的鎖沖突。
畫外音:這個業務,數據量并不大。
這類“秒殺”業務,如果不做特殊的優化,數據庫很容易死鎖卡死,沒有任何人能買票成功。
畫外音:要做什么特殊的優化呢?
收尾
QQ,微博、12306,同樣是高并發業務,就數據存儲鎖沖突來說,各自的難度,數據不一致的概率是不同的。
畫外音:你不能說,QQ不是高并發業務吧。
回到開篇,使用CAS樂觀鎖進庫存扣減:
- UPDATE t_yue SET money=$new_money,ver=$ver_new
- WHERE uid=$uid AND ver=$ver_old;
只要有uid這個過濾屬性,即使10W用戶同時扣款,也不容易出現數據不一致。
只有當同一個用戶,同一秒鐘,有大量扣減時,才有一定幾率會沖撞,但也不會導致數據不一致。畫外音:有一位很可愛的水友,說萬一PC端和APP端同時下單怎么辦。
結論
高并發的扣款場景,可以使用CAS樂觀鎖,采用select&set方式進行扣款,既能夠保證吞吐量,又能夠保證一致性。
【本文為51CTO專欄作者“58沈劍”原創稿件,轉載請聯系原作者】