場景描述
秒殺,搶coupon,大轉盤等業務,會吸引大量用戶同時高並發訪問,而這些業務都需要對數據庫有寫操作,在高並發下,這個類似的場景一般都有下面特點:
一、寫庫前邏輯校驗限制,比如秒殺的庫存檢驗,coupon發放量檢驗等邏輯復雜。
二、更新數據庫時候,集中更新在某些特定的記錄上面,造成少量數據同時大量寫請求。
搶coupon寫操作的實現方案
分布式鎖方案
這種方式是最常用的方式,一般並發量不大的時候適合用這種方式,這種分布式鎖特點:
- 實現簡單:通過第三方的原子操作鎖(redis, zookeeper)在業務開始之前加鎖,業務結束后釋放鎖來完成。
- 性能低:在分布式鎖的這個階段中,需要相同鎖的資源相當於串行執行。鎖中間的業務耗時多,或者鎖的粒度大會嚴重降低服務性能。
關鍵點數據校驗DB樂觀鎖方案
這個方式在業務邏輯上,區分上一種方式,將校驗數據部分區分開,找出關鍵點數據的校驗。關鍵點數據或者叫做易變數據,是指經常變化的,一般每次寫操作都會導致這個數據發生變化。
找出這個關鍵點數據,將關鍵點數據的校驗和更新操作合並為一個數據庫原子操作,例如庫存檢查操作update table set count = count-1 where count-1 > 0 and item=?,將這個檢驗更新操作,在數據庫層面添加樂觀鎖方式實現,如果執行更新成功,再執行后續操作,如果失敗表示校驗失敗,不通過檢查。
特點:
- 業務實現不復雜,關鍵是找出可以合並更新為一個的原子數據庫操作。找不出的話就不適合這個方案。
- 性能瓶頸點在於更新操作,一般是大量並發,修改相同的記錄,例如大量用戶領取相同的一種優惠券,關鍵點合並檢查更新在數據庫里面都會串行等待這個記錄的寫鎖。
關鍵點數據橫向分表校驗DB樂觀鎖方案
本方式是優化了上面方案的關鍵點並發更新的情況,擴展數據庫中的關鍵點數據表,從一個數據表橫向擴展為10,100個,這樣相比於上面方案,這個的並發量相當於提升了10倍,100倍。
在數據創建階段就將關鍵點的數據(庫存,領取量)按照一點規則分配到各個表中。在執行綁定coupon的時候,會隨機或者其他規則從一個關鍵點表中更新數據,如果不成功,重試2次其他數據表更新。
特點:
- 性能提升:通過橫向擴展表,將並發提高n倍
- 實現比較服務,在數據庫層面將關鍵點數據橫向拆分成多表數據,業務處理時候要更新不同表中數據。
- 數據可靠:上面幾種方式都是直接操作數據庫,在極端情況下服務宕機不可用,數據都會保存數據庫中。
通過redis原子操作校驗關鍵點數據方案
該方案是通過redis高性能incr, decr原子操作實現關鍵點數據檢查的方式實現。在數據創建時候將關鍵點數據保存到redis中,處理業務時候,執行到檢查易變數據時,直接更新通過redis.decr操作校驗數據,通過后將后續操作通過可靠MQ異步處理。MQ的消費端在更新關鍵點數據時候,將多條記錄合並一次更新,比如50條扣減數量,一次update更新,提高數據庫的寫性能。后續的其他數據庫操作也都批量操作。
特點:
- 數據可靠性依賴redis, mq,要保證redis可靠穩定。
- 數據庫層面不需要擴展,業務相對簡單些。
無關鍵點數據檢查更新合並操作的業務
這個情況,不能將關鍵點數據的檢查和更新使用同一個數據庫原子操作完成,咱們現在coupon服務就是這種情況,每個coupon的已經發放數量在 coupon表里面沒有記錄,而是通過每次count關聯表來計算出來的,這個關鍵點數據檢查就是每次綁定前都count關聯表數量,然后對比發放最大人群數量。這種情況都是在獲取數量前加鎖,領取完成后釋放鎖,性能很低。
該方案是上面方案的簡化方式,通過在數據創建階段,將初始數據更新到redis中,每次有領取coupon的請求過來,在關鍵點數據檢查時候,redis.incr/decr方式,判斷數據是否檢查通過。通過之后執行后續數據庫操作,這種情況不存在高並發修改少量數據情況,性能不受寫鎖限制。
特點:
- 數據可靠性依賴redis,要保證redis可靠穩定。
- 方案簡單。
- 查詢數據略慢,需要每次count關聯表數量。