事務(wù)原理:
為了支持事務(wù)怜森,Innodb引入了下面幾個概念:
一谤牡、MYSQL 日志介紹:
1.binlog
binlog常用來進(jìn)行數(shù)據(jù)恢復(fù)翅萤、數(shù)據(jù)庫復(fù)制套么,常見的mysql主從架構(gòu),就是采用slave同步master的binlog實(shí)現(xiàn)的, 另外通過解析binlog能夠?qū)崿F(xiàn)mysql到其他數(shù)據(jù)源(如ElasticSearch)的數(shù)據(jù)復(fù)制省咨。
2.redo log- 記錄新數(shù)據(jù)的備份茸炒,保證事務(wù)持久性
當(dāng)客戶端執(zhí)行每條SQL(更新語句)時壁公,redo log會被首先寫入log buffer;當(dāng)客戶端執(zhí)行COMMIT命令時比肄,log buffer中的內(nèi)容會被視情況刷新到磁盤芳绩。在事務(wù)提交前妥色,只要將Redo Log持久化即可嘹害,不需要將數(shù)據(jù)持久化笔呀。當(dāng)系統(tǒng)崩潰時髓需,雖然數(shù)據(jù)沒有持久化僚匆,但是Redo Log已經(jīng)持久化白热。當(dāng)Mysql失效重啟進(jìn)行恢復(fù)時重新執(zhí)行redo log記錄的SQL進(jìn)行數(shù)據(jù)恢復(fù)。redo log在磁盤上作為一個獨(dú)立的文件存在,即Innodb的log文件攻臀。
3.undo log-保證事務(wù)的原子性? & 實(shí)現(xiàn)數(shù)據(jù)多版本MVCC
為了滿足事務(wù)的原子性刨啸,在操作任何數(shù)據(jù)之前设联,首先將數(shù)據(jù)備份到Undo Log.然后再進(jìn)行數(shù)據(jù)的修改。如果出現(xiàn)了錯誤或者用戶執(zhí)行了ROLLBACK語句换团,系統(tǒng)可以利用Undo Log中的備份將數(shù)據(jù)恢復(fù)到事務(wù)開始之前的狀態(tài)艘包。undo log用于數(shù)據(jù)的回滾操作想虎。
? 具體操作是 copy事務(wù)前的數(shù)據(jù)行到undo buffer舌厨,在適合的時間(事務(wù)提交前)把undo buffer中的內(nèi)容刷新到磁盤裙椭。undo log記錄了數(shù)據(jù)變更歷史如煌恢,刪除前備份 insert插入undo log中原行記錄再在刪除行記錄上打刪除標(biāo)記瑰抵,修改前備份 insert插入undo log中原行記錄再修改更新行數(shù)據(jù)打標(biāo)事務(wù)id&回滾指針(指向undo log中原行記錄)二汛。通過undo log可以實(shí)現(xiàn)事務(wù)回滾肴颊,并且可以根據(jù)undo log回溯到某個特定的版本的數(shù)據(jù)婿着,實(shí)現(xiàn)MVCC竟宋。? 與redo log不同的是丘侠,磁盤上不存在單獨(dú)的undo log文件蜗字,所有的undo log均存放在主ibd數(shù)據(jù)文件中(表空間)。
二争便、mysql鎖-實(shí)現(xiàn)隔離性
Innodb提供了行鎖始花,分兩種:排他鎖酷宵、共享鎖浇垦。
共享鎖針對讀男韧,排他鎖針對寫此虑,完全等同讀寫鎖的概念朦前。
排他鎖: 寫鎖, 如果某個事務(wù)在更新某行韭寸,則其他事物無論是讀還是寫本行都必須等待恩伺;
共享鎖: 讀鎖晶渠,如果某個事物讀某行乱陡,則其他讀的事物無需等待,而寫事物則需等待仪壮。
事務(wù)隔離級別 - 隔離級別依次增強(qiáng),但是導(dǎo)致的問題是并發(fā)能力的減弱
眾所周知地是更新(update胳徽、insert积锅、delete)是一個事務(wù)過程爽彤,在Innodb中,查詢也是一個事務(wù)缚陷,只讀事務(wù)适篙。當(dāng)讀寫事務(wù)并發(fā)訪問同一行數(shù)據(jù)時,能讀到什么樣的內(nèi)容則依賴事務(wù)級別:
- READ_UNCOMMITTED:讀未提交-臟讀- 事務(wù)能夠看到其他事務(wù)沒有提及的修改箫爷,當(dāng)另一個事務(wù)又回滾了修改后的情況又被稱為臟讀dirty read
- READ_COMMITTED:讀提交-幻讀- 事務(wù)能夠看到其他事務(wù)提交后的修改嚷节,會出現(xiàn)一個事務(wù)內(nèi)兩次讀取數(shù)據(jù)可能因?yàn)槠渌聞?wù)提交的修改導(dǎo)致不一致的情況,稱為不可重復(fù)讀。
- REPEATABLE_READ:重復(fù)讀 (默認(rèn)級別)-每次都讀取指定的版本缓屠,這樣保證不會產(chǎn)生幻讀羊初,但可能讀不到最新的數(shù)據(jù)
- SERIALIZABLE:串行化 -鎖表涧卵,讀寫相互阻塞,使用較少
三讼庇、MYSQL事務(wù)原理:
Innodb ACID如何保證
? 原子性 redo + undo
? 一致性 redo
? 隔離性 鎖 + MVCC
? 持久性 redo
下面演示下事務(wù)對某行記錄的更新過程:
todo
F1~F6是某行列的名字,1~6是其對應(yīng)的數(shù)據(jù)。后面三個隱含字段分別對應(yīng)該行的事務(wù)號和回滾指針,假如這條數(shù)據(jù)是剛INSERT的硬梁,可以認(rèn)為ID為1罩息,其他兩個字段為空。
當(dāng)事務(wù)1更改該行的值時烘绽,會進(jìn)行如下操作:
用排他鎖鎖定該行
記錄redo log
把該行修改前的值Copy到undo log,即上圖中下面的行
修改當(dāng)前行的值,填寫事務(wù)編號,使回滾指針指向undo log中的修改前的行
commit 提交事務(wù) / rollback回滾事務(wù)(需要根據(jù)當(dāng)前回滾指針從undo log中找出事務(wù)修改前的行記錄版本,并恢復(fù)。)
上述過程確切地說是描述了UPDATE的事務(wù)過程剔难,Undo log分為Insert和Update兩種纯趋,delete可以看做是一種特殊的update痹栖,即在記錄上修改刪除標(biāo)記咆畏。
與事務(wù)1相同,此時undo log,中有有兩行記錄易结,并且通過回滾指針連在一起。todo delete
因此箩溃,如果undo log一直不刪除,則會通過當(dāng)前記錄的回滾指針回溯到該行創(chuàng)建時的初始內(nèi)容涣旨,所幸的時在Innodb中存在purge線程歪架,它會查詢那些比現(xiàn)在最老的活動事務(wù)還早的undo log,并刪除它們剔蹋,從而保證undo log文件不至于無限增長洛口。todo
四、MYSQL到底是怎么實(shí)現(xiàn)MVCC的湘纵?
在Mysql中MVCC是在Innodb存儲引擎中得到支持的汇歹,Innodb為每行記錄都實(shí)現(xiàn)了三個隱藏字段:
6字節(jié)的事務(wù)ID(DB_TRX_ID)-標(biāo)識該行所屬的事務(wù)、
7字節(jié)的回滾指針(DB_ROLL_PTR)、
隱藏的ID
innodb存儲的最基本row中包含一些額外的存儲信息 DATA_TRX_ID(標(biāo)識該行所屬的事務(wù))趟佃,DATA_ROLL_PTR(回滾指針)扇谣,DB_ROW_ID(隱藏id),DELETE BIT(刪除標(biāo)志)
6字節(jié)的DATA_TRX_ID 標(biāo)記了最新更新這條行記錄的transaction id闲昭,每處理一個事務(wù)罐寨,其值自動+1
7字節(jié)的DATA_ROLL_PTR 指向當(dāng)前記錄項(xiàng)的rollback segment的undo log記錄,找之前版本的數(shù)據(jù)就是通過這個指針
6字節(jié)的DB_ROW_ID序矩,當(dāng)由innodb自動產(chǎn)生聚集索引時鸯绿,聚集索引包括這個DB_ROW_ID的值,否則聚集索引中不包括這個值.簸淀,這個用于索引當(dāng)中
DELETE BIT位用于標(biāo)識該記錄是否被刪除瓶蝴,這里的不是真正的刪除數(shù)據(jù),而是標(biāo)志出來的刪除租幕。真正意義的刪除是在commit的時候
MYSQL如何實(shí)現(xiàn)事務(wù)舷手?
? MYSQL讀寫鎖:讀鎖和讀鎖之間不互斥,而寫鎖和寫鎖令蛉、讀鎖都互斥聚霜,提升并發(fā)讀能力狡恬。為了進(jìn)一步提升并發(fā)能力,實(shí)現(xiàn)讀寫之間也不沖突蝎宇,讀取數(shù)據(jù)時通過一種類似快照的方式將數(shù)據(jù)保存下來弟劲,這樣讀鎖就和寫鎖不沖突了,不同的事務(wù)session會看到自己特定版本的數(shù)據(jù)姥芥。
MVCC原理:
? 歷史版本存儲在UNDO Log空間
? 每條記錄帶版本號及回滾指針
? 廢棄記錄異步purge
MVCC實(shí)現(xiàn)
新增一個事務(wù)時事務(wù)id會增加兔乞,trx_id能夠表示事務(wù)開始的先后順序。
update undo log記錄了數(shù)據(jù)之前的數(shù)據(jù)信息凉唐,通過這些信息可以還原到之前版本的狀態(tài)庸追。
當(dāng)進(jìn)行插入操作時,生成的Insert undo log在事務(wù)提交后即可刪除台囱,因?yàn)槠渌聞?wù)不需要這個undo log淡溯。
進(jìn)行刪除修改操作時,會生成對應(yīng)的undo log簿训,并將當(dāng)前數(shù)據(jù)記錄中的db_roll_ptr指向新的undo log
---
MVCC概念: 提升事務(wù)并發(fā)處理能力
1咱娶、MVCC機(jī)制是行級鎖的一種妥協(xié),多線程事務(wù)讀取時强品,避免使用鎖膘侮,而是采用一種更小的開銷,允許非阻塞讀取的榛,寫操作進(jìn)行時只鎖定必要的記錄
2琼了、簡單的實(shí)現(xiàn)方式:MVCC保存某個時間點(diǎn)上的數(shù)據(jù)快照。一個事務(wù)內(nèi)夫晌,看到的是同一個版本的快照雕薪,數(shù)據(jù)一致。不同事務(wù)在同一時間點(diǎn)看到的數(shù)據(jù)會不一致慷丽,因?yàn)樗麄兊玫降臄?shù)據(jù)版本不一樣蹦哼。
MVCC可以任務(wù)它是行級鎖的一個變種,但是它在很多情況下避免了加鎖操作要糊,因此開銷更低纲熏。實(shí)現(xiàn)了非阻塞的讀操作提高db并發(fā)能力,寫操作也只鎖定必要的行锄俄。
MVCC的基本原理: 非理想態(tài)MVCC,提供讀的非阻塞
MVCC的實(shí)現(xiàn)局劲,通過保存數(shù)據(jù)在某個時間點(diǎn)的快照來實(shí)現(xiàn)的。這意味著一個事務(wù)無論執(zhí)行多長時間奶赠,在同一個事務(wù)里看到數(shù)據(jù)都是一致的鱼填。根據(jù)事務(wù)開始的時間不同,每個事務(wù)對同一張表同一個時刻看到的數(shù)據(jù)可能不同毅戈。
MVCC的基本特征:
每行數(shù)據(jù)都存在一個版本苹丸,每次數(shù)據(jù)更新時都更新該版本愤惰。
修改時Copy出當(dāng)前版本隨意修改,各個事務(wù)之間無干擾赘理。
保存時比較版本號宦言,如果成功(commit),則覆蓋原記錄商模;失敗則放棄copy(rollback)
就是每行都有版本號奠旺,保存時根據(jù)版本號決定是否成功,聽起來含有樂觀鎖的味道施流,而Innodb的實(shí)現(xiàn)方式是:
事務(wù)以排他鎖的形式修改原始數(shù)據(jù)
把修改前的數(shù)據(jù)存放于undo log响疚,通過回滾指針與主數(shù)據(jù)關(guān)聯(lián)
修改成功(commit)啥都不做,失敗則恢復(fù)undo log中的數(shù)據(jù)(rollback)
----
InnoDB存儲引擎MVCC的實(shí)現(xiàn)策略:
通過在每一行數(shù)據(jù)后面保存兩個隱藏的列實(shí)現(xiàn):當(dāng)前行創(chuàng)建時的版本號和刪除時的版本號(可能為空)瞪醋。這里的版本號并不是實(shí)際的時間值忿晕,而是系統(tǒng)版本號(可以理解為事務(wù)的ID)。每開始一個新的事務(wù)银受,系統(tǒng)版本號都會自動遞增杏糙。事務(wù)開始時刻的系統(tǒng)版本號會作為事務(wù)的版本號,用來和查詢到的每行記錄的版本號進(jìn)行比較蚓土。
每個事務(wù)又有自己的版本號,這樣事務(wù)內(nèi)執(zhí)行CRUD操作時赖淤,就通過版本號的比較來達(dá)到數(shù)據(jù)版本控制的目的蜀漆。具體做法見下面的示意圖。
MVCC具體的操作如下:
SELECT:InnoDB會根據(jù)以下兩個條件檢查[需同時滿足]每行記錄:
1)InnoDB只查找版本早于當(dāng)前事務(wù)版本的數(shù)據(jù)行(也就是咱旱,行的系統(tǒng)版本號小于或等于事務(wù)的系統(tǒng)版本號)确丢,這樣可以確保事務(wù)讀取的行,要么是在事務(wù)開始前已經(jīng)存在的吐限,要么是事務(wù)自身插入或者修改過的鲜侥。
2)行的刪除版本要么未定義,要么大于當(dāng)前事務(wù)版本號诸典。這可以確保事務(wù)讀取到的行描函,在事務(wù)開始之前未被刪除。
INSERT:InnoDB為新插入的每一行保存當(dāng)前系統(tǒng)版本號作為行版本號狐粱。
DELETE:InnoDB為刪除的每一行保存當(dāng)前系統(tǒng)版本號作為行刪除標(biāo)識舀寓。
UPDATE:InnoDB為插入一行新記錄,保存當(dāng)前系統(tǒng)版本號作為行版本號肌蜻,同時保存當(dāng)前系統(tǒng)的版本號為原來的行作為刪除標(biāo)識互墓。
說明
insert操作時 “創(chuàng)建時間”=DB_ROW_ID,這時蒋搜,“刪除時間 ”是未定義的篡撵;
update時判莉,復(fù)制新增行的“創(chuàng)建時間”=DB_ROW_ID,刪除時間未定義育谬,舊數(shù)據(jù)行“創(chuàng)建時間”不變券盅,刪除時間=該事務(wù)的DB_ROW_ID;
delete操作斑司,相應(yīng)數(shù)據(jù)行的“創(chuàng)建時間”不變渗饮,刪除時間=該事務(wù)的DB_ROW_ID;
select操作對兩者都不修改宿刮,只讀相應(yīng)的數(shù)據(jù)
保存這兩個額外系統(tǒng)版本號互站,使大多數(shù)操作都可以不用加鎖。這樣設(shè)計(jì)使得計(jì)數(shù)據(jù)操作很簡單僵缺,性能很好胡桃,并且也能保證只會讀取到符合標(biāo)準(zhǔn)的行。不足之處是每行記錄都需要額外的存儲空間磕潮,需要做更多的行檢查工作翠胰,以及一些額外的維護(hù)工作。
MVCC只在REPEATABLE READ和READ COMMITED兩個隔離級別下工作自脯,其它兩個隔離級別和MVCC不兼容之景。
---
參考:
http://blog.csdn.net/chen77716/article/details/6742128
https://liuzhengyang.github.io/2017/04/18/innodb-mvcc/
http://www.cnblogs.com/chenpingzhao/p/5065316.html
http://zhangxiong0301.iteye.com/blog/2230193