MySQL插入是并發(fā)還是串行?
當(dāng)前位置:點晴教程→知識管理交流
→『 技術(shù)文檔交流 』
最近和同事爭辯起來,MySQL插入是并發(fā)還是串行,我記得明明是串行插入,同事非要和我杠,說MySQL可以并發(fā)插入。 我要親自試驗一下,打他的臉! 定義表結(jié)構(gòu)MySQL 實驗版本 8.0,首先定義 用戶信息表userInfo,其中id為自增,name具有唯一索引。 驗證流程默認(rèn)情況下,在命令行中 MySQL會自動提交,每個SQL執(zhí)行會非???,無法驗證同時執(zhí)行的兩個事務(wù)之間是否存在阻塞情況,所以需要顯示開啟事務(wù)和提交事務(wù) 。 開始驗證首先,我們開啟兩個事務(wù)。在事務(wù)1中,首先插入一條記錄,暫時不提交。然后,在事務(wù)2中開啟一個新的事務(wù),并插入一條自增記錄。 如果MySQL的innodb插入是串行的,那么此時事務(wù)2的插入記錄將會被阻塞。如果沒有被阻塞,那就說明MySQL的innodb插入是并發(fā)執(zhí)行的。 實驗驗證事務(wù)2 的執(zhí)行記錄 如上圖所示,在事務(wù)1還未提交,事務(wù)2在事務(wù)1的間隙中插入一條記錄,插入操作立即成功,并且事務(wù)2的自增主鍵ID為2。這說明在MySQL中,當(dāng)一個事務(wù)正在插入記錄時,并不會阻塞其他事務(wù)的插入。 在MySQL中,多個事務(wù)之間的插入操作是并發(fā)進(jìn)行的,而不是串行進(jìn)行的。 我感覺自己的臉熱熱的,小丑竟是我自己,趕緊給同事認(rèn)了錯…… 我的認(rèn)知一直是錯誤的。 但是在底層存儲層面,MySQL會對數(shù)據(jù)頁加鎖。如果兩條記錄在同一個數(shù)據(jù)頁,實際寫入是串行的,但是事務(wù)層面是并發(fā)的。 想象一下,庫存扣減和新增庫存流水在同一個事務(wù)中,如果新增庫存流水是串行的,那將極大的降低庫存事務(wù)的并發(fā)度啊。 本以為驗證結(jié)束,打卡下班,結(jié)果發(fā)現(xiàn) MySQL插入似乎存在幻讀問題! 從下圖中可以觀察到,事務(wù)1在插入時似乎確實出現(xiàn)了幻讀問題! 事務(wù) 1 的執(zhí)行記錄顯示,事務(wù)1先于事務(wù)2開啟,但是事務(wù)1期間可以查詢到事務(wù)2提交的記錄。這說明有幻讀問題! 為什么出現(xiàn)幻讀?所謂幻讀,是指在一個事務(wù)讀取記錄時,另一個事務(wù)在此時插入或刪除了一條記錄,導(dǎo)致第一個事務(wù)再次讀取時發(fā)現(xiàn)記錄的數(shù)量發(fā)生了變化。 要想理解出現(xiàn)幻讀的原因,需要先了解MySQL是如何解決幻讀問題的。 為了解決幻讀問題,MySQL采用了間隙鎖和多版本并發(fā)控制(MVCC)的方法。間隙鎖會鎖定一段記錄的范圍,其他事務(wù)無法對這些記錄進(jìn)行更新或刪除操作。這樣,當(dāng)當(dāng)前事務(wù)再次進(jìn)行查詢時,就不會出現(xiàn)記錄數(shù)量的新增或減少的情況了。 MySQL 插入時加了什么鎖?MySQL 插入時存在幻讀問題,說明MySQL 并沒有加間隙鎖,主要考慮也是為了提高插入時并發(fā)度,如果添加間隙鎖,勢必導(dǎo)致插入并發(fā)度降低!MySQL 在插入之前會申請 插入意向鎖,而記錄本身不沖突(無唯一鍵沖突)插入意向鎖就不會沖突。 MySQL 文檔中記錄了 插入意向鎖
插入場景MVCC 不生效?除更新場景外,查詢場景也有幻讀的困惱。如果第一次查詢時只有3條記錄,再次查詢則變?yōu)?條,實在過于奇幻。 如果給普通的查詢語句添加間隙鎖,勢必極大的降低MySQL 的并發(fā)度,如果不能使用間隙鎖,還有哪些辦法解決幻讀呢? MySQL 通過引入MVCC解決查詢場景的幻讀問題。MVCC是多版本并發(fā)控制(Multiversion Concurrency Control)的縮寫,在MVCC中,每個事務(wù)可以看到數(shù)據(jù)庫的一個穩(wěn)定的快照,而不會被其他并發(fā)事務(wù)的修改所干擾。當(dāng)一個事務(wù)修改數(shù)據(jù)庫時,它會創(chuàng)建一個新的數(shù)據(jù)版本,而不是直接在原始數(shù)據(jù)上進(jìn)行修改。而其他事務(wù)仍然可以讀取原始數(shù)據(jù)的舊版本或者已經(jīng)提交的新版本,這樣就避免了讀取到未提交的數(shù)據(jù)或者被其他事務(wù)的寫操作所阻塞。
轉(zhuǎn)機出現(xiàn)了當(dāng)我在苦苦思考,為什么MVCC 沒有生效時,我隨手重新測試發(fā)現(xiàn),如果在 insert語句之前,使用select 查詢一下,就不會出現(xiàn)幻讀問題。 操作順序如下 我在事務(wù)1,開啟事務(wù)以后,新增了select 語句查詢,而后第六步,就不會再有幻讀問題…… 這真的實在太奇幻了。一波三折…… 由此可見 MySQL 插入并沒有幻讀問題,只是我的打開方式不對。我應(yīng)該先 select一下 ……,終究還是我錯了,但是我想問為什么?我為什么錯了? ReadView 是關(guān)鍵!除MVCC 外,MySQL InnoDB 引擎設(shè)計了 ReadView(可讀視圖) 的概念。 ReadView 判斷記錄的可見性,ReadView 實際上是當(dāng)前系統(tǒng)中所有活躍事務(wù)的列表,主要包含以下組成部分:
總結(jié)一下就是: 如果當(dāng)前事務(wù)id的生成時間發(fā)生在 記錄的更新之后,那么當(dāng)前事務(wù)就可以看見這個記錄,否則看不見!避免幻讀問題 那 ReadView 又是何時生成的呢?在 REPEATABLE READ 隔離級別下,每個事務(wù)執(zhí)行第一個 SELECT 語句時,會將當(dāng)前系統(tǒng)中的所有的活躍事務(wù)拷貝到一個列表生成 ReadView,后續(xù)所有的 SELECT 都是復(fù)用這個 ReadView。 REPEATABLE READ 隔離級別下,只有第一次 SELECT 才會生成 ReadView,后續(xù) SELECT 都會復(fù)用這個 ReadView,也就不存在新提交事務(wù)對這個 ReadView 的影響了。 所以 當(dāng)我在 事務(wù) 1 新增select語句,會生成一個ReadView,這個ReadView 生成時間要早于 事務(wù)2的時間,所以事務(wù)1 的后續(xù)所有查詢都不會看到事務(wù)2的記錄,從而避免幻讀問題發(fā)生。 總結(jié)
閱讀原文:原文鏈接 該文章在 2025/5/6 11:43:46 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |