并行事務(wù)會(huì)引發(fā)什么問題?
? ? ? 同時(shí)處理多個(gè)事務(wù)的時(shí)候,就可能出現(xiàn)臟讀(dirty read)、不可重復(fù)讀(non-repeatable read)、幻讀(phantom read)的問題。
1、臟讀。如果一個(gè)事務(wù)「讀到」了另一個(gè)「未提交事務(wù)修改過的數(shù)據(jù)」,就意味著發(fā)生了「臟讀」現(xiàn)象.
2、不可重復(fù)讀。在一個(gè)事務(wù)內(nèi)多次讀取同一個(gè)數(shù)據(jù),如果出現(xiàn)前后兩次讀到的數(shù)據(jù)不一樣的情況,就意味著發(fā)生了「不可重復(fù)讀」現(xiàn)象。
3、幻讀。在一個(gè)事務(wù)內(nèi)多次查詢某個(gè)符合查詢條件的「記錄數(shù)量」,如果出現(xiàn)前后兩次查詢到的記錄數(shù)量不一樣的情況,就意味著發(fā)生了「幻讀」現(xiàn)象
事務(wù)有哪些特性?
? ? ? 事務(wù)的四大特性 : ACID
1、原子性(Atomicity): 一個(gè)事務(wù)中的所有操作,要么全部完成,要么全部不完成,不會(huì)結(jié)束在中間某個(gè)環(huán)節(jié), 不會(huì)出現(xiàn) a 扣錢了, b 沒加錢。
2、一致性(Consistency):是指事務(wù)操作前和操作后,數(shù)據(jù)滿足完整性約束,數(shù)據(jù)庫保持一致性狀態(tài), 總錢數(shù)不會(huì)少
3、隔離性(Isolation): 數(shù)據(jù)庫允許多個(gè)并發(fā)事務(wù)同時(shí)對(duì)其數(shù)據(jù)進(jìn)行讀寫和修改的能力, 每個(gè)事務(wù)都有一個(gè)完整的數(shù)據(jù)空間,對(duì)其他并發(fā)事務(wù)是隔離的, 也就是 a 購買不會(huì)導(dǎo)致 b 購買
4、持久性(Durability):事務(wù)處理結(jié)束后,對(duì)數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會(huì)丟失
InnoDB 引擎通過什么技術(shù)來保證事務(wù)的這四個(gè)特性的呢?
1、原子性 : 通過undo log (回滾日志) 實(shí)現(xiàn)
2、隔離性 : 通過 MVCC (多并發(fā)版本控制) 或鎖機(jī)制保證
3、持久性 : 通過 redo log (重做日志) 實(shí)現(xiàn)
4、一致性 : 通過原子性 + 隔離性 + 持久性實(shí)現(xiàn)
事務(wù)的隔離級(jí)別有哪些?
1、讀未提交(read uncommitted),指一個(gè)事務(wù)還沒提交時(shí),它做的變更就能被其他事務(wù)看到, 可能發(fā)生臟讀、不可重復(fù)讀和幻讀現(xiàn)象。
2、讀提交(read committed),指一個(gè)事務(wù)提交之后,它做的變更才能被其他事務(wù)看到, 可能發(fā)生不可重復(fù)讀和幻讀現(xiàn)象
3、可重復(fù)讀(repeatable read),指一個(gè)事務(wù)執(zhí)行過程中看到的數(shù)據(jù),一直跟這個(gè)事務(wù)啟動(dòng)時(shí)看到的數(shù)據(jù)是一致的**,可能發(fā)生幻讀現(xiàn)象, 但是它很大程度上避免幻讀現(xiàn)象, MySQL InnoDB 引擎的默認(rèn)隔離級(jí)別。
4、串行化(serializable );會(huì)對(duì)記錄加上讀寫鎖,在多個(gè)事務(wù)對(duì)這條記錄進(jìn)行讀寫操作時(shí),如果發(fā)生了讀寫沖突的時(shí)候,后訪問的事務(wù)必須等前一個(gè)事務(wù)執(zhí)行完成,才能繼續(xù)執(zhí)行。
這四種隔離級(jí)別具體是如何實(shí)現(xiàn)的呢?
1、讀未提交 : 直接讀取最新的數(shù)據(jù)。
2、串行化 : 讀寫鎖。
3、讀提交和可重復(fù)讀 : 通過 Read View 來實(shí)現(xiàn) , 區(qū)別在于生成 Read View 的時(shí)機(jī)不同,。
3-1、讀提交是在「每個(gè)語句執(zhí)行前」都會(huì)重新生成一個(gè) Read View。
3-2、可重復(fù)讀是「啟動(dòng)事務(wù)時(shí)」生成一個(gè) Read View。
Read View 在 MVCC 里如何工作的?

