一、為什么MySQL在innodb引擎中即使使用了MVCC機(jī)制仍然會(huì)出現(xiàn)丟失更新
mvcc在innodb中只負(fù)責(zé)解決讀寫(xiě)沖突,把普通select語(yǔ)句變成快照讀。寫(xiě)沖突仍然是靠鎖來(lái)解決的。因此要解決你說(shuō)的丟失更新,要用select…for update主動(dòng)加x鎖。
當(dāng)然mvcc不是說(shuō)完全不能解決丟失更新,比如postgresql的serializable隔離級(jí)別下,遇到寫(xiě)寫(xiě)沖突直接向客戶(hù)端返回異常,保證只有一個(gè)事務(wù)可以更新成功。
Mysql在可重復(fù)讀隔離級(jí)別下可保證事務(wù)較高的隔離性,同樣的sql查詢(xún)語(yǔ)句在一個(gè)事務(wù)里多次執(zhí)行查詢(xún)結(jié)果相同,就算其它事務(wù)對(duì)數(shù)據(jù)有修改也不會(huì)影響當(dāng)前事務(wù)sql語(yǔ)句的查詢(xún)結(jié)果
?? 這個(gè)隔離性就是靠MVCC(Multi-Version Concurrency Control)機(jī)制保證
對(duì)一行數(shù)據(jù)的讀和寫(xiě)兩個(gè)操作默認(rèn)是不會(huì)通過(guò)加鎖互斥來(lái)保證隔離性,避免了頻繁加鎖互斥而在串行化隔離級(jí)別為了保證較高的隔離性是通過(guò)將所有操作加鎖互斥來(lái)實(shí)現(xiàn)的延伸閱讀:
二、MVCC多版本并發(fā)控制機(jī)制的實(shí)現(xiàn)
undo日志版本鏈?zhǔn)侵敢恍袛?shù)據(jù)被多個(gè)事務(wù)依次修改過(guò)后,在每個(gè)事務(wù)修改完后,Mysql會(huì)保留修改前的數(shù)據(jù)undo回滾日志,并且用兩個(gè)隱藏字段trx_id和roll_pointer把這些undo日志串聯(lián)起來(lái)形成一個(gè)歷史記錄版本鏈。
在可重復(fù)讀隔離級(jí)別,當(dāng)事務(wù)開(kāi)啟,執(zhí)行任何查詢(xún)sql時(shí)會(huì)生成當(dāng)前事務(wù)的一致性視圖read-view該視圖在事務(wù)結(jié)束之前都不會(huì)變化(如果是讀已提交隔離級(jí)別在每次執(zhí)行查詢(xún)sql時(shí)都會(huì)重新生成)該視圖由執(zhí)行查詢(xún)時(shí)所有未提交事務(wù)id數(shù)組(數(shù)組里最小的id為min_id)和已創(chuàng)建的最大事務(wù)id(max_id)組成事務(wù)里任何sql的查詢(xún)結(jié)果需要從對(duì)應(yīng)版本鏈里的最新數(shù)據(jù)開(kāi)始逐條跟read-view做比對(duì)從而得到最終的快照結(jié)果1.如果 row 的 trx_id 落在綠色部分( trx_id 2. 如果 row 的 trx_id 落在紅色部分( trx_id>max_id ),表示這個(gè)版本是由將來(lái)啟動(dòng)的事務(wù)生成的,是不可見(jiàn)的(若 row 的 trx_id 就是當(dāng)前自己的事務(wù)是可見(jiàn)的); 3. 如果 row 的 trx_id 落在黃色部分(min_id <=trx_id<= max_id),那就包括兩種情況: 若 row 的 trx_id 在視圖數(shù)組中,表示這個(gè)版本是由還沒(méi)提交的事務(wù)生成的,不可見(jiàn)(若 row 的 trx_id 就是當(dāng)前自己的事務(wù),是可見(jiàn)的); 若 row 的 trx_id 不在視圖數(shù)組中,表示這個(gè)版本是已經(jīng)提交了的事務(wù)生成的,可見(jiàn)。