存儲(chǔ)引擎
現(xiàn)在常用的存儲(chǔ)引擎是InnoDB,以前常用的是MyISAM。
InnoDB
支持事務(wù)、支持行級(jí)鎖、支持外鍵、支持崩潰后數(shù)據(jù)恢復(fù)、支持MVCC;
InnoDB的鎖算法:
- Record lock:記錄鎖,單個(gè)行記錄上的鎖
- Gap lock:間隙鎖,鎖定一個(gè)范圍,不包括記錄本身
- Next-key lock:record+gap 臨鍵鎖,鎖定一個(gè)范圍,包含記錄本身
MyISAM
不支持事務(wù)、支持表級(jí)鎖、不支持外鍵、不支持崩潰后數(shù)據(jù)恢復(fù)、不支持MVCC
事務(wù)
事務(wù)是邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行。
ACID特性:
- 原子性(Atomicity) : 事務(wù)是最小的執(zhí)行單位,不允許分割。事務(wù)的原子性確保動(dòng)作要么全部完成,要么完全不起作用;
- 一致性(Consistency): 執(zhí)行事務(wù)前后,數(shù)據(jù)保持一致,例如轉(zhuǎn)賬業(yè)務(wù)中,無(wú)論事務(wù)是否成功,轉(zhuǎn)賬者和收款人的總額應(yīng)該是不變的;
- 隔離性(Isolation): 并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí),一個(gè)用戶的事務(wù)不被其他事務(wù)所干擾,各并發(fā)事務(wù)之間數(shù)據(jù)庫(kù)是獨(dú)立的;
- 持久性(Durability): 一個(gè)事務(wù)被提交之后。它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變是持久的,即使數(shù)據(jù)庫(kù)發(fā)生故障也不應(yīng)該對(duì)其有任何影響。
如何實(shí)現(xiàn):
- MySQL InnoDB 引擎使用 redo log(重做日志) 保證事務(wù)的持久性,使用 undo log(回滾日志) 來(lái)保證事務(wù)的原子性。
- MySQL InnoDB 引擎通過(guò) 鎖機(jī)制、MVCC 等手段來(lái)保證事務(wù)的隔離性( 默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ )。
- 保證了事務(wù)的持久性、原子性、隔離性之后,一致性才能得到保障。
- 事務(wù)并發(fā)的問(wèn)題:
如何實(shí)現(xiàn):
MySQL InnoDB 引擎使用 redo log(重做日志) 保證事務(wù)的持久性,使用 undo log(回滾日志) 來(lái)保證事務(wù)的原子性。
MySQL InnoDB 引擎通過(guò) 鎖機(jī)制、MVCC 等手段來(lái)保證事務(wù)的隔離性( 默認(rèn)支持的隔離級(jí)別是 REPEATABLE-READ )。
保證了事務(wù)的持久性、原子性、隔離性之后,一致性才能得到保障。
事務(wù)并發(fā)的問(wèn)題:
- 臟讀(Dirty read): 當(dāng)一個(gè)事務(wù)正在訪問(wèn)數(shù)據(jù)并且對(duì)數(shù)據(jù)進(jìn)行了修改,而這種修改還沒(méi)有提交到數(shù)據(jù)庫(kù)中,這時(shí)另外一個(gè)事務(wù)也訪問(wèn)了這個(gè)數(shù)據(jù),然后使用了這個(gè)數(shù)據(jù)。因?yàn)檫@個(gè)數(shù)據(jù)是還沒(méi)有提交的數(shù)據(jù),那么另外一個(gè)事務(wù)讀到的這個(gè)數(shù)據(jù)是“臟數(shù)據(jù)”,依據(jù)“臟數(shù)據(jù)”所做的操作可能是不正確的。
- 丟失修改(Lost to modify): 指在一個(gè)事務(wù)讀取一個(gè)數(shù)據(jù)時(shí),另外一個(gè)事務(wù)也訪問(wèn)了該數(shù)據(jù),那么在第一個(gè)事務(wù)中修改了這個(gè)數(shù)據(jù)后,第二個(gè)事務(wù)也修改了這個(gè)數(shù)據(jù)。這樣第一個(gè)事務(wù)內(nèi)的修改結(jié)果就被丟失,因此稱為丟失修改。 例如:事務(wù) 1 讀取某表中的數(shù)據(jù) A=20,事務(wù) 2 也讀取 A=20,事務(wù) 1 修改 A=A-1,事務(wù) 2 也修改 A=A-1,最終結(jié)果 A=19,事務(wù) 1 的修改被丟失。
- 不可重復(fù)讀(Unrepeatable read): 指在一個(gè)事務(wù)內(nèi)多次讀同一數(shù)據(jù)。在這個(gè)事務(wù)還沒(méi)有結(jié)束時(shí),另一個(gè)事務(wù)也訪問(wèn)該數(shù)據(jù)。那么,在第一個(gè)事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個(gè)事務(wù)的修改導(dǎo)致第一個(gè)事務(wù)兩次讀取的數(shù)據(jù)可能不太一樣。這就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的情況,因此稱為不可重復(fù)讀。
- 幻讀(Phantom read): 幻讀與不可重復(fù)讀類似。它發(fā)生在一個(gè)事務(wù)(T1)讀取了幾行數(shù)據(jù),接著另一個(gè)并發(fā)事務(wù)(T2)插入了一些數(shù)據(jù)時(shí)。在隨后的查詢中,第一個(gè)事務(wù)(T1)就會(huì)發(fā)現(xiàn)多了一些原本不存在的記錄,就好像發(fā)生了幻覺(jué)一樣,所以稱為幻讀。
事務(wù)的隔離級(jí)別:
- READ-UNCOMMITTED(讀取未提交): 最低的隔離級(jí)別,允許讀取尚未提交的數(shù)據(jù)變更,可能會(huì)導(dǎo)致臟讀、幻讀或不可重復(fù)讀。
- READ-COMMITTED(讀取已提交): 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生。
- REPEATABLE-READ(可重復(fù)讀): 對(duì)同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
- SERIALIZABLE(可串行化): 最高的隔離級(jí)別,完全服從 ACID 的隔離級(jí)別。所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。
InnoDB默認(rèn)隔離級(jí)別是可重復(fù)讀,可重復(fù)讀無(wú)法解決幻讀問(wèn)題,需要加鎖讀來(lái)解決可重復(fù)讀級(jí)別下的幻讀問(wèn)題,這個(gè)鎖就是上面提到的Next-Key Lock;
只有在分布式事務(wù)情況下會(huì)用到可串行化隔離級(jí)別
索引
- 常見(jiàn)的索引結(jié)構(gòu):Hash、B樹(shù)、B+樹(shù),InnoDB使用的索引是B+樹(shù)
B樹(shù)和B+樹(shù)的區(qū)別
- B 樹(shù)的所有節(jié)點(diǎn)既存放鍵(key) 也存放 數(shù)據(jù)(data),而 B+樹(shù)只有葉子節(jié)點(diǎn)存放 key 和 data,其他內(nèi)節(jié)點(diǎn)只存放 key。
- B 樹(shù)的葉子節(jié)點(diǎn)都是獨(dú)立的;B+樹(shù)的葉子節(jié)點(diǎn)有一條引用鏈指向與它相鄰的葉子節(jié)點(diǎn)。
- B 樹(shù)的檢索的過(guò)程相當(dāng)于對(duì)范圍內(nèi)的每個(gè)節(jié)點(diǎn)的關(guān)鍵字做二分查找,可能還沒(méi)有到達(dá)葉子節(jié)點(diǎn),檢索就結(jié)束了。而 B+樹(shù)的檢索效率就很穩(wěn)定了,任何查找都是從根節(jié)點(diǎn)到葉子節(jié)點(diǎn)的過(guò)程,葉子節(jié)點(diǎn)的順序檢索很明顯。
索引類型
- 主鍵索引(屬于聚集索引)
- 二級(jí)索引(屬于非聚集索引):
- 唯一索引
- 普通索引
- 前綴索引
- 全文索引
覆蓋索引:如果一個(gè)索引包含(或者說(shuō)覆蓋)所有需要查詢的字段的值,我們就稱之為“覆蓋索引”。
一致性非鎖定讀
實(shí)現(xiàn)一致性非鎖定讀的方法通常是使用版本號(hào)或者時(shí)間戳字段,每更新一次就版本號(hào)+1或者更新時(shí)間戳。而在InnoDB存儲(chǔ)引擎中對(duì)非鎖定讀的實(shí)現(xiàn)是通過(guò)多版本控制(MVCC)實(shí)現(xiàn)的,讀的數(shù)據(jù)屬于快照讀
事務(wù)的可重復(fù)讀級(jí)別就是通過(guò)MVCC實(shí)現(xiàn)的
在一致性非鎖定讀下,即使讀取的記錄已被其它事務(wù)加上X 鎖,這時(shí)記錄也是可以被讀取的,即讀取的快照數(shù)據(jù)。上面說(shuō)了,在 Repeatable Read 下 MVCC 防止了部分幻讀,這邊的 “部分” 是指在 一致性非鎖定讀 情況下,只能讀取到第一次查詢之前所插入的數(shù)據(jù)(根據(jù) Read View 判斷數(shù)據(jù)可見(jiàn)性,Read View 在第一次查詢時(shí)生成)。但是!如果是當(dāng)前讀 ,每次讀取的都是最新數(shù)據(jù),這時(shí)如果兩次查詢中間有其它事務(wù)插入數(shù)據(jù),就會(huì)產(chǎn)生幻讀。所以, InnoDB 在實(shí)現(xiàn)Repeatable Read 時(shí),如果執(zhí)行的是當(dāng)前讀,則會(huì)對(duì)讀取的記錄使用 Next-key Lock ,來(lái)防止其它事務(wù)在間隙間插入數(shù)據(jù)
MVCC
MVCC的實(shí)現(xiàn)主要依賴隱藏字段、Read View、undo log。InnoDB會(huì)通過(guò)隱藏字段里的DB_TRX_ID和Read View來(lái)判斷數(shù)據(jù)的可見(jiàn)性,如果不可見(jiàn),則通過(guò)DB_ROLL_PTR 找到undo log對(duì)應(yīng)歷史版本。每個(gè)事務(wù)讀到的數(shù)據(jù)版本可能是不一樣的,在同一個(gè)事務(wù)中,用戶只能看到該事務(wù)創(chuàng)建 Read View 之前已經(jīng)提交的修改和該事務(wù)本身做的修改
Read View 主要是用來(lái)做可見(jiàn)性判斷,里面保存了 “當(dāng)前對(duì)本事務(wù)不可見(jiàn)的其他活躍事務(wù)”
讀提交(RC)級(jí)別和可重復(fù)讀(RR)級(jí)別下,MVCC的不同之處在于
- RC是每次select查詢前都會(huì)生成一個(gè)ReadView
- RR是在事務(wù)開(kāi)啟后第一個(gè)select數(shù)據(jù)前生成一個(gè)ReadView
鎖定讀
如果執(zhí)行的是下列語(yǔ)句,就是 鎖定讀(Locking Reads)
- select ... lock in share mode(加讀鎖)
- select ... for update(加寫(xiě)鎖)
- insert、update、delete 操作(加寫(xiě)鎖)
鎖定讀讀取的是最新版本數(shù)據(jù),叫做當(dāng)前讀
|