m_ids(Read View中未提交事務(wù)ID列表) :指的是在創(chuàng)建 Read View 時(shí),當(dāng)前數(shù)據(jù)庫中啟動(dòng)但是未提交的事務(wù) id 列表,注意是一個(gè)列表。
min_trx_id(Read View中 未提交事務(wù)列表中事務(wù)id最小的一個(gè)) : 指的是在創(chuàng)建 Read View 時(shí), 啟動(dòng)但是未提交的事務(wù) id 列表中事務(wù) id 最小的值。
max_trx_id(Read View中? 未提交事務(wù)列表中預(yù)留下一個(gè)事務(wù)的ID值) :這個(gè)并不是 m_ids 的最大值,而是創(chuàng)建 Read View 時(shí)當(dāng)前數(shù)據(jù)庫中應(yīng)該給下一個(gè)事務(wù)的 id 值,也就是全局事務(wù)中最大的事務(wù) id 值 + 1。
creator_trx_id :指的是創(chuàng)建該 Read View 的事務(wù)的事務(wù) id
聚簇索引記錄中的兩個(gè)隱藏列。

trx_id : 該事務(wù)的事務(wù) id
roll_pointer : 這個(gè)隱藏列是個(gè)指針,指向每一個(gè)舊版本記錄,可以通過它找到修改前的記錄。
MVCC 工作過程

一個(gè)事務(wù)去訪問記錄的時(shí)候,除了自己的更新記錄總是可見之外,還有這幾種情況:
1、如果該記錄的 trx_id 小于 Read View 的 min_trx_id, 說明該記錄是在創(chuàng)建 Read View 之前的事務(wù)生成的, 可見。
2、如果該記錄的 trx_id 大于 Read View 的 max_trx_id, 說明該記錄是在創(chuàng)建 Read View 之后的事務(wù)生成的, 不可見。
3、如果記錄的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之間, 需要判斷 trx_id 是否在 m_ids 列表中
? ? 3-1、如果 trx_id 在 m_ids 列表中, 說明創(chuàng)建該記錄的事務(wù)還未提交, 不可見。
? ? 3-2、如果 trx_id 不在 m_ids 列表中, 說明創(chuàng)建該記錄的事務(wù)已經(jīng)提交, 可見。
當(dāng)前不可見的可以通過 roll_pointer 指針尋找 undo log 版本鏈找到事務(wù)開始時(shí)的數(shù)據(jù)。這種通過「版本鏈」來控制并發(fā)事務(wù)訪問同一個(gè)記錄時(shí)的行為就叫 MVCC(多版本并發(fā)控制)
MySQL 可重復(fù)讀隔離級(jí)別,完全解決幻讀了嗎?
? ? ? 并沒有,還是會(huì)發(fā)生幻讀現(xiàn)象。通過串行讀來避免。
當(dāng)前讀是如何避免幻讀的?
? ? ? MySQL 里除了普通查詢是快照讀,其他都是當(dāng)前讀(讀提交),比如 update、insert、delete,這些語句執(zhí)行前都會(huì)查詢最新版本的數(shù)據(jù),然后再做進(jìn)一步的操作, 另外, select ... for update 這種查詢語句是當(dāng)前讀,每次執(zhí)行的時(shí)候都是讀取最新的數(shù)據(jù)。
? ? ? ? Innodb 引擎為了解決「可重復(fù)讀」隔離級(jí)別使用「當(dāng)前讀」而造成的幻讀問題,就引出了間隙鎖(next-key lock 是間隙鎖+記錄鎖的組合)。
幻讀被完全解決了嗎?
? ? ? 可重復(fù)讀隔離級(jí)別下雖然很大程度上避免了幻讀,但是還是沒有能完全解決幻讀。
總結(jié)
? ? ? 在多線程并發(fā)的場景下,會(huì)經(jīng)常產(chǎn)生事務(wù)并行,而并行事務(wù)會(huì)發(fā)生臟讀、不可重復(fù)讀、幻讀問題,這跟事務(wù)本身的特性相駁。事務(wù)的特性:ACID? 原子性 一致性 隔離性 持久性。而這些特性是通過一些邏輯進(jìn)行保證,但是如果每次操作都保證絕對(duì)的一致性,會(huì)非常影響性能。為了盡可能的保證性能,事務(wù)的隔離級(jí)別分為4種讀未提交、讀提交、可重復(fù)讀、串行讀。可以根據(jù)業(yè)務(wù)場景選擇不同的隔離級(jí)別進(jìn)行處理,而這些隔離級(jí)別通過MVCC/讀寫鎖來保證。另外mysql默認(rèn)可重復(fù)讀隔離級(jí)別。