事務(wù)(Transaction) 是指作為單個(gè)邏輯工作單元執(zhí)行的一系列操作溃蔫。
A琳猫、原子性(Atomicity)
表示組成一個(gè)事務(wù)的多個(gè)數(shù)據(jù)庫(kù)操作是一個(gè)不可分隔的原子單元伟叛,只有所有的操作執(zhí)行成功,整個(gè)事務(wù)才提交脐嫂,事務(wù)中任何一個(gè)數(shù)據(jù)庫(kù)操作失敗统刮,已經(jīng)執(zhí)行的任何操作都必須撤銷(xiāo),讓數(shù)據(jù)庫(kù)返回到初始狀態(tài)账千。
B侥蒙、一致性(Consistency)
事務(wù)操作成功后,數(shù)據(jù)庫(kù)所處的狀態(tài)和它的業(yè)務(wù)規(guī)則是一致的匀奏,即數(shù)據(jù)不會(huì)被破壞鞭衩。
C、隔離性(Isolation)
在并發(fā)數(shù)據(jù)操作時(shí),不同的事務(wù)擁有各自數(shù)據(jù)空間论衍,它們的操作不會(huì)對(duì)對(duì)方產(chǎn)生干擾恒水。數(shù)據(jù)庫(kù)規(guī)定了多種事務(wù)隔離級(jí)別,不同隔離級(jí)別對(duì)應(yīng)不同的干擾程度饲齐,隔離級(jí)別越高钉凌,數(shù)據(jù)一致性越好,但并發(fā)性越弱捂人。
D御雕、持久性(Durabiliy)
一旦事務(wù)提交成功后,事務(wù)中所有的數(shù)據(jù)操作都必須被持久化到數(shù)據(jù)庫(kù)中滥搭,即使提交事務(wù)后酸纲,數(shù)據(jù)庫(kù)馬上崩潰,在數(shù)據(jù)庫(kù)重啟時(shí)瑟匆,也必須能保證能夠通過(guò)某種機(jī)制恢復(fù)數(shù)據(jù)闽坡。
A愁溜、自動(dòng)提交事務(wù)
系統(tǒng)默認(rèn)每個(gè)TRANSACT-SQL命令都是一個(gè)事務(wù)處理疾嗅,由系統(tǒng)自動(dòng)開(kāi)始并提交。
B冕象、隱式事務(wù)
不需要顯示開(kāi)始事務(wù)代承,需要顯示提交,隱式事務(wù)是任何單獨(dú)的INSERT渐扮、UPDATE 或者DELETE語(yǔ)句構(gòu)成论悴。當(dāng)有大量的DDL和DML命令執(zhí)行時(shí)會(huì)自動(dòng)開(kāi)始,并一直保持到用戶(hù)明確提交為止墓律。
SHOW VARIABLES 查看變量膀估。
SET AUTOCOMMIT=0,關(guān)閉自動(dòng)提交功能耻讽。
需要顯示提交或者回滾察纯。
updatetablenamesetsname='孫悟空'wherestudentid='000000000000003';
commit;
或
rollback;
C、顯示事務(wù)
顯示事務(wù)是用戶(hù)自定義事務(wù)齐饮,以START TRANSACTION(事務(wù)開(kāi)始)開(kāi)頭捐寥,以 COMMIT(事務(wù)提交)或者 ROLLBACK(回滾事務(wù))語(yǔ)句結(jié)束。
starttransaction
updatetablenamesetsname='孫悟空'wherestudentid='000000000000003';
commit
或
rollback
D祖驱、分布式事務(wù)
跨越多個(gè)服務(wù)器的事務(wù)稱(chēng)為分布式事務(wù)握恳。從MySQL5.03開(kāi)始支持分布式事務(wù)。
A乡洼、開(kāi)始事務(wù)
標(biāo)記一個(gè)顯式事務(wù)的開(kāi)始點(diǎn)崇裁,即事務(wù)開(kāi)始。其語(yǔ)法如下:
START { TRAN | TRANSACTION }
B束昵、提交事務(wù)
標(biāo)記一個(gè)成功的隱性事務(wù)或顯式事務(wù)的結(jié)束拔稳,即事務(wù)提交。其語(yǔ)法如下:
COMMIT
C锹雏、回滾事務(wù)
將顯式事務(wù)或隱性事務(wù)回滾到事務(wù)的起點(diǎn)或事務(wù)內(nèi)的某個(gè)保存點(diǎn)巴比。其語(yǔ)法如下:
ROLLBACK
D、事務(wù)設(shè)置
SET AUTOCOMMIT 可以修改當(dāng)前連接事務(wù)提交方式礁遵。
SET AUTOCOMMIT=0轻绞,則需要明確的命令進(jìn)行提交或者回滾。
5佣耐、事務(wù)并發(fā)帶來(lái)的問(wèn)題
臟讀(Dirty Read)是指某個(gè)事務(wù)(A)讀取另外事務(wù)(B)尚未提交的更改數(shù)據(jù)政勃,并在讀取的數(shù)據(jù)的基礎(chǔ)上操作。如果恰巧 B事務(wù)回滾兼砖,那么 A事務(wù)讀到的數(shù)據(jù)根本是不被承認(rèn)的奸远。
不可重復(fù)讀(Unrepeatable Read)是指A事務(wù)讀取了B事務(wù)已經(jīng)提交的更改數(shù)據(jù)。
幻象讀(Phantom Read)
A事務(wù)讀取B事務(wù)提交的新增數(shù)據(jù)讽挟,這時(shí)A事務(wù)將出現(xiàn)幻象讀的問(wèn)題懒叛。
第一類(lèi)丟失更新
A事務(wù)撤銷(xiāo)時(shí),把已經(jīng)提交的B事務(wù)的更新數(shù)據(jù)覆蓋戏挡。
第二類(lèi)丟失更新
A事務(wù)覆蓋B事務(wù)已經(jīng)提交的數(shù)據(jù)芍瑞,造成B事務(wù)所做操作丟失晨仑。
SQL標(biāo)準(zhǔn)定義了4類(lèi)隔離級(jí)別洪己,包括了一些具體規(guī)則妥凳,用來(lái)限定事務(wù)內(nèi)外的哪些改變是可見(jiàn)的,哪些是不可見(jiàn)的答捕。低級(jí)別的隔離級(jí)一般支持更高的并發(fā)處理逝钥,并擁有更低的系統(tǒng)開(kāi)銷(xiāo)。
Read Uncommitted(讀取未提交內(nèi)容)
本隔離級(jí)別拱镐,事務(wù)可以讀取其他未提交事務(wù)的執(zhí)行結(jié)果艘款。讀取未提交的數(shù)據(jù),也被稱(chēng)之為臟讀(Dirty Read)沃琅。
Read Committed(讀取提交內(nèi)容)
大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)的默認(rèn)隔離級(jí)別(但不是MySQL默認(rèn)的)哗咆。事務(wù)只能讀取其他事務(wù)已經(jīng)提交的執(zhí)行結(jié)果。本隔離級(jí)別支持所謂的不可重復(fù)讀(Nonrepeatable Read)益眉,因?yàn)橥皇聞?wù)的其他實(shí)例在該實(shí)例處理其間可能會(huì)有新的commit晌柬,所以同一select可能返回不同結(jié)果姥份。
Repeatable Read(可重讀)
MySQL默認(rèn)的事務(wù)隔離級(jí)別,會(huì)給查詢(xún)的記錄做快照年碘,直到事務(wù)結(jié)束澈歉。確保同一事務(wù)的多個(gè)實(shí)例在并發(fā)讀取數(shù)據(jù)時(shí),會(huì)看到同樣的數(shù)據(jù)行屿衅,會(huì)導(dǎo)致幻讀(Phantom Read)埃难。幻讀指當(dāng)用戶(hù)讀取某一范圍的數(shù)據(jù)行時(shí)涤久,另一個(gè)事務(wù)又在該范圍內(nèi)插入了新行凯砍,當(dāng)用戶(hù)再讀取該范圍的數(shù)據(jù)行時(shí),會(huì)發(fā)現(xiàn)有新的“幻影” 行拴竹。InnoDB和Falcon存儲(chǔ)引擎通過(guò)多版本并發(fā)控制(MVCC悟衩,Multiversion Concurrency Control)機(jī)制解決了幻讀問(wèn)題。
Serializable(可串行化)
最高的隔離級(jí)別栓拜,對(duì)同一條記錄讀和修改的多個(gè)事務(wù)只能結(jié)束一個(gè)座泳,才能開(kāi)始下一個(gè)。
通過(guò)強(qiáng)制事務(wù)排序幕与,使之不可能相互沖突挑势,從而解決幻讀問(wèn)題。在每個(gè)讀的數(shù)據(jù)行上加上共享鎖啦鸣,可能導(dǎo)致大量的超時(shí)現(xiàn)象和鎖競(jìng)爭(zhēng)潮饱。
用戶(hù)可以用SET TRANSACTION語(yǔ)句改變單個(gè)會(huì)話(huà)或者所有新進(jìn)連接的隔離級(jí)別诫给。語(yǔ)法如下:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
默認(rèn)的行為(不帶session和global)是為下一個(gè)(未開(kāi)始)事務(wù)設(shè)置隔離級(jí)別香拉。如果使用GLOBAL關(guān)鍵字,語(yǔ)句在全局對(duì)新開(kāi)始創(chuàng)建的所有新連接設(shè)置默認(rèn)事務(wù)級(jí)別中狂,需要SUPER權(quán)限凫碌。使用SESSION關(guān)鍵字為將來(lái)在當(dāng)前連接上執(zhí)行的事務(wù)設(shè)置默認(rèn)事務(wù)級(jí)別。 任何客戶(hù)端都能自由改變會(huì)話(huà)隔離級(jí)別胃榕,或者為下一個(gè)事務(wù)設(shè)置隔離級(jí)別盛险。
查詢(xún)?nèi)趾蜁?huì)話(huà)事務(wù)隔離級(jí)別:
SELECT@@global.tx_isolation;
SELECT@@session.tx_isolation;
SELECT@@tx_isolation;
通過(guò)mySQL配置文件修改全局事務(wù)隔離級(jí)別,設(shè)置全局會(huì)話(huà)默認(rèn)事務(wù)隔離級(jí)別勋又。
[mysqld]
xxxxxxx
transaction-isolation=read-committed
重啟mySQL服務(wù)苦掘,生效。
設(shè)置當(dāng)前會(huì)隔離級(jí)別
SETSESSIONTRANSACTIONISOLATIONLEVELREADUNCOMMITTED
SETSESSIONTRANSACTIONISOLATIONLEVELREADCOMMITTED
SETSESSIONTRANSACTIONISOLATIONLEVELREPEATABLEREAD
SETSESSIONTRANSACTIONISOLATIONLEVELSERIALIZABLE
不同會(huì)話(huà)的事務(wù)隔離級(jí)別不同
在會(huì)話(huà)1終端查看當(dāng)前會(huì)話(huà)的事務(wù)隔離級(jí)別
select @@tx_isolation
查詢(xún)結(jié)果為:可重復(fù)讀REPEATABLE-READ
設(shè)置當(dāng)前會(huì)話(huà)事務(wù)隔離級(jí)別為READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
打開(kāi)另一個(gè)SQL Manager終端作為會(huì)話(huà)2,查看當(dāng)前會(huì)話(huà)的事務(wù)隔離級(jí)別
select @@tx_isolation
查詢(xún)結(jié)果為:可重復(fù)讀REPEATABLE-READ
創(chuàng)建一張表挺邀,含ID揉忘、姓名跳座、年齡字段,用于驗(yàn)證不同的事務(wù)隔離級(jí)別泣矛。
CREATETABLEta
(
idINTNOTNULLPRIMARYKEY,
nameVARCHAR(10),
ageINT
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
insertintotavalues(1,'孫悟空',500);
insertintotavalues(2,'唐僧',30);
注:由于本人SQL Manager Lite客戶(hù)端的事務(wù)回滾機(jī)制失效疲眷,以下實(shí)驗(yàn)使用Navicat for MySQL客戶(hù)端。
2您朽、驗(yàn)證READ UNCOMMITTED隔離級(jí)別
打開(kāi)一個(gè)會(huì)話(huà)1狂丝,設(shè)置事務(wù)隔離級(jí)別為READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
打開(kāi)會(huì)話(huà)2,開(kāi)始一個(gè)事務(wù)哗总,更新ID為1的記錄的age為1000几颜。
startTRANSACTION;
updatetasetage=1000whereid=1;
在會(huì)話(huà)1查看ta表中ID為1的信息,age已經(jīng)為1000讯屈。
select * from ta;
會(huì)話(huà)1的事務(wù)隔離級(jí)別允許讀取未提交的數(shù)據(jù)蛋哭。
在會(huì)話(huà)2回滾事務(wù)
ROLLBACK;
會(huì)話(huà)1和會(huì)話(huà)2查詢(xún)ta表中ID為1的記錄,age為500
3涮母、驗(yàn)證READ COMMITTED隔離級(jí)別
打開(kāi)一個(gè)會(huì)話(huà)1谆趾,設(shè)置事務(wù)隔離級(jí)別為READ COMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED
打開(kāi)會(huì)話(huà)2,開(kāi)始一個(gè)事務(wù)叛本,更新ID為1的記錄的age為5000沪蓬。
startTRANSACTION;
updatetasetage=5000whereid=1;
在會(huì)話(huà)1查看ta表中ID為1的信息,age為500来候。
select * from ta;
會(huì)話(huà)1的事務(wù)隔離級(jí)別不允許讀取未提交的數(shù)據(jù)跷叉。
在會(huì)話(huà)2提交事務(wù)
COMMIT;
會(huì)話(huà)1查詢(xún)ta表中ID為1的記錄,age為5000
4营搅、驗(yàn)證REPEATABLE READ隔離級(jí)別
打開(kāi)一個(gè)會(huì)話(huà)1云挟,設(shè)置事務(wù)隔離級(jí)別為REPEATABLE READ
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ
在會(huì)話(huà)1,開(kāi)始一個(gè)事務(wù)剧防,查詢(xún)ID為1的記錄的age為5000植锉。
startTRANSACTION;
SELECT*FROMtawhereid=1;
在會(huì)話(huà)2更新ta表中ID為1的信息,age為1000峭拘。
UPDATE ta SET age=1000 WHERE id=1;
在會(huì)話(huà)2查看ta表中ID為1的信息,age已經(jīng)為1000狮暑。
select * from ta WHERE id=1;
在會(huì)話(huà)1再次查看ta表中ID為1的信息鸡挠,age仍舊為5000。
select * from ta WHERE id=1;
在會(huì)話(huà)1提交事務(wù)
COMMIT;
會(huì)話(huà)1查詢(xún)ta表中ID為1的記錄搬男,age已經(jīng)為1000拣展。
打開(kāi)一個(gè)會(huì)話(huà)1缔逛,設(shè)置事務(wù)隔離級(jí)別為SERIALIZABLE
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE
打開(kāi)會(huì)話(huà)2备埃,開(kāi)始一個(gè)事務(wù)姓惑,更新ID為1的記錄的age為5000。
startTRANSACTION;
updatetasetage=5000whereid=1;
在會(huì)話(huà)1開(kāi)始一個(gè)事務(wù)按脚,查看ta表中ID為1的信息于毙,會(huì)話(huà)1處于等待狀態(tài)。
startTRANSACTION;
select*fromta;
在會(huì)話(huà)2提交事務(wù)后辅搬,
COMMIT;
會(huì)話(huà)1查詢(xún)SQL執(zhí)行完畢唯沮,結(jié)果為5000。
數(shù)據(jù)庫(kù)中的鎖是指一種軟件機(jī)制,用來(lái)控制防止某個(gè)用戶(hù)(進(jìn)程會(huì)話(huà))在已經(jīng)占用了某種數(shù)據(jù)資源時(shí)溶褪,其他用戶(hù)做出影響本用戶(hù)數(shù)據(jù)操作或?qū)е聰?shù)據(jù)非完整性和非一致性問(wèn)題發(fā)生的手段币旧。
按照鎖級(jí)別劃分猿妈,鎖可分為共享鎖佳恬、排他鎖。
A于游、共享鎖(讀鎖)
針對(duì)同一塊數(shù)據(jù)毁葱,多個(gè)讀操作可以同時(shí)進(jìn)行而不會(huì)互相影響。
共享鎖只針對(duì)UPDATE時(shí)候加鎖贰剥,在未對(duì)UPDATE操作提交之前倾剿,其他事務(wù)只能夠獲取最新的記錄但不能夠UPDATE操作。
B蚌成、排他鎖(寫(xiě)鎖)
當(dāng)前寫(xiě)操作沒(méi)有完成前前痘,阻斷其他寫(xiě)鎖和讀鎖。
按鎖的粒度劃分芹缔,鎖可分為表級(jí)鎖、行級(jí)鎖瓶盛、頁(yè)級(jí)鎖最欠。
A、行級(jí)鎖
開(kāi)銷(xiāo)大惩猫,加鎖慢芝硬,會(huì)出現(xiàn)死鎖,鎖定力度最小轧房,發(fā)生鎖沖突的概率最低拌阴,并發(fā)度高。
B奶镶、表級(jí)鎖
開(kāi)銷(xiāo)小迟赃,加鎖快陪拘,不會(huì)出現(xiàn)死鎖,鎖定力度大纤壁,發(fā)生沖突所的概率高左刽,并發(fā)度低。
C摄乒、頁(yè)面鎖
開(kāi)銷(xiāo)和加鎖時(shí)間介于表鎖和行鎖之間悠反,會(huì)出現(xiàn)死鎖,鎖定力度介于表和行行級(jí)鎖之間馍佑,并發(fā)度一般斋否。
MySQL的鎖機(jī)制比較簡(jiǎn)單拭荤,最顯著的特點(diǎn)是不同的存儲(chǔ)引擎支持不同的鎖機(jī)制茵臭。
MyISAM和MEMORY存儲(chǔ)引擎采用表級(jí)鎖。
InnoDB支持行級(jí)鎖舅世、表級(jí)鎖旦委,默認(rèn)情況采用行級(jí)鎖。
MyISAM存儲(chǔ)引擎和InnoDB存儲(chǔ)引擎都支持表級(jí)鎖。
MyISAM存儲(chǔ)引擎支持表級(jí)鎖罢低,為了保證數(shù)據(jù)的一致性查辩,更改數(shù)據(jù)時(shí),防止其他人更改數(shù)據(jù)网持,可以人工添加表級(jí)鎖宜岛。可以使用命令對(duì)數(shù)據(jù)庫(kù)的表枷鎖功舀,使用命令對(duì)數(shù)據(jù)庫(kù)的表解鎖萍倡。
給表加鎖的命令Lock Tables,給表解鎖的命令Unlock Tables
MyISAM引擎在用戶(hù)讀數(shù)據(jù)自動(dòng)加READ鎖辟汰,更改數(shù)據(jù)自動(dòng)加WRITE鎖列敲。使用lock Tables和Unlock Tables顯式加鎖和解鎖。
打開(kāi)會(huì)話(huà)1酿炸,創(chuàng)建表
CREATETABLEtc
(
idINT,
nameVARCHAR(10),
ageINT
)ENGINE=MyISAMDEFAULTCHARSET=utf8;
插入兩條記錄:
insertintotcvalues(1,'孫悟空',500);
insertintotcvalues(3,'豬八戒',100);
對(duì)表加READ鎖
lock tables tc read;
加鎖后只可以查詢(xún)已經(jīng)加鎖的表,
select * from tc;
查詢(xún)沒(méi)有加鎖的表將失敗
select * from ta;
打開(kāi)會(huì)話(huà)2涨冀,對(duì)已經(jīng)加鎖的表進(jìn)行查詢(xún),成功麦萤。
select * from tc;
對(duì)加鎖的表tc進(jìn)行更新操作鹿鳖,將失敗
update tc set age=100 where id=1;
會(huì)話(huà)1中使用LOCK TABLE命令給表加了讀鎖扁眯,會(huì)話(huà)1可以查詢(xún)鎖定表中的記錄,但更新或訪問(wèn)其他表都會(huì)提示錯(cuò)誤翅帜;會(huì)話(huà)2可以查詢(xún)表中的記錄姻檀,但更新就會(huì)出現(xiàn)鎖等待。
在會(huì)話(huà)1對(duì)表進(jìn)行解鎖涝滴,會(huì)話(huà)2的更新操作成功绣版。
unlock tables;
在會(huì)話(huà)1,再次鎖定表tc歼疮,后面帶local參數(shù)杂抽。
lock tables tc read local;
Local參數(shù)允許在表尾并發(fā)插入,只鎖定表中當(dāng)前記錄韩脏,其他會(huì)話(huà)可以插入新的記錄
在會(huì)話(huà)2插入一條記錄
insert into tc values(2, '唐僧', 20);
在會(huì)話(huà)1查看tc表的記錄缩麸,無(wú)插入記錄
select * from tc;
READ鎖是共享鎖赡矢,不影響其他會(huì)話(huà)的讀取杭朱,但不能更新已經(jīng)加READ鎖的數(shù)據(jù)。MyISAM表的讀寫(xiě)是串行的吹散,但是總體而言的弧械,在一定條件下,MyISAM表也支持查詢(xún)和插入操作的并發(fā)進(jìn)行空民。
MyISAM存儲(chǔ)引擎有一個(gè)系統(tǒng)變量concurrent_insert刃唐,用以控制其并發(fā)插入的行為,其值分別可以為0袭景、1或2唁桩。
0:不允許并發(fā)操作
1:如果MyISAM表中沒(méi)有空洞(即表的中間沒(méi)有被刪除的行),MyISAM允許在一個(gè)進(jìn)程讀表的同時(shí)耸棒,另一個(gè)進(jìn)程從表尾插入記錄荒澡,是MySQL的默認(rèn)設(shè)置。
2:無(wú)論MyISAM表中有沒(méi)有空洞与殃,都允許在表尾并發(fā)插入記錄单山。
在MySQL配置文件添加,concurrent_insert=2幅疼,重啟mySQL服務(wù)設(shè)置生效米奸。
設(shè)置concurrent_insert為0
在會(huì)話(huà)1對(duì)表tc加鎖
lock tables tc read local;
在會(huì)話(huà)2插入一條記錄爽篷,此時(shí)tc表被鎖定悴晰,進(jìn)入等待
insert into tc values(4, '沙悟凈', 30);
在會(huì)話(huà)1解鎖表tc,此時(shí)會(huì)話(huà)2插入成功
unlock tables;
設(shè)置concurrent_insert為1
在會(huì)話(huà)1刪除ID為3的記錄
delete from tc where id=3;
在會(huì)話(huà)1對(duì)表tc加鎖
lock tables tc read local;
在會(huì)話(huà)2插入一條記錄,此時(shí)tc表被鎖定铡溪,并且表中有空洞漂辐,進(jìn)入等待
insert into tc values(5, '白骨精', 1000);
在會(huì)話(huà)1解鎖表tc,此時(shí)會(huì)話(huà)2插入成功棕硫,此時(shí)表中已經(jīng)沒(méi)有空洞
unlock tables;
在會(huì)話(huà)1對(duì)表tc加鎖
lock tables tc read local;
在會(huì)話(huà)2插入一條記錄髓涯,插入成功,支持有條件并發(fā)插入
insert into tc values(6, '白骨精', 1000);
在會(huì)話(huà)1解鎖表tc
unlock tables;
設(shè)置concurrent_insert為2
在會(huì)話(huà)1刪除ID為5的記錄哈扮,創(chuàng)造一個(gè)空洞
delete from tc where id=5;
在會(huì)話(huà)1對(duì)表tc加鎖
lock tables tc read local;
在會(huì)話(huà)2插入一條記錄纬纪,插入成功,支持無(wú)條件并發(fā)插入
insert into tc values(7, '蜘蛛精', 1000);
在會(huì)話(huà)1解鎖表tc
unlock tables;
添加表級(jí)寫(xiě)鎖語(yǔ)法如下:
LOCK TABLES tablename WRITE;
不允許其他會(huì)話(huà)查詢(xún)包各、修改、插入記錄赦邻。
InnoDB存儲(chǔ)引擎實(shí)現(xiàn)的是基于多版本的并發(fā)控制協(xié)議——MVCC (Multi-Version Concurrency Control)惶洲。MVCC的優(yōu)點(diǎn)是讀不加鎖按声,讀寫(xiě)不沖突。在讀多寫(xiě)少的OLTP應(yīng)用中恬吕,讀寫(xiě)不沖突是非常重要的签则,極大的增加了系統(tǒng)的并發(fā)性能。
在MVCC并發(fā)控制中铐料,讀操作可以分成兩類(lèi):快照讀 (snapshot read)與當(dāng)前讀 (current read)渐裂。
快照讀,讀取的是記錄的可見(jiàn)版本 (有可能是歷史版本)钠惩,不用加鎖柒凉。
當(dāng)前讀,讀取的是記錄的最新版本篓跛,并且當(dāng)前讀返回的記錄都會(huì)加上鎖膝捞,保證其他事務(wù)不會(huì)再并發(fā)修改。事務(wù)加鎖愧沟,是針對(duì)所操作的行蔬咬,對(duì)其他行不進(jìn)行加鎖處理。
快照讀:簡(jiǎn)單的SELECT操作沐寺,屬于快照讀林艘,不加鎖。
select * from table where ?;
當(dāng)前讀:特殊的讀操作混坞,INSERT/UPDATE/DELETE狐援,屬于當(dāng)前讀,需要加鎖。
select*fromtablewhere?lockinsharemode;
select*fromtablewhere?forupdate;
insertintotablevalues(…);
updatetableset?where?;
deletefromtablewhere?;
以上SQL語(yǔ)句屬于當(dāng)前讀咕村,讀取記錄的最新版本场钉。并且蚊俺,讀取之后懈涛,還需要保證其他并發(fā)事務(wù)不能修改當(dāng)前記錄,對(duì)讀取記錄加鎖泳猬。其中批钠,除了第一條語(yǔ)句,對(duì)讀取記錄加S鎖 (共享鎖)外得封,其他的操作埋心,都加的是X鎖 (排它鎖)。
打開(kāi)會(huì)話(huà)1拷呆,創(chuàng)建一個(gè)表,含ID疫粥、姓名茬斧、年齡
CREATETABLEtd
(
idINT,
nameVARCHAR(10),
ageINT
)ENGINE=innoDBDEFAULTCHARSET=utf8;
在插入兩條記錄
insertintotdvalues(1,'孫悟空',500);
insertintotdvalues(2,'豬八戒',100);
在會(huì)話(huà)1開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)1查詢(xún)ID位1的記錄信息
select * from td where id =1;
打開(kāi)會(huì)話(huà)2,更新ID為1的age為1000
update td set age=1000 where id=1;
在會(huì)話(huà)2查看ID為1的age已經(jīng)更新為1000梗逮。
select * from td where id =1;
在會(huì)話(huà)1查看ID為1的age项秉,仍然為500。
select * from td where id =1;
在會(huì)話(huà)1提交事務(wù)
COMMIT;
在會(huì)話(huà)1查看ID為1的age慷彤,已經(jīng)為1000娄蔼。
在會(huì)話(huà)1開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)1給select語(yǔ)句添加共享鎖底哗。
select * from td where id=1 lock in share mode;
在會(huì)話(huà)2岁诉,更新ID為1的age的值為100,進(jìn)入鎖等待
update td set age=100 where id=1;
在會(huì)話(huà)1提交事務(wù)
COMMIT;
會(huì)話(huà)2的更新操作成功跋选。
在會(huì)話(huà)1開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)1更新ID為1的age的值為500。
update td set age=500 where id=1;
在會(huì)話(huà)2開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)2更新ID為2的age的值為1000野建,此時(shí)進(jìn)入鎖等待
update td set age=1000 where id=2;
td表沒(méi)有指定主鍵属划,事務(wù)不支持行級(jí)鎖。會(huì)話(huà)1的事務(wù)給整張表加了鎖候生。
在會(huì)話(huà)1提交事務(wù)同眯,此時(shí)會(huì)話(huà)2的修改成功
COMMIT;
在會(huì)話(huà)2提交事務(wù),解除對(duì)表的鎖定
COMMIT;
在會(huì)話(huà)1唯鸭,給表的ID增加主鍵
alter table td add primary key(id);
在會(huì)話(huà)1開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)1更新ID為1的age的值為5000
update td set age=5000 where id=1;
在會(huì)話(huà)2上開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)2上修改ID為2的get的值為10000须蜗,更新成功,說(shuō)明會(huì)話(huà)1只鎖定了ID為1的行。
update td set age=10000 where id=2;
在會(huì)話(huà)2上更新ID是1的age值為100明肮,出現(xiàn)等待菱农。因?yàn)闀?huì)話(huà)1給ID為1的行添加了獨(dú)占鎖。
update td set age=5000 where id=1;
在會(huì)話(huà)1提交事務(wù)
COMMIT;
在會(huì)話(huà)2提交事務(wù)
COMMIT;
在會(huì)話(huà)1查詢(xún)柿估,會(huì)話(huà)1和會(huì)話(huà)2對(duì)age列的修改都生效
select * from td;
A事務(wù)添加共享鎖后,B事務(wù)也可以添加共享鎖秫舌。A事務(wù)UPDATE鎖定記錄的妖,處于等待中,于此同時(shí)B事務(wù)也UPDATE更新鎖定的記錄足陨,就產(chǎn)生死鎖嫂粟。
在會(huì)話(huà)1開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)1查詢(xún)ID是1的記錄,并添加共享鎖墨缘。
select * from td where id=1 lock in share mode;
在會(huì)話(huà)2開(kāi)始事務(wù)
start transaction;
在會(huì)話(huà)2查詢(xún)ID是1的記錄星虹,并添加共享鎖。
select * from td where id=1 lock in share mode;
在會(huì)話(huà)1更新ID為1的age值為镊讼,等待會(huì)話(huà)2釋放共享鎖
update td set age=200 where id=1;
在會(huì)話(huà)2更新ID為1的age為宽涌,會(huì)話(huà)2發(fā)現(xiàn)死鎖,回滾事務(wù)狠毯。
update td set age=200 where id=1;
在會(huì)話(huà)1提交事務(wù)
COMMIT;
事務(wù)提交還是回滾,可以在事務(wù)結(jié)束處判斷是否出現(xiàn)錯(cuò)誤嚼松,如果出現(xiàn)嫡良,回滾。如果沒(méi)有錯(cuò)誤献酗,提交事務(wù)寝受。
使用自定義條件來(lái)決定事務(wù)是提交還是回滾。
在存儲(chǔ)過(guò)程中使用事務(wù)很澄,在事務(wù)的末尾判斷是否有錯(cuò)誤,插入失敗颜及,則回滾事務(wù)甩苛。
創(chuàng)建兩張表,存儲(chǔ)ID俏站、姓名讯蒲、年齡,創(chuàng)建存儲(chǔ)過(guò)程將A表的指定ID的記錄轉(zhuǎn)移到B表肄扎。
CREATETABLEta
(
idINTNOTNULLPRIMARYKEY,
nameVARCHAR(10),
ageINT
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
insertintotavalues(1,'孫悟空',500);
insertintotavalues(2,'唐僧',30);
CREATETABLEtb
(
idINTNOTNULLPRIMARYKEY,
nameVARCHAR(10),
ageINT
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
insertintotbvalues(1,'孫悟空',500);
insertintotbvalues(3,'豬八戒',100);
CREATEPROCEDUREmove(numINT)
BEGIN
DECLAREerrorinfoINTDEFAULT0;
DECLARECONTINUEHANDLERFORSQLEXCEPTIONSETerrorinfo=1;
STARTTRANSACTION;
INSERTINTOtbSELECT*FROMtaWHEREid=num;
DELETEFROMtaWHEREid=num;
IF errorinfo=1
THENROLLBACK;
ELSE
COMMIT;
ENDIF;
END
將ID為2的記錄從A表轉(zhuǎn)移到B表
call move(2);
創(chuàng)建兩個(gè)表赁酝,每個(gè)表含賬戶(hù)、姓名旭等、余額信息酌呆,創(chuàng)建一個(gè)存儲(chǔ)過(guò)程,從A表中的一個(gè)賬戶(hù)轉(zhuǎn)賬一定金額到B表的一個(gè)賬戶(hù)搔耕,如果轉(zhuǎn)出賬戶(hù)的余額不足隙袁,則回滾,否則提交度迂。
create table accountA
(
account INT PRIMARYKEYNOTNULL,
name VARCHAR(10),
balanceDOUBLE
)ENGINE=innoDBdefaultCHARSET=utf8;
insertintoaccountA VALUES(1,'孫悟空', 10000);
insertintoaccountA VALUES(2,'唐僧', 20000);
create table accountB
(
account INT PRIMARYKEYNOTNULL,
name VARCHAR(10),
balanceDOUBLE
)ENGINE=innoDBdefaultCHARSET=utf8;
insertintoaccountB VALUES(1,'孫悟空', 10000);
insertintoaccountB VALUES(2,'唐僧', 20000);
CREATE PROCEDURE transfer(fromaccout INT,toaccount INT, numDOUBLE)
BEGIN
DECLAREmDOUBLE;
START TRANSACTION;
UPDATE accountBSETbalance=balance + numWHEREaccount=toaccount;
UPDATE accountASETbalance=balance - numWHEREaccount=fromaccout;
SELECTbalanceINTOmfromaccountAWHEREaccount=fromaccout;
IFm <0
THENROLLBACK;
ELSE
? COMMIT;
ENDIF;
END
從A表的賬戶(hù)2轉(zhuǎn)出25000元到B表的賬戶(hù)2藤乙。
call transfer(2,2,25000);
此時(shí)A表的余額不足,回滾