引擎層實(shí)現(xiàn)事務(wù)鳞仙。MySQL 支持多引擎寇蚊,MyISAM 不支持事務(wù), InnoDB 支持棍好。
一仗岸、隔離性(Isolation)與隔離級(jí)別
隔離級(jí)別解決:多事務(wù)同時(shí)執(zhí)行,臟讀梳玫、不可重復(fù)讀(non-repeatable read)爹梁、幻讀(phantom read)的問題
四種隔離級(jí)別http://www.reibang.com/p/6ed305b67c8c
1、讀未提交提澎,事務(wù)沒提交時(shí)姚垃,變更被看到。
2盼忌、讀提交积糯,事務(wù)提交,變更才被看到谦纱。
3看成、可重復(fù)讀,事務(wù)執(zhí)行過程中跨嘉,啟動(dòng)時(shí)看到數(shù)據(jù)是一致川慌。未提交變更對(duì)其他事務(wù)也是不可見。
4、串行化梦重,對(duì)同一行記錄兑燥,“寫”加“寫鎖”,“讀”加“讀鎖”琴拧。沖突時(shí)降瞳,等前一個(gè)完成,才可繼續(xù)蚓胸。
表 T 中只有一列挣饥,其中一行的值為 1,按照時(shí)間順序執(zhí)行兩個(gè)事務(wù)的行為沛膳。
mysql> create? table T(c int) engine=InnoDB;? ? insert into T(c)? values(1);
讀未提交”扔枫, V1 = 2。 B 雖沒提交于置, A 看到了茧吊。V2、V3 = 2八毯。
“讀提交”搓侄,V1 = 1,V2 = 2话速。B 更新提交后, A 看到讶踪。 V3 = 2。
“可重復(fù)讀”泊交,V1乳讥、V2 = 1,V3 = 2廓俭。
“串行化”云石,V1、V2 = 1研乒,V3 = 2汹忠。
db里創(chuàng)建視圖,訪問時(shí)候以邏輯結(jié)果為準(zhǔn)雹熬。
? ? “讀未提交”返回記錄上最新值宽菜,沒有視圖概念;
? ?“讀提交” SQL 語句執(zhí)行時(shí)創(chuàng)建竿报。
? ??“可重復(fù)讀”啟動(dòng)時(shí)創(chuàng)建(可認(rèn)為是靜態(tài)铅乡,不受其他事務(wù)更新影響),整個(gè)事務(wù)存在期間都用烈菌。阵幸。
? ? “串行化”加鎖避免并行
Oracle 默認(rèn)“讀提交”花履,從 Oracle 遷移到MySQL 的應(yīng)用,為保證一致挚赊, MySQL設(shè)置為“讀提交”臭挽。
配置的方式,啟動(dòng)參數(shù)transaction-isolation 設(shè)置 READ-COMMITTED
mysql> show? variables like 'transaction_isolation';
| Variable_name |? Value |
|? transaction_isolation | READ-COMMITTED |
“可重復(fù)讀”場(chǎng)景呢:
銀行月底對(duì)賬咬腕,判斷上個(gè)月和當(dāng)前差額,與本月賬單明細(xì)是否一致葬荷。過程中涨共,新交易,不影響結(jié)果宠漩。
二举反、事務(wù)隔離的實(shí)現(xiàn)
展開說明“可重復(fù)讀”。MySQL 更新時(shí)記錄回滾操作扒吁。
從 1 被按順序改成 2火鼻、3、4雕崩,回滾日志:
當(dāng)前值是 4魁索,查詢有不同 read-view。同一記錄在系統(tǒng)中可多版本(MVCC)盼铁。read-view A得到 1粗蔚,依次執(zhí)行所有的回滾
即將 4 改成 5,跟A饶火、B鹏控、C 會(huì)沖突
系統(tǒng)判斷,沒有事務(wù)用回滾日志時(shí)(例:沒有比這個(gè)回滾日志更早 read-view)肤寝、刪除当辐。
盡量不要使用長(zhǎng)事務(wù)
提交之前,回滾記錄保留鲤看,占大量存儲(chǔ)空間缘揪、鎖資源,可能拖垮整個(gè)庫(kù)
在 MySQL 5.5 及以前的版本刨摩,回滾日志是跟數(shù)據(jù)字典一起放在ibdata文件里的寺晌,即使長(zhǎng)事務(wù)最終提交,回滾段被清理澡刹,文件也不會(huì)變小呻征。我見過數(shù)據(jù)只有 20GB,而回滾段有 200GB 的庫(kù)罢浇。最終只好為了清理回滾段陆赋,重建整個(gè)庫(kù)沐祷。
三、事務(wù)的啟動(dòng)方式
MySQL 的事務(wù)啟動(dòng)方式有以下幾種:
1.? 顯式啟動(dòng)事務(wù)語句攒岛, begin 或 start transaction赖临。配套的提交語句是 commit,回滾語句是 rollback灾锯。
2.? set auto commit=0兢榨,線程自動(dòng)提交關(guān)掉。執(zhí)行select 語句顺饮,事務(wù)啟動(dòng)不會(huì)提交吵聪。直到 commit 或 rollback或斷開連接。
如果是長(zhǎng)連接兼雄,導(dǎo)致意外長(zhǎng)事務(wù)吟逝。建議 set auto commit=1,顯式啟動(dòng)事務(wù)。
auto commit 為 1 情況赦肋,begin 顯式啟動(dòng)块攒,commit 。如果執(zhí)行 commit work and chain佃乘,提交事務(wù)并自動(dòng)啟動(dòng)下一個(gè)事務(wù)囱井,省去再次begin開銷。知道每個(gè)語句是否處于事務(wù)中恕稠。
查找持續(xù)時(shí)間超過 60s 的事務(wù):
select * from? information_schema.innodb_trx where? TIME_TO_SEC (time diff(now(),trx_started) )>60
問題
長(zhǎng)事務(wù)風(fēng)險(xiǎn)琅绅,如何避免
應(yīng)用開發(fā)端來看:
1.? 是否set autocommit=0《煳。可在測(cè)試環(huán)境中千扶,把 MySQL 的 general_log 開起來,然后隨便跑一個(gè)業(yè)務(wù)邏輯骆捧,通過 general_log 的日志來確認(rèn)澎羞。改成 1。
2.? 是否有不必要只讀事務(wù)敛苇。有些框架不管什么先用 begin/commit框起來妆绞。我見過有些是業(yè)務(wù)并沒有這個(gè)需要,但是也把好幾個(gè) select 語句放到了事務(wù)中枫攀。這種只讀事務(wù)可以去掉括饶。
3.? 業(yè)務(wù)連接數(shù)據(jù)庫(kù)的時(shí)候,根據(jù)業(yè)務(wù)本身的預(yù)估来涨,通過 SET MAX_EXECUTION_TIME 命令图焰,來控制每個(gè)語句執(zhí)行的最長(zhǎng)時(shí)間,避免單個(gè)語句意外執(zhí)行太長(zhǎng)時(shí)間蹦掐。(為什么會(huì)意外技羔?在后續(xù)的文章中會(huì)提到這類案例)
數(shù)據(jù)庫(kù)端來看:
1.? 監(jiān)控information_schema.Innodb_trx 表僵闯,設(shè)置長(zhǎng)事務(wù)閾值,超過就報(bào)警 / 或者 kill藤滥;
2.? Percona 的 pt-kill 這個(gè)工具不錯(cuò)
3. 在業(yè)務(wù)功能測(cè)試階段要求輸出所有的 general_log鳖粟,分析日志行為提前發(fā)現(xiàn)問題;
4. 如果使用的是 MySQL 5.6 或者更新版本拙绊,把innodb_undo_tablespaces 設(shè)置成 2(或更大的值)向图。如果真的出現(xiàn)大事務(wù)導(dǎo)致回滾段過大,這樣設(shè)置后清理更方便标沪。
評(píng)論1
SET GLOBAL MAX_EXECUTION_TIME=3000. 確保單條語句執(zhí)行時(shí)間