什么是事務(wù)?
事務(wù)是邏輯上的一組操作,要么都執(zhí)行,要么都不執(zhí)行。
事務(wù)的特性?
ACID:
原子性: 事務(wù)是最小的執(zhí)行單位,不允許分割。事務(wù)的原子性確保動作要么全部完成,要么完全不起作用。
一致性: 執(zhí)行事務(wù)前后,數(shù)據(jù)保持一致。例如轉(zhuǎn)賬業(yè)務(wù)中,無論事務(wù)是否成功,轉(zhuǎn)賬者和收款人的總額應(yīng)該是不變的。
隔離性: 并發(fā)訪問數(shù)據(jù)庫時,一個用戶的事務(wù)不被其他事務(wù)所干擾,各并發(fā)事務(wù)之間數(shù)據(jù)庫是獨立。
持久性: 一個事務(wù)被提交之后,它對數(shù)據(jù)庫中數(shù)據(jù)的改變是持久的,即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響。
并發(fā)事務(wù)帶來的問題?
臟讀(Dirty read): 事務(wù)A在修改事務(wù),且還沒有提交,事務(wù)B讀取到了這個事務(wù)。
幻讀(Phantom read): 事務(wù)A讀取了一個范圍數(shù)據(jù),事務(wù)B在這個范圍內(nèi)插入(刪除)了數(shù)據(jù),事務(wù)A再次讀讀這個范圍數(shù)據(jù),發(fā)現(xiàn)多了(少了)一些數(shù)據(jù)。
不可重復(fù)讀(Unrepeatableread): 事務(wù)A讀取了一條數(shù)據(jù),事務(wù)B對這條數(shù)據(jù)進行了修改,事務(wù)A再次讀取這個數(shù)據(jù),發(fā)現(xiàn)讀取到的數(shù)據(jù)不一樣了。
丟失修改(Lost to modify): 事務(wù)A對一條數(shù)據(jù)進行修改,同時事務(wù)B對這條數(shù)據(jù)也進行修改,導(dǎo)致事務(wù)A對這條數(shù)據(jù)的修改丟失。例如a = 20,并發(fā)進行a = a - 1,最終結(jié)果是19,說明事務(wù)A的修改丟失了。
不可重復(fù)度和幻讀區(qū)別?
不可重復(fù)讀的重點是修改,幻讀的重點在于新增或者刪除。
事務(wù)隔離級別?
READ-UNCOMMITTED(讀取未提交): 最低的隔離級別,允許讀取尚未提交的數(shù)據(jù)變更,可能會導(dǎo)致臟讀、幻讀或不可重復(fù)讀。
READ-COMMITTED(讀取已提交): 允許讀取并發(fā)事務(wù)已經(jīng)提交的數(shù)據(jù),可以阻止臟讀,但是幻讀或不可重復(fù)讀仍有可能發(fā)生。
REPEATABLE-READ(可重復(fù)讀): 對同一字段的多次讀取結(jié)果都是一致的,除非數(shù)據(jù)是被本身事務(wù)自己所修改,可以阻止臟讀和不可重復(fù)讀,但幻讀仍有可能發(fā)生。
SERIALIZABLE(可串行化): 最高的隔離級別,完全服從ACID的隔離級別。所有的事務(wù)依次逐個執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說,該級別可以防止臟讀、不可重復(fù)讀以及幻讀。
MySQL InnoDB默認(rèn)支持的隔離級別?
MySQL InnoDB 存儲引擎的默認(rèn)支持的隔離級別是 REPEATABLE-READ(可重讀)。
為什么大部分?jǐn)?shù)據(jù)庫的默認(rèn)隔離級別都是讀已提交?
因為隔離級別越低,事務(wù)請求的鎖越少,所以大部分?jǐn)?shù)據(jù)庫系統(tǒng)的隔離級別都是 READ-COMMITTED(讀取提交內(nèi)容) ,但是 InnoDB 存儲引擎默認(rèn)使用 REPEATABLE-READ(可重讀) 并不會有任何性能損失。
一致性非鎖定讀和鎖定讀?
一致性非鎖定讀(Consistent Nonlocking Reads):
就是不加鎖讀,通常實現(xiàn)是使用版本號或者時間戳,在更新數(shù)據(jù)時,對版本號+1或者更新當(dāng)前時間戳。查詢時將當(dāng)前可見版本號與對應(yīng)記錄的版本號進行對比,如果記錄版本小于版本號小于可見版本號,表示該記錄可見。
InnoDB 存儲引擎的多版本控制 (multi versioning)是對非鎖定讀的實現(xiàn),如果讀取的行正在執(zhí)行 DELETE 或 UPDATE 操作,這時讀取操作不會去等待行上鎖的釋放。相反地,InnoDB 存儲引擎會去讀取行的一個快照數(shù)據(jù),對于這種讀取歷史數(shù)據(jù)的方式,我們叫它快照讀 (snapshot read)。
在 Repeatable Read 和 Read Committed 兩個隔離級別下,如果是執(zhí)行普通的 select 語句會使用 一致性非鎖定讀(MVCC:多版本并發(fā)控制)。并且在 Repeatable Read 下 MVCC 實現(xiàn)了可重復(fù)讀和防止部分幻讀(一致性非鎖定讀的時候,第一次讀取生成快照,以后每次都讀快照,阻止了這部分幻讀)。
鎖定讀(Locking Reads):
讀取的是數(shù)據(jù)的最新版本,這種讀也被稱為 當(dāng)前讀(current read)。
select ... lock in share mode:對記錄加 S 鎖,其它事務(wù)也可以加S鎖,如果加 x 鎖則會被阻塞。
select ... for update、insert、update、delete:對記錄加 X 鎖,且其它事務(wù)不能加任何鎖。
不管是S鎖還是X鎖,都不影響一致性非鎖定讀,所以MVCC可以實現(xiàn)Repeatable Read在一致性非鎖定讀的情況下的幻讀。但是如果是當(dāng)前讀(current read)又叫鎖定讀,則會通過對讀取的記錄使用 Next-key Lock ,來防止其它事務(wù)在間隙間插入數(shù)據(jù),來防止幻讀。
InnoDB 對 MVCC 的實現(xiàn)?
MVCC 的實現(xiàn)依賴于隱藏字段、Read View、undo log。首先通過隱藏字段和Read View來判斷數(shù)據(jù)的可見性。如不可見,則通過隱藏字段來找到undo log 中的歷史版本中,最后提交事務(wù)的數(shù)據(jù)。同一個事務(wù)中,用戶只能看到該事務(wù)創(chuàng)建 Read View 之前已經(jīng)提交的修改和該事務(wù)本身做的修改。
undo log 主要有兩個作用?
- 當(dāng)事務(wù)回滾時用于將數(shù)據(jù)恢復(fù)到修改前的樣子。
- 另一個作用是 MVCC ,當(dāng)讀取記錄時,若該記錄被其他事務(wù)占用或當(dāng)前版本對該事務(wù)不可見,則可以通過 undo log 讀取之前的版本數(shù)據(jù),以此實現(xiàn)非鎖定讀。
在 InnoDB 存儲引擎中 undo log 分為兩種?
insert undo log :指在 insert 操作中產(chǎn)生的 undo log。因為 insert 操作的記錄只對事務(wù)本身可見,對其他事務(wù)不可見,故該 undo log 可以在事務(wù)提交后直接刪除。不需要進行 purge 操作。
update undo log :update 或 delete 操作中產(chǎn)生的 undo log。該 undo log可能需要提供 MVCC 機制,因此不能在事務(wù)提交時就進行刪除。提交時放入 undo log 鏈表,等待 purge線程進行最后的刪除。
RC 和 RR 隔離級別下 MVCC 的差異?
在事務(wù)隔離級別 RC 和 RR (InnoDB 存儲引擎的默認(rèn)事務(wù)隔離級別)下,InnoDB 存儲引擎使用 MVCC(非鎖定一致性讀),但它們生成 Read View 的時機卻不同:
- 在 RC 隔離級別下的 每次select 查詢前都生成一個Read View (m_ids 列表)。
- 在 RR 隔離級別下只在事務(wù)開始后 第一次select 數(shù)據(jù)前生成一個Read View(m_ids 列表)。
MySQL支持的鎖介紹?
行鎖(Record Lock):鎖直接加在索引記錄上面,鎖住的是key。
間隙鎖(Gap Lock):鎖定索引記錄間隙,確保索引記錄的間隙不變。間隙鎖是針對事務(wù)隔離級別為可重復(fù)讀或以上級別而已的。
Next-Key Lock :行鎖和間隙鎖組合起來就叫Next-Key Lock。
Next-Key Lock 工作原理?
- 默認(rèn)情況下,InnoDB工作在可重復(fù)讀隔離級別下,并且會以Next-Key Lock的方式對數(shù)據(jù)行進行加鎖,這樣可以有效防止幻讀的發(fā)生。Next-Key Lock是行鎖和間隙鎖的組合,當(dāng)InnoDB掃描索引記錄的時候,會首先對索引記錄加上行鎖(Record Lock),再對索引記錄兩邊的間隙加上間隙鎖(Gap Lock)。加上間隙鎖之后,其他事務(wù)就不能在這個間隙修改或者插入記錄。
- Gap Lock在InnoDB的唯一作用就是防止其他事務(wù)的插入刪除操作,以此防止幻讀的發(fā)生。
MySQL在InnoDB存儲引擎在 RR 級別下解決幻讀的方式?
- 執(zhí)行普通 select,此時會以 MVCC 快照讀的方式讀取數(shù)據(jù)。
- 執(zhí)行 select...for update/lock in share mode、insert、update、delete 等當(dāng)前讀時,使用Next-key-Lock 防止幻讀。