一、前言
作為程序員的你,數據庫作為一門必修課,而MySQL數據庫毫無疑問已經是最常用的數據庫了。系統的穩定、高效、高併發等指標,很大程度上取決於數據庫性能是否夠優,可見性能優化的重要性,這也就不難理解各位在任何一場面試中都會被問及到數據庫調優相關的問題。
因此,這就是我為何考慮寫該系列文章的主要原因,希望該系列文章(MySQL性能優化)能夠給你帶來收穫,讓你更系統、更全面的掌握MySQL性能優化的技能、技巧。該系列文章將會持續分享、更新,如果覺得現在或者將來可能對你有用,不妨持續關注、收藏。
在MySQL性能優化之前,你有必要重新再認識下MySQL,便於後續更容易理解MySQL性能優化中涉及到的知識點。本文將從MySQL架構、核心問題來針對性展開討論,這也將是MySQL性能優化系列文章的開篇之作。
二、MySQL邏輯架構
想深入探究MySQL之前,有必要了解一下MySQL的邏輯架構,邏輯架構圖如下:
MySQL的邏輯架構中,分為三層,如上圖紅色虛線框的三部分。
最上層架構並不是MySQL所獨有的,大多數基於客户端/服務器形態的系統或者服務,都有類似的架構,其中包含MySQL的連接處理、授權認證、安全控制等等。
第二層架構是MySQL中最為核心的部分,其中包括查詢解析、分析、優化、緩存以及所有的內置函數(如:日期、時間、函數等),所有跨存儲引擎的功能都在這一層實現,例如:存儲過程、觸發器、視圖等。
第三層架構是存儲引擎。存儲引擎負責MySQL中數據的存儲和提取,類似與Linux系統下的各種文件系統一樣,不同存儲引擎都有各自的優勢和劣勢,不同場景可選擇不同的引擎。不同存儲引擎之間是不會相互通信的,只是簡單地響應上層的請求。
三、如何控制高併發的讀寫?
無論何時,對於數據庫而言,高併發的讀寫操作是很常見的,針對同一條記錄在同一時刻進行修改、查詢操作,都會產生併發控制的問題,處理不當將會出現大量的髒數據。那麼,如何控制高併發的讀寫操作呢?
1.讀寫鎖
在我們學習任何一門語言時,針對處理併發問題都會選擇鎖機制來解決並加以控制,這也是解決併發控制的經典方法,MySQL也不例外。在MySQL處理高併發的讀或者寫時,可以通過實現兩種類型的鎖來解決,這兩種類型的鎖通常被稱為共享鎖(Shared lock)和排他鎖(exclusive lock),其實就是大家叫的讀鎖(read lock)和寫鎖(write lock)。
讀鎖,是共享的,也就是説是互相不阻塞的。多個請求在同一時刻可以同時讀取同一條記錄,而互不干擾。
寫鎖,是排他的,也就是説一個寫鎖會阻塞其他的寫鎖和讀鎖,避免在寫的過程中進行讀、再寫的操作,這更是出於安全的考慮,只有這樣才能確保數據的準確、乾淨。在數據庫中,每時每刻都在發生鎖定,當某次請求修改數據時,MySQL都會通過鎖來防止其他請求讀取同一數據。
2.鎖策略
有了鎖的機制,就能更好的控制高併發的讀寫操作,我們都知道鎖也是有範圍的,鎖定對象範圍的選擇,更具有挑戰性。儘量只鎖定需要修改的部分數據,而不是所有數據,這也是選擇鎖定對象範圍最想滿足的。鎖定範圍越精確,鎖定的數據量就越小,則系統的併發程度越高,加鎖本身消耗的資源也就越小。
上述提到的無非就是設定鎖的粒度,而MySQL則提供了多種選擇,每種MySQL存儲引擎都可以實現自己的鎖策略和鎖粒度。下面將介紹兩種最常用的鎖策略。
表級鎖是MySQL最基本的鎖策略,並且是開銷最小的策略,它會鎖定整張表。一個請求在對錶進行寫操作(插入、修改、刪除等)前,需要先獲得寫鎖,此時會阻塞其他請求對該表的所有讀寫操作。只有沒有寫鎖時,其他請求才能讀取並獲得讀鎖,讀鎖之間是不相互阻塞的。
儘管存儲引擎可以管理自己的鎖,而且MySQL本身還會使用各種有效的表級鎖來實現不同的目的。例如,諸如之類的語句就使用了表級鎖,而忽略存儲引擎的鎖機制。
開銷小,加鎖快;不會出現死鎖;鎖定粒度大,發出鎖衝突的概率最高,併發度最低。
表級鎖,更適用於以查詢為主,只有少量按索引條件更新數據的應用。
行級鎖可以最大程度地支持併發處理(同時也帶來了最大的鎖開銷)。
開銷大,加鎖慢;會出現死鎖;鎖定粒度最小,發生鎖衝突的概率最低,併發度最高。
行級鎖,更適合於有大量按索引發更新少量不同數據,同時又有併發查詢的應用,如一些在線事務處理系統。
四、MySQL存儲引擎是怎樣的?
在文件系統中,MySQL將每個數據庫(即:)保存為數據目錄data下的一個子目錄。創建表時,MySQL會在數據庫data目錄下創建一個和表同名的文件來保存表的定義。
不同的存儲引擎保存數據和索引的方式是不同的,但表的定義則是在MySQL服務層統一處理的。
可以使用命令來查看錶的存儲引擎以及表的其他相關信息,例如,查看mysql數據庫中的user表:
mysql> use mysql;
No connection. Trying to reconnect...
Connection id: 20587
Current database: *** NONE ***
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show table status like 'user' \G;
*************************** 1. row ***************************
Name: user
Engine: MyISAM
Version: 10
Row_format: Dynamic
Rows: 3
Avg_row_length: 125
Data_length: 512
Max_data_length: 281474976710655
Index_length: 4096
Data_free: 136
Auto_increment: NULL
Create_time: 2019-07-12 14:45:17
Update_time: 2019-12-20 15:55:44
Check_time: NULL
Collation: utf8_bin
Checksum: NULL
Create_options:
Comment: Users and global privileges
1 row in set (0.00 sec)
ERROR:
No query specified
從查詢結果的字段可以表明,user表的存儲引擎類型為,其他字段在此就不一一説明,如想詳細瞭解可查閲相關文檔。
在瞭解MySQL存儲引擎前,可以先看看你的MySQL數據庫支持哪些存儲引擎,可通過命令查看。我使用的MySQL版本為,查看結果如下圖所示:
從上述結果可以看出,支持的存儲引擎有:、、、、、、 、、,其中也做了簡單的解釋説明。
本文只針對、兩種最常見的存儲引擎進行着重説明,其它存儲引擎只做簡單説明,詳細可查閲官方文檔。
1.InnoDB存儲引擎
是MySQL的默認事務型引擎,也是最重要、使用最廣泛的存儲引擎,並且有行級鎖定和外鍵約束。
它被設計用來處理大量的短期(short-lived)事務,短期事務大部分情況是正常提交,很少會被回滾。InnoDB的性能和自動崩潰恢復特性,使得它在非事務型存儲的需求中也很流行。除非有非常特別的原因需要使用其他的存儲引擎,否則應該優先考慮引擎。
的適用場景/特性,有以下幾種:
經常更新的表,適合處理多重併發的更新請求。
支持事務。
可以從災難中恢復(通過bin-log日誌等)。
外鍵約束。只有他支持外鍵。
支持自動增加列屬性auto_increment。
2.MyISAM存儲引擎
提供了大量的特性,包括全文檢索、壓縮等,但不支持事務和行級鎖,支持表級鎖。對於只讀的數據,或者表較小、可以忍受修復操作的場景,依然可以使用MyISAM。
的適用場景/特性,有以下幾種:
不支持事務的設計,但是並不代表着有事務操作的項目不能用存儲引擎,完全可以在程序層進行根據自己的業務需求進行相應的控制。
不支持外鍵的表設計。
查詢速度很快,如果數據庫和的操作比較多的話比較適用。
整天 對錶進行加鎖的場景。
極度強調快速讀取操作。
中存儲了表的行數,於是時只需要直接讀取已經保存好的值而不需要進行全表掃描。如果表的讀操作遠遠多於寫操作且不需要數據庫事務的支持,那麼也是很好的選擇。
3.MySQL內建的其他存儲引擎
MySQL還有一些特殊用途的存儲引擎,在一些特殊場景下用起來會很爽的。在MySQL新版本中,有些可能因為一些原因已經不再支持了,還有一些會繼續支持,但是需要明確地啓用後才能使用。
引擎只支持和操作,並且在MySQL 5.1之前連索引都不支持。
引擎會緩存所有的寫並利用zlib對插入的行進行壓縮,所以比引擎的磁盤I/O更少。但是每次查詢都需要進行全表掃描,所以Archive更適合日誌和數據採集類應用,況且這類應用在做數據分析時往往需要全表掃描。
引擎支持行級鎖和專用的緩衝區,所以可以實現高併發的插入。在一個查詢開始直到返回表中存在的所有行之前,引擎會阻止其他的執行,以實現一致性讀。另外,這也實現了批量插入在完成之前對讀操作是不看見的。
引擎沒有實現任何的存儲機制,它會丟失所有插入的數據,不做任何保存。怪哉,豈不是一無用處?
但是服務器會記錄的日誌,所以可以用於複製數據到備庫,或者只是簡單地記錄到日誌。這種特殊的存儲引擎可以在一些特殊的複製架構和日誌審核時發揮作用。
但這種存儲引擎的存在,至今還是有些難以理解。
引擎可以將普通的文件作為MySQL的表來處理,但這種表不支持索引。
引擎可以在數據庫運行時拷入或者拷出文件,可以將Excel等電子表格軟件中的數據存儲為文件,然後複製到MySQL數據目錄下,就能在MySQL中打開使用。同樣,如果將數據寫入到一個引擎表中,其他的外部程序也能立即從表的數據文件中讀取格式的數據。
因此,引擎可以作為一種數據交換的機制,是非常有用的。
如果需要快速地訪問數據,並且這些數據不會被修改,重啓以後丟失也沒有關係,那麼使用引擎是非常有用的。引擎至少比引擎要快一個數量級,因為所有的數據都保存在內存中,不需要進行磁盤I/O。引擎的表結構在重啓以後還會保留,但數據會丟失。
引擎在很多場景下可以發揮很好的作用:
用於查找或者映射表,例如將郵箱和州名映射的表。
用於緩存週期性聚合數據的結果。
用於保存數據分析中產生的中間數據。
引擎支持Hash索引,因此查找非常快。雖然的速度非常快,但還是無法取代傳統的基於磁盤的表。引擎是表級鎖,因此併發吸入的性能較低。
如果MySQL在執行查詢的過程中,需要使用臨時表來保存中間結果,內部使用的臨時表就是Memory引擎。如果中間結果太大超出了的限制,或者含有或字段,則臨時表會轉換成的引擎。
看了上面的説明,大家就會經常混淆Memory和臨時表了。臨時表是指使用語句創建的表,它可以使用任何存儲引擎,因此和Memory不是一回事。臨時表只在單個連接中可見,當連接斷開時,臨時表也將不復存在。
關於臨時表和Memory引擎的那些事,可參考MySQL · 引擎特性 · 臨時表那些事兒。
MySQL的存儲引擎及第三方存儲引擎,還有很多,在此就不一一介紹了,後續如有需要,再進一步來談談。
4.如何選擇合適的存儲引擎呢
這麼多存儲引擎,真是眼花繚亂,我們該如何選擇呢?
大部分情況下,都會選擇默認的存儲引擎——,並且這也是最正確的選擇,所以Oracle在MySQL 5.5版本時終於將作為默認的存儲引擎了。
對於如何選擇合適的存儲引擎,可以簡單地歸納為一句話:”除非需要用到某些InnoDB不具備的特性,並且沒有其他可以替代,否則都應該優先選擇InnoDB引擎”。
例如,如果要用到全文檢索,建議優先考慮加上的組合,而不是使用支持全文檢索的。當然,如果不需要用到的特性,同時其他引擎的特性能夠更好地滿足需求,就可以考慮一下其他存儲引擎。
除非萬不得已,建議不要混合使用多種存儲引擎,否則可能帶來一系列複雜的問題,以及一些潛在的bug和邊界問題。
如果需要使用不同的存儲引擎,建議考慮從以下幾個因素進行衡量考慮。
事務
備份
恢復
特有的特性
參考文章:
https://www.cnblogs.com/sunsky303/p/8274586.html
https://www.cnblogs.com/coderyuhui/p/10773143.html