InnoDB 是 MySQL 5.5 以後默認的存儲引擎,它具備高可靠、高性能的特色,主要具有如下優點:html
一個 InnoDB 引擎完整的內存結構和磁盤結構以下圖所示:mysql
MyISAM 是 MySQL 5.5 以前默認的存儲引擎。建立 MyISAM 表時會建立兩個同名的文件:git
.MYD
(MYData
):用於存儲表數據;.MYI
(MYIndex
): 用於存儲表的索引信息。在 MySQL 8.0 以後,只會建立上述兩個同名文件,由於 8.0 後表結構的定義存儲在 MySQL 數據字典中,但在 MySQL 8.0 以前,還會存在一個擴展名爲 .frm
的文件,用於存儲表結構信息。MyISAM 與 InnoDB 主要的區別其只支持表級鎖,不支持行級鎖,不支持事務,不支持自動崩潰恢復,但可使用內置的 mysqlcheck 和 myisamchk 工具來進行檢查和修復。github
MEMORY 存儲引擎(又稱爲 HEAP 存儲引擎)一般用於將表中的數據存儲在內存中,它具備如下特徵:算法
基於以上特性,MEMORY 表主要適合於存儲臨時數據 ,如會話狀態、實時位置等信息。sql
CSV 存儲引擎使用逗號分隔值的格式將數據存儲在文本文件中。建立 CSV 表時會同時建立兩個同名的文件:數據庫
csv
,負責存儲表的數據,其文件格式爲純文本,能夠經過電子表格應用程序 (如 Microsoft Excel ) 進行修改,對應的修改操做也會直接反應在數據庫表中。CSM
,負責存儲表的狀態和表中存在的行數。ARCHIVE 存儲引擎默認採用 zlib 無損數據壓縮算法進行數據壓縮,可以利用極小的空間存儲大量的數據。建立ARCHIVE 表時,存儲引擎會建立與表同名的 ARZ
文件,用於存儲數據。它還具備如下特色:緩存
MERGE 存儲引擎,也稱爲 MRG_MyISAM 引擎,是一組相同 MyISAM 表的集合。 」相同」 表示全部表必須具備相同的列數據類型和索引信息。能夠經過 UNION = (list-of-tables)
選項來建立 MERGE 表,以下:安全
mysql> CREATE TABLE t1 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, message CHAR(20)) ENGINE=MyISAM;
mysql> CREATE TABLE t2 ( a INT NOT NULL AUTO_INCREMENT PRIMARY KEY, message CHAR(20)) ENGINE=MyISAM;
mysql> INSERT INTO t1 (message) VALUES ('Testing'),('table'),('t1');
mysql> INSERT INTO t2 (message) VALUES ('Testing'),('table'),('t2');
mysql> CREATE TABLE total (a INT NOT NULL AUTO_INCREMENT,message CHAR(20), INDEX(a))
ENGINE=MERGE UNION=(t1,t2) INSERT_METHOD=LAST;
複製代碼
建立表時能夠經過 INSERT_METHOD
選項來控制 MERGE 表的插入:使用 FIRST
或 LAST
分別表示在第一個或最後一個基礎表中進行插入;若是未指定 INSERT_METHOD 或者設置值爲 NO ,則表示不容許在 MERGE 表上執行插入操做。MERGE 表支持 SELECT,DELETE,UPDATE 和 DELETE 語句,示例以下:服務器
mysql> SELECT * FROM total;
+---+---------+
| a | message |
+---+---------+
| 1 | Testing |
| 2 | table |
| 3 | t1 |
| 1 | Testing |
| 2 | table |
| 3 | t2 |
+---+---------+
複製代碼
若是沒有特殊說明,一般大多數數據庫採用的索引都是 B+ tree 索引,它是基於 B+ tree 這種數據結構構建的。爲何採用 B+ tree 而不是平衡二叉樹 (AVL) 或紅黑樹等數據結構?這裏假設索引爲 1-16 的自增數據,各種數據結構的表現以下:
平衡二叉樹數據結構:
紅黑樹數據結構:
Btree 數據結構:
B+ Tree 數據結構
以上圖片均經過數據結構可視化網站 Data Structure Visualizations 自動生成,感興趣的小夥伴也可自行嘗試。
從上面的圖示中咱們能夠看出 B+ Tree 樹具備如下優勢:
對於 InnoDB ,由於主鍵索引是彙集索引,因此其葉子節點存儲的就是實際的數據。而非主鍵索引存儲的則是主鍵的值 :
對於 MyISAM,由於主鍵索引是非彙集索引,因此其葉子節點存儲的只是指向數據位置的指針:
綜上所述,B+ tree 結構廣泛適用於範圍查找,優化排序和分組等操做。B+ tree 是基於字典序進行構建的,所以其適用於如下查詢:
emp_no
字段爲索引,查詢條件爲 emp_no = 10008
。emp_no
和 dept_no
爲聯合索引,查找條件爲 emp_no = 10008
。dept_no
爲索引,查詢條件爲 dept_no like "d1%"
。前綴匹配和列前綴匹配都是索引前綴性的體現,在某些時候也稱爲前綴索引。emp_no
字段爲索引,查詢條件爲 emp_no > 10008
。emp_no
字段爲索引,查詢語句爲 select emp_no from employees
,此時 emp_no 索引被稱爲本次查詢的覆蓋索引,即只須要從索引上就能夠獲取所有的查詢信息,而沒必要訪問實際的表中的數據。emp_no
和 dept_no
爲聯合索引,查找條件爲 dept_no = "d004" and emp_no < 10020
,這種狀況下索引順序必須爲 ( emp_no,dept_no ),這樣才能基於 emp_no 的字典序排序進行範圍查找。使用哈希索引時,存儲引擎會對索引列的值進行哈希運算,並將計算出的哈希值和指向該行數據的指針存儲在索引中,所以它更適用於等值比較查詢,而不是範圍查詢,一樣也不能用於優化排序和分組等操做。在創建哈希索引時,須要選取選擇性比較高的列,即列上的數據不容易重複 (如身份證號),這樣能夠儘可能避免哈希衝突。由於哈希索引並不須要存儲索引列的數據,因此其結構比較緊湊,對應的查詢速度也比較快。
InnoDB 引擎有一個名爲 「自適應哈希索引 (adaptive hash index)」 的功能,當某些索引值被頻繁使用時,它會在內存中基於 B+ tree 索引再建立一個哈希索引,從而讓 B-Tree 索引具有哈希索引快速查找的優勢。
InnoDB 存儲引擎支持如下兩種標準的行級鎖:
排它鎖和共享鎖的兼容狀況以下:
X | X | |
---|---|---|
X | 不兼容 | 不兼容 |
S | 不兼容 | 兼容 |
爲了說明意向鎖的做用,這裏先引入一個案例:假設事務 A 利用 S 鎖鎖住了表中的某一行,讓其只能讀不能寫。以後事務 B 嘗試申請整個表的寫鎖,若是事務 B 申請成功,那麼理論上它就應該能修改表中的任意一行,這與事務 A 持有的行鎖是衝突的。想要解決這個問題,數據庫必須知道表中某一行已經被鎖定,從而在事務 B 嘗試申請整個表的寫鎖時阻塞它。想要知道表中某一行被鎖定,能夠對錶的每一行進行遍歷,這種方式可行可是性能比較差,因此 InnoDB 引入了意向鎖。
按照意向鎖的規則,當上面的事務 A 給表中的某一行加 S 鎖時,會同時給表加上 IS 鎖,以後事務 B 嘗試獲取表的 X 鎖時,因爲 X 鎖與 IS 鎖並不兼容,因此事務 B 會被阻塞。
X | IX | S | IS | |
---|---|---|---|---|
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
IX | 不兼容 | 兼容 | 不兼容 | 兼容 |
S | 不兼容 | 不兼容 | 兼容 | 兼容 |
IS | 不兼容 | 兼容 | 兼容 | 兼容 |
1. 一致性非鎖定讀
一致非鎖定讀 (consistent nonlocking read) 是指在 InnoDB 存儲引擎下,若是將要讀取的行正在執行 DELETE 或 UPDATE 操做,此時沒必要去等待行上鎖的釋放,而是去讀取 undo 日誌上該行的快照數據,具體以下:
基於多版本併發控制和一致性非鎖定讀,能夠避免獲取鎖的等待,從而提升併發訪問下的性能。
2. 一致性鎖定度
一致性鎖定讀則容許用戶按照本身的需求在進行 SELECT 操做時手動加鎖,一般有如下兩種方式:
InnoDB 存儲引擎支持如下三種鎖的算法:
Record Lock:行鎖,用於鎖定單個行記錄。示例以下:
-- 利用行鎖能夠防止其餘事務更新或刪除該行
SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;
複製代碼
Gap Lock:間隙鎖,鎖定一個範圍,但不包括記錄自己,主要用於解決幻讀問題,示例以下:
-- 利用間隙鎖能夠阻止其餘事務將值15插入列 t.c1
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;
複製代碼
Next-Key Lock:等價於 行鎖+間隙鎖,既鎖定範圍,也鎖定記錄自己。能夠用於解決幻讀中的 」當前讀「 的問題。
InnoDB 存儲引擎徹底支持 ACID 模型:
1. 原子性(Atomicity)
事務是不可分割的最小工做單元,事務的全部操做要麼所有提交成功,要麼所有失敗回滾,不存在部分紅功的狀況。
2. 一致性(Consistency)
數據庫在事務執行先後都保持一致性狀態,數據庫的完整性沒有被破壞。
3. 隔離性(Isolation)
容許多個併發事務同時對數據進行操做,但一個事務所作的修改在最終提交之前,對其它事務是不可見的。
4. 持久性(Durability)
一旦事務提交,則其所作的修改將會永遠保存到數據庫中。即便宕機等故障,也不會丟失。
數據庫隔離性由上一部分介紹的鎖來實現,而原子性、一致性、持久性都由 undo log 和 redo log 來實現。
在併發環境下,數據的更改一般會產生下面四種問題:
1.丟失更新
一個事務的更新操做被另一個事務的更新操做鎖覆蓋,從而致使數據不一致:
2. 髒讀
在不一樣的事務下,一個事務讀取到其餘事務未提交的數據:
3. 不可重複讀
在同一個事務的兩次讀取之間,因爲其餘事務對數據進行了修改,致使對同一條數據兩次讀到的結果不一致:
4.幻讀
在同一個事務的兩次讀取之間,因爲其餘事務對數據進行了修改,致使第二次讀取到第一次不存在數據,或第一次本來存在的數據,第二次卻讀取不到,就好像以前的讀取是 「幻覺」 同樣:
想要解決以上問題,能夠經過設置隔離級別來實現:InnoDB 支持如下四個等級的隔離級別,默認隔離級別爲可重複讀:
在每一個級別下,併發問題是否可能出現的狀況以下:
隔離級別 | 髒讀 | 不可重複讀 | 幻讀 |
---|---|---|---|
讀未提交(READ UNCOMMITTED) | 可能出現 | 可能 | 可能 |
讀已提交(READ COMMITTED) | 不可能出現 | 可能 | 可能 |
可重複讀(REPEATABLE READ) | 不可能 | 不可能 | 可能 |
串行化(SERIALIZABLE) | 不可能 | 不可能 | 不可能 |
就數據庫層面而言,當前任何隔離級別下都不會發生丟失更新的問題,以 InnoDB 存儲引擎爲例,若是你想要更改表中某行數據,該行數據上必然會加上 X 鎖,而對應的表上則會加上 IX 鎖,其餘任何事務必須等待獲取該鎖才能進行修改操做。
數據庫設計當中經常使用的三範式以下:
要求表中的每一列都是不可再細分的原子項。這是最低的範式要求,一般都可以被知足。
要求非主鍵列必須徹底依賴於主鍵列,而不能存在部分依賴。示例以下:
mechanism_id (組織機構代碼) | employee_id (僱員編號) | ename (僱員名稱) | mname (機構名稱) |
---|---|---|---|
28193182 | 10001 | heibaiying | XXXX公司 |
以上是一張全市在職人員統計表,主鍵爲:機構編碼 + 僱員編號。表中的僱員名稱徹底依賴於此聯合主鍵,但機構名稱卻只依賴於機構編碼,這就是部分依賴,所以違背了第二範式。此時經常使用的解決方式是創建一張組織機構與組織名稱的字典表。
非主鍵列不能依賴於其餘非主鍵列,若是其餘非主鍵列又依賴於主鍵列,此時就出現了傳遞依賴。示例以下:
employee_id (僱員編號) | ename (僱員名稱) | dept_no (部門編號) | dname(部門名稱) |
---|---|---|---|
10001 | heibaiying | 06 | 開發部 |
以上是一張僱員表,僱員名稱和所屬的部門編號都依賴於主鍵 employee_id ,但部門名稱卻依賴於部門編號,此時就出現了非主鍵列依賴於其餘非主鍵列,這就違背的第三範式。此時經常使用的解決方式是創建一張部門表用於維護部門相關的信息。
從上面的例子中咱們也能夠看出,想要徹底遵循三範式設計,可能須要額外增長不少表來進行維護。因此在平常開發中,基於其餘因素的綜合考量,可能並不會徹底遵循範式設計,甚至可能違反範式設計,這就是反範式設計。
更多文章,歡迎訪問個人 GitHub 倉庫:Full-Stack-Notes !