秒殺應用的MySQL資料庫優化

2022-02-19 09:03:07 字數 2943 閱讀 7659

其實秒殺應用的資料庫層優化非常簡單,各個層面做好排隊即可,mysql企業版提供了執行緒池外掛程式,但是需要額外的費用。

小夥伴們可以使用開源的mysql版本innosql,其免費提供了執行緒池,可以保證應用在大併發量下依舊保證應用的穩定性,特別是對於秒殺類的應用。

關於秒殺

隨著雙11活動的不斷發展,小公尺飢餓營銷模式的興起,「秒殺」已經成為乙個熱點詞彙。在一些活動中,熱銷商品會以驚人的速度售罄,比如最近筆者在搶購美圖m4手機,12點開賣,1分鐘之內就被售罄。

秒殺的實現

對於關注資料庫的筆者來說,更關心的是如何高效的實現秒殺應用。之前**在2023年的資料庫大會上分享過他們的秒殺方案,修改mysql資料庫原始碼來實現高效的秒殺應用。但是,那篇分享過於高大上,沒有給出具體的實現過程。另外,從其他渠道打聽到的是這個方案並沒有在生產環境上線,不知道有沒有其他知道內幕的小夥伴,具體來說說**的方案是否有上線。

當然,有多種方法來優化秒殺應用,比如使用memcached的cas功能,但是這些方法都不能實現事務的特性。對於深受jim gray事務處理教育長大的一代,筆者覺得任何事情都應該事務的,不支援事務只不過能取得暫時的勝利,整個世界的哲學應該就是事務,即要麼全做,要麼全不做,不要處於乙個中間狀態。筆者的為人哲學就是,要麼不去設定乙個目標,否則這個目標一定會去實現。比如,筆者決定去讀博,那麼一定會完成這個學業。

筆者感覺雖然**沒有給出具體的實現方式,但是丟擲了秒殺應用對於資料庫壓力的問題所在,即大併發量下更新同一行資料的壓力。例如併發執行如下的sql語句模擬秒殺場景:

begin; 

insert into stock_log values 

select count from stock where id=1 and count>0 for update; 

update stock set count = count -1 where id=1 and count > 0; 

commit; 

在做秒殺時,最主要是對庫存表進行操作,在操作前可能需要插入一些其他操作,比如日誌等,然後就是對庫存表進行更新。下圖顯示增大併發量的情況下,事務處理的效能:

顯而易見的是隨著併發量的增大,事務處理的效能越差。這和**之前分享的資料基本一致。導致其中的原因就是秒殺是對同一件商品進行更新,需要對同一行記錄加鎖,因此秒殺操作雖然是並行的,但是在資料庫層面是序列的。

隨著併發的不斷增大,不斷發生事務的鎖等待與喚醒操作,導致效能的急劇下降。如果通過perf工具來觀察的話,應該可以觀察到類似如下的內容:

# 59.06% mysqld mysqld [.] lock_deadlock_recursive 

16.63% mysqld libc-2.13.so [.] 0x115171 3.09% mysqld mysqld [.] lock_rec_get_prev 

2.96% mysqld mysqld [.] my_strnncollsp_utf8 

...... 

可以發現鎖的死鎖檢測佔據了大部分的cpu時間,究其原因,就是因為鎖等待。

innodb_thread_concurrency

有小夥伴或許會知道可以通過innodb_thread_concurrency引數來控制innodb儲存引擎層的併發量。的確,通過這個引數可以限制進入innodb引擎層的事務數量,對比測試的話,效能上的確會有一定的提公升:

可以發現,將innodb_thread_concurrency設定為16,效能的確會有一定的提公升。併發執行緒數在128的時候,tps從原有的4300提公升為了7200,將近有65%的效能提公升。但是在256執行緒之後,效能依舊堪憂。

導致上述的原因是雖然在innodb儲存引擎層做了「限流」,但是mysql資料庫上層的執行緒依然需要等待喚醒。

執行緒池技術

業界提供了很多關於秒殺mysql的解決方案,然而非常的定製化,並且需要應用修改相信的程式,比如通過在sql語句中寫hint來進行排隊,而這種的排隊機制在我看來在低併發量下效能反而又會變差。因此,乙個通用的解決方案是採用執行緒池技術。

執行緒池可以在mysql上層限制住同時執行的mysql的事務數,這樣就解決了由秒殺而導致的資源競爭問題。例如,通過前面的測試,已經得知併發16執行緒時,秒殺可以有最好的效能,那麼這時使用者將執行緒池的大小設定為16,這樣就能獲得使用者預期想要的效能:

可以發現即使在4096個併發執行緒下,秒殺依然可以有近10000的tps。通過執行緒池技術,秒殺就是這麼簡單,無需任何應用端的修改。

但是執行緒池這裡有個引數thread_pool_oversubscribe,這個引數其實有點類似雲計算中「超售」概念,即mysql的執行緒池允許有額外的執行緒執行。該引數預設是3,之前thread_pool_size設定為16,那麼總共允許16*(1+3)=64個執行緒同時執行。這個引數的預設值本身沒有問題,但是對於秒殺應用來說確是不需要的,因為之前已經討論過,秒殺應用是序列的。所以將引數thread_pool_oversubscribe設定為1,秒殺應用還能有進一步的提公升:

可以發現在大併發的執行緒下,效能還能有10%~30%的提公升。

總結

其實秒殺應用的資料庫層優化非常簡單,各個層面做好排隊即可,如:

mysql企業版提供了執行緒池外掛程式,但是需要額外的費用。小夥伴們可以使用開源的mysql版本innosql,其免費提供了執行緒池,可以保證應用在大併發量下依舊保證應用的穩定性,特別是對於秒殺類的應用。

原文

秒殺系統 mysql 秒殺系統之資料庫優化

由之前的文章,我們可以看到資料庫為保證資料持久化,需要落盤,而該操作將會成為秒殺系統的瓶頸所在。那在資料庫層面如何進行優化呢,可以分為以下幾點來考慮 庫存拆分 將同乙個商品的庫存記錄拆分為多行甚至多個表,降低併發衝突。舉乙個簡單的例子 對業務請求中的userid計算hash取模後確定查詢哪個庫那張表...

秒殺系統 mysql 秒殺系統之資料庫

秒殺系統的資料庫中的庫存加減操作是最為關鍵的點。12年天貓雙十一的超賣事件,對平台的負面影響是非常巨大的。資料庫裡做庫存扣減,簡單的可以用以下sql來說明 update stock table set inventory inventory 1 where item id xx and invent...

資料庫應用優化

基本語句優化的10個原則 1 盡量避免在列上進行運算,這樣會導致索引失敗 2 使用join時,應該用小結果集驅動大結果集。同時把複雜的join查詢拆分成多個query。因為join多個表時,可能導致更多的鎖定和堵塞 3 注意like模糊查詢的使用,避免 4 僅列出需要查詢的字段,這對速度不會有明顯的...