關於秒殺mysql
隨着雙11活動的不斷髮展,小米飢餓營銷模式的興起,「秒殺」已經成爲一個熱點詞彙。在一些活動中,熱銷商品會以驚人的速度售罄,好比最近本人在搶購美圖M4手機,12點開賣,1分鐘以內就被售罄。web
對於關注數據庫的本人來講,更關心的是如何高效的實現秒殺應用。以前淘寶在2013年的數據庫大會上分享過他們的秒殺方案,修改MySQL數據庫源碼來實現高效的秒殺應用。可是,那篇分享過於高大上,沒有給出具體的實現過程。另外,從其餘渠道打聽到的是這個方案並無在生產環境上線,不知道有沒有其餘知道內幕的小夥伴,具體來講說淘寶的方案是否有上線。sql
固然,有多種方法來優化秒殺應用,好比使用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;
在作秒殺時,最主要是對庫存表進行操做,在操做前可能須要插入一些其餘操做,好比日誌等,而後就是對庫存表進行更新。下圖顯示增大併發量的狀況下,事務處理的性能:
memcached
顯而易見的是隨着併發量的增大,事務處理的性能越差。這和淘寶以前分享的數據基本一致。致使其中的緣由就是秒殺是對同一件商品進行更新,須要對同一行記錄加鎖,所以秒殺操做雖然是並行的,可是在數據庫層面是串行的。工具
隨着併發的不斷增大,不斷髮生事務的鎖等待與喚醒操做,致使性能的急劇降低。若是經過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存儲引擎層的併發量。的確,經過這個參數能夠限制進入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數據庫層使用線程池技術來保證大併發量下的性能
調整參數thread_pool_oversubscribe用來進一步提高性能
MySQL企業版提供了線程池插件,可是須要額外的費用。小夥伴們可使用開源的MySQL版本InnoSQL,其免費提供了線程池,能夠保證應用在大併發量下依舊保證應用的穩定性,特別是對於秒殺類的應用。