寫在前面:最先接觸的MySQL是在三年前,那時候MySQL仍是4.x版本,不少功能都不支持,好比,存儲過程,視圖,觸發器,更別說分佈式事務等複雜特性了。但從5.0(2005年10月)開始,MySQL漸漸步入企業級數據庫的行列了;複製、集羣、分區、分佈式事務,這些企業級的特性,使得如今的MySQL,徹底能夠應用於企業級應用環境(不少互聯網公司都用其做爲數據庫服務器,儘管節約成本是一個因素,可是沒有強大功能做後盾,則是不可想象的)。雖然,MySQL還有不少不足,好比,複製、分區的支持都十分有限、查詢優化仍須要改進,可是MySQL已是一個足夠好的DBMS了,更況且它是opensource的。這段時間沒有事,出於好奇,略微的研究了一下MySQL,積累了一些資料,欲總結出來。這些資料打算分爲兩部分,上部主要討論MySQL的優化,其中主要參考了《MySQL Manual》和《High Performance MySQL》,若是有時間,之後在下部分析一下MySQL的源碼。若是你是MySQL高手,但願你不吝賜教;若是你是新手,但願對你有用。 mysql
第一章、MySQL架構與概念
一、MySQL的邏輯架構
sql
最上面不是MySQL特有的,全部基於網絡的C/S的網絡應用程序都應該包括鏈接處理、認證、安全管理等。
中間層是MySQL的核心,包括查詢解析、分析、優化和緩存等。同時它還提供跨存儲引擎的功能,包括存儲過程、觸發器和視圖等。
最下面是存儲引擎,它負責存取數據。服務器經過storage engine API能夠和各類存儲引擎進行交互。
1.一、查詢優化和執行(Optimization and Execution) 數據庫
MySQL將用戶的查詢語句進行解析,並建立一個內部的數據結構——分析樹,而後進行各類優化,例如重寫查詢、選擇讀取表的順序,以及使用哪一個索引等。查詢優化器不關心一個表所使用的存儲引擎,可是存儲引擎會影響服務器如何優化查詢。優化器經過存儲引擎獲取一些參數、某個操做的執行代價、以及統計信息等。在解析查詢以前,服務器會先訪問查詢緩存(query cache)——它存儲SELECT語句以及相應的查詢結果集。若是某個查詢結果已經位於緩存中,服務器就不會再對查詢進行解析、優化、以及執行。它僅僅將緩存中的結果返回給用戶便可,這將大大提升系統的性能。 緩存
1.二、併發控制
MySQL提供兩個級別的併發控制:服務器級(the server level)和存儲引擎級(the storage engine level)。加鎖是實現併發控制的基本方法,MySQL中鎖的粒度:
(1) 表級鎖:MySQL獨立於存儲引擎提供表鎖,例如,對於ALTER TABLE語句,服務器提供表鎖(table-level lock)。
(2) 行級鎖:InnoDB和Falcon存儲引擎提供行級鎖,此外,BDB支持頁級鎖。InnoDB的併發控制機制,下節詳細討論。
另外,值得一提的是,MySQL的一些存儲引擎(如InnoDB、BDB)除了使用封鎖機制外,還同時結合MVCC機制,即多版本兩階段封鎖協議(Multiversion two-phrase locking protocal),來實現事務的併發控制,從而使得只讀事務不用等待鎖,提升了事務的併發性。
注:併發控制是DBMS的核心技術之一(實際上,對於OS也同樣),它對系統性能有着相當重要的影響,之後再詳細討論。 安全
1.三、事務處理
MySQL中,InnoDB和BDB都支持事務處理。這裏主要討論InnoDB的事務處理(關於BDB的事務處理,也十分複雜,之前曾較爲詳細看過其源碼,之後有機會再討論)。
1.3.一、事務的ACID特性
事務是由一組SQL語句組成的邏輯處理單元,事務具備如下4個屬性,一般簡稱爲事務的ACID屬性(Jim Gray在《事務處理:概念與技術》中對事務進行了詳盡的討論)。
(1)原子性(Atomicity):事務是一個原子操做單元,其對數據的修改,要麼全都執行,要麼全都不執行。
(2)一致性(Consistent):在事務開始和完成時,數據都必須保持一致狀態。這意味着全部相關的數據規則都必須應用於事務的修改,以保持數據的完整性;事務結束時,全部的內部數據結構(如B樹索引或雙向鏈表)也都必須是正確的。
(3)隔離性(Isolation):數據庫系統提供必定的隔離機制,保證事務在不受外部併發操做影響的「獨立」環境執行。這意味着事務處理過程當中的中間狀態對外部是不可見的,反之亦然。
(4)持久性(Durable):事務完成以後,它對於數據的修改是永久性的,即便出現系統故障也可以保持。
1.3.二、事務處理帶來的相關問題
因爲事務的併發執行,帶來如下一些著名的問題:
(1)更新丟失(Lost Update):當兩個或多個事務選擇同一行,而後基於最初選定的值更新該行時,因爲每一個事務都不知道其餘事務的存在,就會發生丟失更新問題--最後的更新覆蓋了由其餘事務所作的更新。
(2)髒讀(Dirty Reads):一個事務正在對一條記錄作修改,在這個事務完成並提交前,這條記錄的數據就處於不一致狀態;這時,另外一個事務也來讀取同一條記錄,若是不加控制,第二個事務讀取了這些「髒」數據,並據此作進一步的處理,就會產生未提交的數據依賴關係。這種現象被形象地叫作"髒讀"。
(3)不可重複讀(Non-Repeatable Reads):一個事務在讀取某些數據後的某個時間,再次讀取之前讀過的數據,卻發現其讀出的數據已經發生了改變、或某些記錄已經被刪除了!這種現象就叫作「不可重複讀」。
(4)幻讀(Phantom Reads):一個事務按相同的查詢條件從新讀取之前檢索過的數據,卻發現其餘事務插入了知足其查詢條件的新數據,這種現象就稱爲「幻讀」。
1.3.三、事務的隔離性
SQL2標準定義了四個隔離級別。定義語句以下:
SET TRANSACTION ISOLATION LEVEL
[READ UNCOMMITTED |
READ COMMITTED |
REPEATABLE READ |
SERIALIZABLE ]
這與Jim Gray所提出的隔離級別有點差別。其中READ UNCOMMITTED即Jim的10(瀏覽);READ COMMITTED即20,遊標穩定性;REPEATABLE READ爲2.99990隔離(沒有幻像保護);SERIALIZABLE隔離級別爲30,徹底隔離。SQL2標準默認爲徹底隔離(30)。各個級別存在問題以下: 服務器
隔離級 網絡 |
髒讀 session |
不可重複讀 數據結構 |
幻象讀 架構 |
讀未提交 (Read uncommitted) |
可能 |
可能 |
可能 |
讀提交 (Read committed) |
不可能 |
可能 |
可能 |
可重複讀 (Repeatable read) |
不可能 |
不可能 |
可能 |
可串行化 (Serializable) |
不可能 |
不可能 |
不可能 |
各個具體數據庫並不必定徹底實現了上述4個隔離級別,例如,Oracle只提供READ COMMITTED和Serializable兩個標準隔離級別,另外還提供本身定義的Read only隔離級別;SQL Server除支持上述ISO/ANSI SQL92定義的4個隔離級別外,還支持一個叫作「快照」的隔離級別,但嚴格來講它是一個用MVCC實現的Serializable隔離級別。MySQL 支持所有4個隔離級別,其默認級別爲Repeatable read,但在具體實現時,有一些特色,好比在一些隔離級別下是採用MVCC一致性讀。國產數據庫DM也支持全部級別,其默認級別爲READ COMMITTED。
1.3.四、InnoDB的鎖模型
InnoDB的行級鎖有兩種類型:
(1)共享鎖(shared lock,S):容許一個事務去讀一行,阻止其餘事務得到相同數據集的排他鎖。
(2)排它鎖(exclusive lock,X):容許得到排它鎖的事務更新數據,阻止其餘事務取得相同數據集的共享讀鎖和排他寫鎖。
此外,InnoDB支持多粒度加鎖(multiple granularity locking),從而容許對記錄和表同時加鎖。爲此,InnoDB引入意向鎖(intention locks),意向鎖是針對表的:
(1)意向共享鎖(IS):事務打算給數據行加行共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
(2)意向排他鎖(IX):事務打算給數據行加行排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
例如,SELECT ... LOCK IN SHARE MODE加IS鎖,SELECT ... FOR UPDATE加IX鎖,意向鎖的規則以下:
(1)事務在對錶T中的記錄獲取S鎖前,先要獲取表T的IS鎖或者更強的鎖;
(2)事務在獲取表T中記錄的X鎖前,先要獲取表T的IX鎖。
InnoDB的鎖相容性矩陣:
若是一個事務請求的鎖模式與當前的鎖兼容,InnoDB就將請求的鎖授予該事務;反之,若是二者不兼容,該事務就要等待鎖釋放。意向鎖只會阻塞其它事務對錶的請求,例如,LOCK TABLES …WRITE,意向鎖的主要目的是代表該事務將要或者正在對錶中的記錄加鎖。使用封鎖機制來進行併發控制,一個比較重要的問題就是死鎖。
來看一個死鎖的例子:
例1-1
Session 1 |
Session 2 |
mysql> CREATE TABLE t (i INT) ENGINE = InnoDB; Query OK, 0 rows affected (0.22 sec)
mysql> INSERT INTO t (i) VALUES(1); Query OK, 1 row affected (0.08 sec)
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM t WHERE i = 1 LOCK IN SHARE MODE; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.01 sec)
mysql> |
|
|
mysql> START TRANSACTION; Query OK, 0 rows affected (0.00 sec)
mysql> DELETE FROM t WHERE i = 1; 等待… |
mysql> DELETE FROM t WHERE i = 1; 等待… |
|
|
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction |
Query OK, 1 row affected (0.00 sec) |
|
1.3.五、一致性非阻塞讀
一致性讀是MySQL的重要特色之一,InnoDB經過MVCC機制表示數據庫某一時刻的查詢快照,查詢能夠看該時刻以前提交的事務所作的改變,可是不能看到該時刻以後或者未提交事務所作的改變。可是,查詢能夠看到同一事務中以前語句所作的改變,例如:
例1-2
Session 1 |
Session 2 |
mysql> select * from t; Empty set (0.00 sec)
mysql> INSERT INTO t (i) VALUES(1); Query OK, 1 row affected (0.00 sec)
mysql> select * from t; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.00 sec)
mysql> set autocommit = 0; Query OK, 0 rows affected (0.01 sec)
mysql> update t set i=3; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from t; +------+ | i | +------+ | 3 | +------+ 1 row in set (0.00 sec) |
|
|
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec)
mysql> select * from t; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.00 sec) |
mysql> commit; Query OK, 0 rows affected (0.06 sec) |
|
|
mysql> select * from t; +------+ | i | +------+ | 1 | +------+ 1 row in set (0.00 sec) |
|
mysql> commit; Query OK, 0 rows affected (0.00 sec)
mysql> select * from t; +------+ | i | +------+ | 3 | +------+ 1 row in set (0.00 sec) |
若是事務的隔離級別爲REPEATABLE READ(默認),同一個事務中的全部一致性讀都是讀的事務的第一次讀操做建立的快照。你能夠提交當前事務,而後在新的查詢中便可看到最新的快照,如上所示。
若是事務的隔離級別爲READ COMMITTED,一致性讀只是對事務內部的讀操做和它本身的快照而言的,結果以下:
例1-3
Session 1 |
Session 2 |
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.01 sec)
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec)
mysql> select * from t; +------+ | i | +------+ | 3 | +------+ 1 row in set (0.00 sec) |
|
|
mysql> SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED; Query OK, 0 rows affected (0.01 sec)
mysql> set autocommit = 0; Query OK, 0 rows affected (0.00 sec)
mysql> select * from t; +------+ | i | +------+ | 3 | +------+ 1 row in set (0.00 sec) |
mysql> update t set i=5; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 |
|
|
mysql> select * from t; +------+ | i | +------+ | 3 | +------+ 1 row in set (0.00 sec) |
mysql> commit; Query OK, 0 rows affected (0.06 sec) |
|
|
mysql> select * from t; +------+ | i | +------+ | 5 | +------+ 1 row in set (0.00 sec) |
注意,session 2發生了不可重複讀。
當InnoDB在READ COMMITTED 和REPEATABLE READ隔離級別下處理SELECT語句時,一致性讀是默認的模式。一致性讀不會對錶加任何鎖,因此,其它鏈接能夠同時改變表。
假設事務處於REPEATABLE READ級別,當你正在進行一致性讀時,InnoDB根據查詢看到的數據給你一個時間點。若是其它的事務在該時間點以後刪除一行,且提交事務,你不會看到行已經被刪除,插入和更新操做同樣。可是,InnoDB與其它DBMS的不一樣是,在REPEATABLE READ隔離級別下並不會形成幻像。
一致性讀不與DROP TABLE 或者 ALTER TABLE一塊兒工做。
在nodb_locks_unsafe_for_binlog變量被設置或者事務的隔離級別不是SERIALIZABLE的狀況下,InnoDB對於沒有指定FOR UPDATE 或 LOCK IN SHARE MODE的INSERT INTO ... SELECT, UPDATE ... (SELECT), 和CREATE TABLE ... SELECT語句使用一致性讀,在這種狀況下,查詢語句不會對錶中的元組加鎖。不然,InnoDB將使用鎖。
1.3.六、SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE MODE的加鎖讀(locking read)
在一些場合,一致性讀並非很方便,此時,能夠用加鎖讀。InnoDB支持兩種加鎖讀:
(1) SELECT ... LOCK IN SHARE MODE:對讀取的元組加S鎖。
(2) SELECT ... FOR UPDATE:在掃描索引記錄的過程當中,會阻塞其它鏈接的SELECT ...LOCK IN SHARE MODE和必定事務隔離級別下的讀操做。
InnoDB使用兩階段封鎖協議,事務直到提交或回滾時纔會釋放全部的鎖,這都是系統自動執行的。此外,MySQL支持LOCK TABLES和UNLOCK TABLES,但這些都是在服務器層實現的,而不是在存儲引擎。它們有用處,可是不能取代存儲引擎完成事務處理,若是你須要事務功能,請使用事務型存儲引擎。
來考慮locking read的應用,假設你要在表child插入一個新的元組,並保證child中的記錄在表parent有一條父記錄。若是你用一致性讀來讀parent表,確實能夠將要插入的child row的parent row,可是能夠安全的插入嗎?不,由於在你讀parent表時,其它鏈接可能已經刪除該記錄。(一致性讀是針對事務內而言的,對於數據庫的狀態,它應該叫作「不一致性讀」)
此時,就可使用SELECT LOCK IN SHARE MODE,它會對讀取的元組加S鎖,從而防止其它鏈接刪除或更新元組。另外,若是你想在查詢的同時,進行更新操做,可使用SELECT ... FOR UPDATE,它讀取最新的數據,而後對讀到的元組加X鎖。此時,使用SELECT ... LOCK IN SHARE MODE不是一個好主意,由於此時若是有兩個事務進行這樣的操做,就會形成死鎖。
注:SELECT ... FOR UPDATE僅在自動提交關閉(即手動提交)時纔會對元組加鎖,而在自動提交時,符合條件的元組不會被加鎖。
1.3.七、記錄鎖(record lok)、間隙鎖(gap lock)和後碼鎖(next-key lock)
InnoDB有如下幾種行級鎖:
(1)記錄鎖:對索引記錄(index records)加鎖,InnoDB行級鎖是經過給索引的索引項加鎖來實現的,而不是對記錄實例自己加鎖。若是表沒有定義索引,InnoDB建立一個隱藏的聚簇索引,而後用它來實現記錄加鎖(關於索引與加鎖之間的關係的詳細介紹請看下一章)。
(2)間隙鎖:對索引記錄之間的區間,或者第一個索引記錄以前的區間和最後一個索引以後的區間加鎖。
(3)後碼鎖:對索引記錄加記錄鎖,且對索引記錄以前的區間加鎖。
默認狀況下,InnoDB的事務工做在REPEATABLE READ的隔離級別,並且系統變量innodb_locks_unsafe_for_binlog爲關閉狀態。此時,InnoDB使用next-key鎖進行查找和索引掃描,從而達到防止「幻像」的目的。
Next-key鎖是記錄鎖和間隙的結合體。當InnoDB查找或掃描表的索引時,對它遇到的索引記錄加S鎖或者X鎖,因此,行級鎖(row-level lock)實際上就是索引記錄鎖(index-record lock);此外,它還對索引記錄以前的區間加鎖。也就是說,next-key鎖是索引記錄鎖,外加索引記錄以前的區間的間隙鎖。若是一個鏈接對索引中的記錄R持有S或X鎖,其它的鏈接不能按照索引的順序在R以前的區間插入一個索引記錄。
假設索引包含如下值:10, 11,13和20,則索引的next-key鎖會覆蓋如下區間(「(」表示不包含,「[」表示包含):
(negative infinity, 10]
(10, 11]
(11, 13]
(13, 20]
(20, positive infinity)
對於最後一個區間,next-key鎖將鎖住索引最大值以上的區間,上界虛記錄(「supremum」 pseudo-record)的值比索引中的任何值都大,其實,上界不是一個真實的索引記錄,因此,next-lock將對索引的最大值以後的區間加鎖。
間隙鎖對查詢惟一索引中的惟一值是沒有必要的,例如,id列有惟一索引,則下面的查詢僅對id=100的元組加索引記錄鎖(index-record lock),而無論其它鏈接是否在以前的區間插入元組。
SELECT * FROM child WHERE id = 100;
若是id沒有索引,或者非惟一索引,則語句會鎖住以前的空間。
例1-4
Session 1 |
Session 2 |
mysql> create unique index i_index on t(i); Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> select * from t; +------+ | i | +------+ | 4 | | 10 | +------+ 2 rows in set (0.00 sec) |
|
|
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select i from t where i =10 lock in share mode; +------+ | i | +------+ | 10 | +------+ 1 row in set (0.00 sec) |
mysql> insert into t(i) values(9); Query OK, 1 row affected (0.03 sec) |
|
|
mysql> select * from t; +------+ | i | +------+ | 4 | | 9 | | 10 | +------+ 3 rows in set (0.00 sec) |
上例中,產生了幻像問題。若是將惟一查詢變成範圍查詢,結果以下(接上例的索引):
例1-5
Session 1 |
Session 2 |
mysql> select * from t; +------+ | i | +------+ | 4 | | 9 | | 10 | +------+ 3 rows in set (0.00 sec) |
|
|
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> select i from t where i>4 lock in share mode; +------+ | i | +------+ | 9 | | 10 | +------+ 2 rows in set (0.00 sec) |
mysql> insert into t(i) values(1); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t(i) values(8); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
|
能夠看到,session 2 的next-key使得在i=4以前的區間和以後的插入都被阻塞。
另外,若是刪除索引i_index,則結果以下:
例1-6
Session 1 |
Session 2 |
mysql> drop index i_index on t; Query OK, 3 rows affected (0.25 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> select * from t; +------+ | i | +------+ | 4 | | 10 | | 9 | +------+ 3 rows in set (0.00 sec) |
|
|
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec)
mysql> select i from t lock in share mode; +------+ | i | +------+ | 4 | | 10 | | 9 | +------+ 3 rows in set (0.00 sec) |
mysql> insert into t(i) values(8); 等待。。。 |
|
另外,針對插入(INSERT)操做,只要多個事務不會在同一索引區間的同一個位置插入記錄,它們就不用互相等待,這種狀況能夠稱爲插入意向間隙鎖(insertion intention gap lock)。例如,索引記錄的值爲4和7,兩個獨立的事務分別插入5和6,僅管它們都持有4—7之間的間隙鎖,可是它們不會相互阻塞。這能夠提升事務的併發性。
例1-7
Session 1 |
Session 2 |
mysql> select * from t; +------+ | i | +------+ | 4 | | 10 | | 9 | | 8 | +------+ 4 rows in set (0.00 sec)
mysql> create unique index i_index on t(i); Query OK, 4 rows affected (0.34 sec) Records: 4 Duplicates: 0 Warnings: 0
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
|
|
mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) |
mysql> insert into t(i) values(5); Query OK, 1 row affected (0.00 sec) |
|
|
mysql> insert into t(i) values(5); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction mysql> insert into t(i) values(6); Query OK, 1 row affected (0.00 sec) |
間隙鎖是能夠顯示關閉的,若是你將事務的隔離級別設爲READ COMMITTED,或者打開innodb_locks_unsafe_for_binlog系統變量,間隙鎖就會關閉。在這種狀況下,查找或掃描索引僅會進行外鍵約束檢查和重複鍵值檢查。
此外,READ COMMITTED隔離級別和關閉nodb_locks_unsafe_for_binlog還有另一個負做用:MySQL會釋放掉不匹配Where條件的記錄鎖。例如,對於UPDATE語句,InnoDB只能進行「半一致性(semi_consistent)讀」,因此,它會返回最新提交事務所作改變,從而產生不可重複讀和幻像問題。
1.3.八、使用next-key lock防止幻像問題
例1-4展現了一個幻像問題。使用next-key鎖的select語句能夠解決幻像問題,但例1-4的之因此會產生老是在於惟一索引,使得select語句沒有使用gap lock,而只使用了index-record lock。
1.四、存儲引擎
插件式存儲引擎是MySQL最重要特性之一,也是最不一樣於其它DBMS的地方。MySQL支持不少存儲引擎,以適用於不一樣的應用需求,經常使用的包括MyISAM、InnoDB、BDB、MEMORY、MERGE、NDB Cluster等。其中,BDB和NDB Cluster提供事務支持。
MySQL默認的存儲引擎爲MyISAM,固然,建立表的時候能夠指定其它的存儲引擎,你能夠在同一個數據庫中對不一樣的表使用不一樣的存儲引擎(這是很是強大而獨特的特性)。能夠經過SHOW TABLE STATUS命令查詢表所使用的存儲引擎,例如,查看mysql數據庫的user表:
mysql> SHOW TABLE STATUS LIKE 'user' \G *************************** 1. row *************************** Name: user Engine: MyISAM Version: 10 Row_format: Dynamic Rows: 4 Avg_row_length: 61 Data_length: 244 Max_data_length: 281474976710655 Index_length: 2048 Data_free: 0 Auto_increment: NULL Create_time: 2009-06-16 21:50:34 Update_time: 2009-09-30 14:59:08 Check_time: NULL Collation: utf8_bin Checksum: NULL Create_options: Comment: Users and global privileges 1 row in set (0.00 sec) |
Name:表的名稱;
Engine:表使用的存儲引擎;
Row_format:記錄的格式。MyISAM支持三種不一樣的存儲格式:靜態(固定長度)表(默認格式)、動態表及壓縮表。靜態表的字段都是固定長度的,例如CHAR和INTEGER;動態表的字段能夠是變長的,例如,VARCHAR或者BLOB。
Rows:表中記錄的數量。
Avg_row_length:記錄的平均長度(字節數);
Data_length:表中數據的所有字節數;
Max_data_length:表中數據最大的字節數;
Index_length:索引消耗的磁盤空間;
Data_free:對於MyISAM表,表示已經分配但尚未使用的空間;該空間包含之前刪除的記錄留下的空間,能夠被INSERT操做重用。
Auto_increment:下一個自增的值。
Check_time:上次使用CHECK TABLE或myisamchk檢查表的時間。
1.4.一、MyISAM
1.4.1.一、存儲
MySQL的默認存儲引擎,性能與功能的折中,包括全文索引(full-text index)、數據壓縮,支持空間(GIS)數據,可是,不支持事務和行級鎖。通常來講,MyISAM更適用於大量查詢操做。若是你有大量的插入、刪除操做,你應該選擇InnoDB。
每一個表包含3個文件:
(1).frm:表定義文件,對於其它存儲引擎也同樣。
(2).MYD文件:數據文件。
(3).MYI文件:索引文件。
能夠在建立表時經過DATA DIRECTORY和INDEX DIRECTORY爲數據文件和索引文件指定路徑,它們能夠位於不一樣目錄。另外,MyISAM的存儲格式是跨平臺的,你能夠將數據文件和索引文件從Intel平臺拷貝到PPC或者SPARC平臺。
5.0中,MyISAM的變長記錄表默認處理256TB數據,使用6字節的指針來指向數據記錄;而以前的版本使用默認的4字節指針,因此只能處理4GB數據。全部的版本均可以將指針增長到8字節指針,若是你想改變MyISAM表的指針的大小,能夠經過設置MAX_ROWS和AVG_ROW_LENGTH來實現:
CREATE TABLE mytable (
a INTEGER NOT NULL PRIMARY KEY,
b CHAR(18) NOT NULL
) MAX_ROWS = 1000000000 AVG_ROW_LENGTH = 32;
上面的例子中,MySQL將至少能夠存儲32GB的數據。能夠查看一下表的信息:
mysql> SHOW TABLE STATUS LIKE 'mytable' \G *************************** 1. row *************************** Name: mytable Engine: MyISAM Row_format: Fixed Rows: 0 Avg_row_length: 0 Data_length: 0 Max_data_length: 98784247807 Index_length: 1024 Data_free: 0 Auto_increment: NULL Create_time: 2002-02-24 17:36:57 Update_time: 2002-02-24 17:36:57 Check_time: NULL Create_options: max_rows=1000000000 avg_row_length=32 Comment: 1 row in set (0.05 sec) |
能夠看到,Create_options列出了建立時的選項,並且該表的最大的數據量爲91GB。你能夠用ALTER TABLE來改變指針的大小,可是那會致使表和索引的重建,這會花費很長的時間。
1.4.1.二、MyISAM的特性
(1)鎖與併發性:MyISAM只有表級鎖,不支持行級鎖。因此不適合於大量的寫操做,可是它支持併發插入(concurrent inserts),這是一個很是重要且有用的特性。
(2)自動修復:MySQL支持自動檢查和修復MyISAM表。
(3)手動修復:你可使用CHECK TABLE檢查表的狀態,並用REPAIR TABLE修復表。
(4)索引:你能夠爲BLOB和TEXT的前500個字符建立索引。並且,MyISAM還支持全文索引,但僅限於CHAR、VARCHAR、和TEXT列。
(5)延遲鍵寫(Delayed key writes):若是建立MyISAM表時指定DELAY_KEY_WRITE,MySQL在查詢結束時,不會將改變的索引數據寫入磁盤,而將修改保存在key buffer中。只有要改變緩存或者關閉表時,纔會把索引數據刷入磁盤。
1.4.二、InnoDB
InnoDB是一個高性能的事務存儲引擎,此外,BDB也支持事務處理(關於BDB,之前曾較爲詳細的閱讀過其源碼,之後有時間再討論),它有如下一些特色:
1.4.2.一、表空間
InnoDB存儲表和索引有兩種方式:
(1)共享表空間存儲:這種方式下,表的定義位於.frm文件中,數據和索引保存在innodb_data_home_dir和innodb_data_file_path指定的表空間中。
(2)多表空間存儲:表的定義仍位於.frm文件,可是,每一個InnoDB表和它的索引在它本身的文件(.idb)中,每一個表有它本身的表空間。
對那些想把特定表格移到分離物理磁盤的用戶,或者那些但願快速恢復單個表的備份而無須打斷其他InnoDB表的使用的用戶,使用多表空間會是有益的。你能夠往my.cnf的[mysqld]節添加下面行來容許多表空間:
[mysqld]
innodb_file_per_table
重啓服務器以後,InnoDB存儲每一個新建立的表到表格所屬於的數據庫目錄下它本身的文件tbl_name.ibd裏。這相似於MyISAM存儲引擎所作的,但MyISAM 把表分紅數據文件tbl_name.MYD和索引文件tbl_name.MYI。對於InnoDB,數據和因此被一塊兒存到.ibd文件。tbl_name.frm文件照舊依然被建立。
若是你從my.cnf文件刪除innodb_file_per_table行,並重啓服務器,InnoDB在共享的表空間文件裏再次建立表。
innodb_file_per_table隻影響表的建立。若是你用這個選項啓動服務器,新表被用.ibd文件來建立,可是你仍舊能訪問在共享表空間裏的表。若是你刪掉這個選項,新表在共享表空間內建立,但你仍舊能夠訪問任何用多表空間建立的表。
InnoDB老是須要共享表空間,.ibd文件對InnoDB不足以去運行,共享表空間包含熟悉的ibdata文件,InnoDB把內部數據詞典和undo日誌放在這個文件中。
1.4.2.二、外鍵約束
MySQL中,支持外鍵的存儲引擎只有InnoDB,在建立外鍵時,要求被參照表必須有對應的索引,參照表在建立外鍵時也會自動建立對應的索引。
1.4.2.三、MVCC與後碼鎖(next-key locking)
InnoDB將MVCC機制與next-key lock結合起來,實現事務的各個隔離級別,這是很是用意思的。在nodb_locks_unsafe_for_binlog變量被設置或者事務的隔離級別不是SERIALIZABLE的狀況下,InnoDB對於沒有指定FOR UPDATE 或 LOCK IN SHARE MODE的INSERT INTO ... SELECT, UPDATE ... (SELECT), 和CREATE TABLE ... SELECT語句使用一致性讀(參照前面),在這種狀況下,查詢語句不會對錶中的元組加鎖。不然,InnoDB將使用鎖。
主要參考:
《MySQL Manual》
《High Performance MySQL》