多是本性不肯隨衆的緣由,我對於程序員面試中動輒就是考察併發上千萬級別的QPS向來嗤之以鼻,好像國內的應用都是那麼多用戶量同樣,其實併發達到千萬,百萬以上的應用能有幾個?html
絕大多數的程序員面臨的只是解決百級、千計、萬級的併發量,與其純粹爲了面試學一些空中樓閣,不如腳踏實地的學習如何解決眼前實際問題。mysql
而對於我,最能激發我學習動力的不是什麼造火箭面試,而是可以解決實際的問題。linux
哪怕看起來沒那麼高大上的場景,只要能解決產品給客戶帶來的痛點,就能給作技術的人帶來很大的成就感!程序員
言歸正傳,公司最近開發的一個用於學生在線考試的產品正好面臨這樣的問題。面試
1. 打開應用:學生使用平板電腦打開app,並進入考試列表頁面(原生),這裏後臺讀取操做是查詢本課程目前的考試 2. 開始考試:當某一考試開始時間到達,考生能夠點擊開始答題按鈕進入考試,此時,會將整個試卷的內容都拉取下來,並存儲到安卓本地的DB中(這裏當讀取的併發很大) 3. 作題:學生開始答題,當學生每點擊下一題時,會將本題的答案保存到後臺,成功後會跳轉到下一題(寫入的併發壓力大) 4. 提交:學生答完全部題目後,會在最後一題點擊提交按鈕,保存最後一題,並自動計算得分
因此讀取的問題鎖定在第2步,也就是拉取試卷的過程,那麼首先考慮能否使用緩存,而不是每次都去DB存儲讀取,由於試卷一旦創建,數據是固定的,而不會去更新,契合緩存使用的場景。進一步思考爲何MySQL沒用使用緩存呢,原諒個人無知,查閱了一下才知道,項目使用的MySQL5.7.27中默認參數以下sql
innodb_buffer_pool_size=8M innodb_buffer_pool_instances=8
innodb_log_file_size=48M
調整後參數以下(關於這幾個參數,請參考後續的原理解析):數據庫
innodb_buffer_pool_size=5G innodb_buffer_pool_instances=8 innodb_log_file_size=256M
磁盤讀的壓力問題解決。windows
雖然這裏的解決方案都不是那麼「高大上」的技術手段,但正是由於這樣的契機,激發了我係統學習MySQL的興趣後端
在試圖解決以及避免考試中出現性能問題總,總結了幾個比較重要的優化措施緩存
緩存相關
1. innodb_buffer_pool_size = 下面兩個參數乘積的整數倍,InnoDB引擎下能夠緩存索引和數據塊,若是是專用DB服務器,能夠最大設置到OS內存的80%。5.7.5以後能夠動態調整
此參數也不宜過大,由於過大會致使髒頁太多,DB關閉時會比較慢,開啓的預熱也比較慢,因此上面的設置實際上是有點大的 2. innodb_buffer_pool_chunk_size (5.7.5以後引入,默認128M) 3. innodb_buffer_pool_instances (默認8)
日誌相關
1. innodb_log_buffer_size 當存在較大數據量的事務提交的時候,修改此參數會下降磁盤寫
2. innodb_flush_logs_at_trx_commit (默認爲1,對寫性能要求高而對數據不是特別敏感時能夠改成0或者2)
參考:https://dev.mysql.com/doc/refman/5.7/en/innodb-buffer-pool-resize.html
當執行一條查詢SQL時,步驟以下(如下圖表參考極客,若有侵權,請聯繫)
今後圖中瞭解數據查詢爲什麼走緩存快的多了,由於省去了分析、優化、執行等過程,不與存儲引擎交互,固然也就沒有磁盤讀了。
固然,這也是項目中增大innodb_buffer_pool_size的緣由
充分利用MySQL影響性能的參數
這裏也有必要提下MySQL的日誌機制(分爲redoLog和binLog),如下是SQL更新的過程
>項目中沒有調整innodb_flush_logs_at_trx_commit默認值1(表示每次事務提交都會持久化到磁盤中),由於咱們的數據用於學生考試,對精確性有必定的要求。
>若是磁盤寫壓力比較大,而平板端提交等待時間長的話(好比最後提交試卷),應該調大innodb_log_buffer_size,這個參數其實只是對於單次commit比較大的數據有用,表示在真正commit前若是達到了這個參數的峯值,會先寫入磁盤中,極大下降單次提交的效率。這個參數在版本更新中多次增大,在5.7.6以後默認爲16M,足以應對絕大多數狀況。
>項目增大innodb_log_file_size(默認48M),是爲了減小磁盤的IO,這個參數控制的是redoLog的文件大小,參考此圖()
圖中innodb_log_files_in_group設置爲4(默認2)
每個log文件的大小爲innodb_log_file_size,若是設置太小,checkpoint到達(覆蓋以前的日誌)後,就會先擦除掉以前的redoLog,而後再往前寫。
因此若是這個參數太小,就會常常下降磁盤IO,推薦設置爲innodb_buffer_pool_size的1/4
慢查詢:查詢時間長於long_query_time參數的設置(默認10秒),查詢方法:show variables like 'long_query_time';
查看慢查詢日誌(slow_query_log):show variables like 'slow_query%'; (默認不開啓)
慢日誌開啓方法:my.cnf中設置slow_query_log =1 以及slow_query_log_file的路徑
而後經過mysqldumpslow來找到執行慢的SQL
下面總結幾條創建索引的通常規則
> 利用主鍵索引來避免使用非主鍵索引(二次查詢)
> 利用覆蓋索引來避免回表
> 利用前綴索來避免索引的字段過長
> 使用短字段創建索引
> 避免使用使索引失效的語句,好比now()函數、用戶自定義函數、存儲函數、用戶變量、臨時表、mysql庫中的系統表
至於什麼B樹、MySQL的B+樹等裝逼的知識,不在本文討論範圍內,感興趣的本身去查
監控MySQL的運行狀態(找到須要優化的地方)
show global status; // 顯示全部狀態,下面列出幾個跟性能關係比較大的status
show status like 'Threads%'; // 顯示的Threads_connected可用來標示併發數
show status like '%conn%' // 顯示的Connections是全部嘗試鏈接的數量
show processlist 顯示正在執行的MySQL鏈接,記錄數與上面的Threads-connected相等
> 對應的配置項能夠在配置文件中修改(windows的my.ini,linux的my.cnf),好比max_connections等
關於事務,儘可能避免死鎖和減小鎖數據的時間
須要瞭解如下內容
> MySQL默認都是行鎖
> 行鎖都是對索引的鎖
> 不要把查詢語句放在事務開啓和提交之間
> 默認鎖級別爲REPEATABLE READ(可重讀),可經過更改TRANSACTION ISOLATION LEVEL,下面的四種級別的鎖,就再也不贅述了
SERIALIZABLE(序列化)、REPEATABLE READ(可重讀)、READ COMMITTED(提交後讀)、READ UNCOMMITTED(未提交讀)
MySQL的其餘基本原理
此文會持續更新,直到把全部我認爲對實際項目有幫助的地方總結完畢!
參考:
https://dev.mysql.com/doc/refman/8.0/en/server-status-variables.html#statvar_Connections
http://www.javashuo.com/article/p-azpvgegy-k.html