下圖顯示增大併發量的情況下,事務處理的效能:
顯而易見的是隨著併發量的增大,事務處理的效能越差。這和**之前分享的資料基本一致。導致其中的原因就是秒殺是對同一件商品進行更新,需要對同一行記錄加鎖,因此秒殺操作雖然是並行的,但是在資料庫層面是序列的。
隨著併發的不斷增大,不斷發生事務的鎖等待與喚醒操作,導致效能的急劇下降。如果通過perf工具來觀察的話,應該可以觀察到類似如下的內容:
#可以發現鎖的死鎖檢測佔據了大部分的cpu時間,究其原因,就是因為鎖等待。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
......
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,其免費提供了執行緒池,可以保證應用在大併發量下依舊保證應用的穩定性,特別是對於秒殺類的應用。
節選自
資料庫高併發解決方法總結
資料庫高併發解決方法總結 2017年01月23日 10 33 23 乙個專案剛開始的時候是為了實現基本功能,隨著版本和功能的迭代,大資料和高併發成了軟體設計必須考慮的問題 本質很簡單,乙個是慢,乙個是等。兩者是相互關聯的,因為慢,所以要等,因為等,所以慢,解決了慢,也就解決了等,解決了等,也就解決了...
資料庫高併發解決方法總結
乙個專案剛開始的時候是為了實現基本功能,隨著版本和功能的迭代,大資料和高併發成了軟體設計必須考慮的問題 本質很簡單,乙個是慢,乙個是等。兩者是相互關聯的,因為慢,所以要等,因為等,所以慢,解決了慢,也就解決了等,解決了等,也就解決了慢。關鍵是如何解決慢和等,核心乙個是短,乙個是少,乙個是分流,最後乙...
併發競態的解決方法
有多個程序同時訪問同乙個驅動程式中的臨界資源的時候,競態就會產生了。競態產生的根本原因 1.對於單核cpu,核心支援搶占。2.多核cpu,核與核之間會產生競態 3.中斷和程序間也會產生競態 4.中斷和中斷間產生競態 中斷巢狀可以 arm架構不支援 一 中斷遮蔽 了解 中斷遮蔽只適合在單核cpu。中斷...