MySQL是非常靈活的一款數據庫,雖然它不是絕對完美,但它的靈活足夠適應很多高要求的環境。為了發揮MySQL的性能並很好的使用它,我們就得先了解其設計。MySQL的靈活主要體現在我們可以通過不同的配置使他在不同的硬件上都能運行的很好。但是MySQL最重要,與種不同的特性是它的存儲引擎架構,這種架構將查詢處理及其他系統任務和數據的存儲/提取相分離。
如上圖,我們可以簡單的將其邏輯架構分為3層
(1)網絡接口層:主要負責接受連接,並讀寫連接對應的內容,這點和我們平時所寫的網絡服務器的I/O接口層基本相似
(2)請求處理層:如上圖所示,該層主要是對請求SQL語句進行解析,解析之後或是進一步對請求的SQL語句進行優化,或是直接在緩存中找到請求的內容
(3)存儲引擎層:該層主要負責數據的提取和存儲<喎?http://www.Bkjia.com/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjwvYmxvY2txdW90ZT4NCjxoNCBpZD0="1連接管理">(1)連接管理MySQL的每個客戶端連接都會在服務器擁有一個線程,該線程的查詢只會在這個單獨的線程中去執行。服務器會負責緩存該線程,因此不需要為每個新連接創建或銷毀線程(線程池)
(2)優化與執行
MySQL會解析查詢,並創建內部數據結構(解析樹),然後對其進行各種優化,包括重寫查詢,決定表的讀寫順序,以及選擇合適的索引等。用戶可以通過explain來查看查詢優化過程,從而可知服務器是如何進行查詢優化決策的。需要注意的是優化器並不需要知道存儲引擎是什麼,但不同的存儲引擎對優化是有影響的
2.並發控制
(1)讀寫鎖
當我們的多個用戶同時對數據庫中的一張表進行讀操作,這並不會產生什麼問題。但是如果此時有一個進程在修改表中的某條數據,這就會產生不可預料的事情了。為了防止類似的事情發生,MySQL采用了倆中鎖:共享鎖和排他鎖。這倆中鎖從概念上就很好理解,共享意思持有這些鎖的線程互相不阻塞,對應此處就是一張表幾個線程可以同時讀。排他則意味著只要有一個線程獲得該鎖,則其他的所有線程無論讀寫都會被阻塞
(2)鎖粒度
能夠提高並發性的一種方式就是讓鎖的對象更具有選擇性。盡量只鎖定真正需要修改的數據,而不是有關的無關的都鎖。鎖定的資源(臨界區)越少,則並發度也就越高,只要相互之間不發生競爭即可。剛才的說法其實只是一種理想,真實環境下,我們創建鎖,加鎖,解鎖,銷毀鎖也都會也一定的代價,那麼你把鎖的對象縮小,對應的鎖粒度就會增加,鎖粒度一旦增加,那麼大量的加鎖,解鎖操作是不是會降低性能。所以我們是不是因該根據實際的應運場景來在這倆者中尋找出最適合該場景的一個平衡點?
MySQL的各種引擎支持如下倆種鎖策略
表鎖:表鎖是MySQL中最基本的策略,並且是開銷最小的策略。表鎖會鎖定整張表,一個用戶對表進行寫操作(插入,刪除,跟新)前,需先獲得寫鎖,這樣就會阻塞其他用戶對表進行的所有操作。只有沒有寫鎖時,其他用戶才能獲得該表的讀鎖,讀鎖之間是不阻塞的上文有提到的
行鎖:行鎖可以最大程度的支持並發處理(也同時帶來了最大的鎖開銷)InnoDB支持的就是行鎖,這裡所說的鎖都是只在存儲引擎中的實現,服務器是完全不了解存儲引擎的鎖實現的3.事務
對數據庫而言事務就是一組原子的SQL查詢(注意這裡所所的查詢可不單指select哦),或者說是一個獨立的單元。如果數據庫引擎能夠成功的對數據庫進行該組內的全部查詢語句,那麼就執行該組查詢。如果有一條不能執行(例如程序崩潰導致),那麼所有的語句都不會執行
事物具有ACID特性,分別如下
原子性:一個事務必須被視為一個不可分割的最小單位,整個事務的操作要麼全部提交成功,要麼失敗回滾
一致性:數據庫總是從一個一致性的狀態轉換到另一個一致性的狀態
隔離性:通常來說在一個事務所做的修改在最終提交之前,對其他事務是不可見的
持久性:一旦事務提交,則其所做的修改就會永久保存就像鎖粒度的升級會增加系統開銷一樣,這種事務處理過程中額外的安全性,也同樣會使數據庫做更多的工作。一個實現ACID的數據庫比沒有實現的要復雜太多。所以MySQL提供多種引擎,用戶可根據需求在不需要事務特性的使用中選擇其他引擎
(1)事務的隔離級別
隔離其實是很復雜的,在SQL中定義了4種隔離級別,每一種級別都規定了一個事務所做的修改,哪些事務和事務間是可見的哪些是不可見的。較低級別的隔離可以執行更高的並發,系統的開銷也更低
幾種隔離級別
未提交讀:事務中的修改即使沒有提交,對其他事務也都是可見的。事務可以讀取為提交的數據,這也被稱為髒讀。這種級別性能也就那樣,但卻會導致很多問題
提交讀:大多數數據庫默認的就是該級別。一個事務開始時只能看到已提交的事務。換句話說,一個事務從開始到提交前,所做的任何修改對其他事物都是不可見的
可重復讀:解決了髒讀問題,但是卻無法解決幻讀問題(是指,當某個事務讀取某個范圍內的記錄時,另一個事務又在該范圍內插入了新數據,當該事務在此讀取該范圍內的數據時,會產生幻行)InnoDB通過MVVC解決了幻讀問題
可串行化是最高級別的隔離,它通過強制事務串行化執行,避免了前面所說的幻讀問題(2)死鎖
當多個事務試圖以不同的順序鎖定資源時,就可能會產生死鎖。
實例如下
如上圖中的事務1和事務2所示,如果倆事務都先執行完了第一條UPDATE操作,那麼當分別同時在執行第二條時,就會陷入死鎖狀態即相互等為了解決上述問題InnoDB通過檢測死鎖循環依賴,並立即返回一個錯誤。這種方式很有效,否則死鎖會導致查詢變的非常慢。InnoDB目前處理死鎖的方法是,將持有最少行級鎖的事物進行回滾(這是相對比較簡單的死鎖回滾算法)
(3)事務日志
先假設這樣一個問題,如果讓你來設計日志持久化,你會咋樣做,沒插入一個數據,就把它寫如磁盤對應該數據的位置麼?要知道,插入的數據可不是隨便插入的,你得保證你插入的數據就在磁盤對應該表的准確位置,而不是像我們平時寫文件一樣直接把數據寫到文件尾部就可以了。所以如果采用你的方式對數據庫文件進行持久化的話,我想效率也太低了,每次在磁盤I/O就不知要阻塞多長時間(需要在磁盤的多個地方移動磁頭)。所以MySQL持久化數據寫把數據寫到內存,然後通過往事務日志尾部添加進行第一步持久化。日志持久化(這一步是備胎)之後,我們就可以慢慢的在後台把內存中的數據持久化到磁盤中去了(這一步才是真正的持久化),此時即使機器死機,也可以在重啟之後通過事物務日志來恢復未真正持久化的數據。所以MySQL的數據持久化會經歷倆次磁盤I/O
(4)MySQL中的事務
自動提交
MySQL提供了倆中事務型的存儲引擎:InnoDB和NDB Cluster。事務型的存儲引擎默認采取自動提交(AUTOCOMMIT)模式。每個查詢都會默認被當做一個事務執行提交操作
讀者可以用上述命令來查看自己的存儲引擎是否默認采取自動提交
其中ON表示開啟,OFF表示關閉混合使用存儲引擎
這個其實是非常不可取的,如果你混合使用了事務和非事務型的存儲引擎,那麼當需要事務回滾時該咋辦?
隱式和顯示鎖定
InnoDB采用倆階段鎖協議。在事務執行過程中隨時加鎖,在COMMIT或ROLLBACK時將所有鎖同時全部釋放,這是隱式鎖定。當然你也可以用SQL語句進行顯示鎖定
多版本並發控制
之前在講隔離級別時有個幻讀的概念,那麼如何解決此問題呢?正是這裡的多版本控制,這裡有個對杭機鎖控制的專業名詞MVCC,InnoDB的MVCC是通過在每行記錄後面保存倆個隱藏的列來實現的,這倆個列一個保存創建時的版本號,另一個保存過期或刪除的版本號。每次開始一個新的事務這些版本號都會自增一次。事務開始時刻的版本號會作為事務的版本號,用來和查詢的每行記錄做比較
表一
表明為Test行號 用戶列1 用戶列2 行版本號 刪除版本號 行1 a1 b1 0 0 行2 a2 b2 0 0 行3 a3 b3 0 0假設事務1先開始給其一個系統版本號(就是默認的每一行對應隱藏列的所有號,而不是單獨的每個號)(即快照1)
事務1所有執行的事務為
“`
(1)delete from Test where 用戶列2 = b2;事務2開始給其的系統號快照2(假設同時獲得的快照此時快照1.2是相同的其實) 事務2所要執行的事務為
(2)select *from Test;
“`
執行(1)之後真實表變為如下
行號
用戶列1
用戶列2
行版本號
刪除版本號
行1
a1
b1
0
0
行2
a2
b2
0
1
行3
a3
b3
0
0
(2)
事務2執行查詢操作時,每一步都會和其快照的版本號進行比對
1.只查找小於或等於當前事務版本號的行
行的刪除大與該事務系統版本號的不找
根據上述倆規則,則而的執行結果如下
行號
用戶列1
用戶列2
行版本號
刪除版本號
行1
a1
b1
0
0
行3
a3
b3
0
0
(5)MySQL的存儲引擎
InnoDB是MySQL默認引擎,也是最重要最廣泛的存儲引擎。它被用來處理大量短期事務,短期事務大部分情況下是正常提交,很少會被回滾。InnoDB的性能和自動崩潰恢復的特性,使得它在非關系型的存儲需求中也很流行。除非有特別的需求,負責因該優先考慮InnoDB引擎
InnoDB的概述
InnoDB數據存儲在表空間中,表空間是由InnoDB管理的一個黑盒子,由一系列的數據文件組成。現在版本的InnoDB可以將每個表的數據和索引存放在單獨的文件中。InnoDB采用MVCC來支持高並發,並且通過間隙鎖來防止幻讀
InnoDB是基於聚簇索引建立的,聚簇索引對主鍵的查詢會有很高的性能。不過它的二級索引中必須包含主鍵列,所以如果主鍵列很大的話,其他的索引都會很大。因此若表上的索引較多的話,主鍵應當盡可能的小
InnoDB內部做了很多優化,包括從磁盤讀取數據時預判性讀,能夠自動在內存中建立hash,以及能夠加速插入操作的插入緩沖區等
InnoDB通過一些機制和工具支持熱備份
MyISAM存儲引擎
MyISAM使用全文索引,不支持事務和行鎖等,它的缺陷就是不能安全恢復在對只讀數據或者表比較小,可以忍受修復的操作,則可以使用MyISAM
MyISAM會將數據和索引存在倆個表中
MyISAM特性
加鎖與並發:其對整張表加鎖,讀時加讀鎖,寫加寫鎖,但是在讀時也可以插入,這就是其並發插入
修復:可以手動執行簡單的修復
對於MyISAM表,即使是text等長字段也可以選其前500字節做索引
MyISAM的性能
MyISAM設計簡單,數據以緊密格式存儲,所以在某些場景下性能很好