聲明測試表,供文章案例使用
CREATE TABLE `cs` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`num` int(10) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
事務(wù)的分類
- 顯示事務(wù):
- read write:讀寫事務(wù),默認模式,表示當(dāng)前事務(wù)可以讀寫數(shù)據(jù)。
- read only:只讀事務(wù),很少用,表示當(dāng)前事務(wù)不能修改數(shù)據(jù)。
- with consistent snapshot:一致性快照,在數(shù)據(jù)庫事務(wù)中確保事務(wù)在執(zhí)行過程中能看到一個事務(wù)開始時的一致數(shù)據(jù)庫狀態(tài),避免被其他并發(fā)操作影響。
- 隱式事務(wù):不需要顯示聲明事務(wù)相關(guān)語句,autocommit是開啟狀態(tài)(默認值),每條DML和DDL的SQL語句都是一個獨立的事務(wù)。
MySQL事務(wù)的4個特性:
- 原子性(Atomicity):當(dāng)前事務(wù)中的執(zhí)行結(jié)果,要么全部執(zhí)行成功,要么全部執(zhí)行失敗。
- 一致性(Consistency):事務(wù)執(zhí)行前后,數(shù)據(jù)庫從一個合法(指符合業(yè)務(wù)預(yù)期)狀態(tài)轉(zhuǎn)換成另一個一合法狀態(tài)。
- 隔離性(Isolation):多個事務(wù)可以并發(fā)執(zhí)行,各個事務(wù)之間的操作互相隔離互不干擾。
- 持久性(Durability):無論事務(wù)提交還是回滾,都會持久化到磁盤中。
自動提交
自動提交(auto commit),指的是SQL語句執(zhí)行完畢后自動將數(shù)據(jù)持久化到磁盤(刷盤)中。
顯式的聲明事務(wù),或者聲明set autocommit = 0;
都可以關(guān)閉自動提交。
savepoint
- 俗稱保存點,是用于實現(xiàn)部分事務(wù)回滾的一種機制,需要確定從哪里開始回滾,就需要savepoint的標(biāo)識來定位。
- 回滾范圍:從保存點開始到事務(wù)最后一條SQL,都會被回滾。
- 適用場景:用于復(fù)雜的業(yè)務(wù)邏輯中,給出靈活可控的后悔藥,降低事務(wù)回滾影響范圍。
- 注意:rollback to之后,不代表事務(wù)流程走完,還需要再次commit提交其它未回滾的事務(wù)。
- 用法:
savepoint 保存點名:創(chuàng)建一個 Savepoint,并為其指定一個名稱。
rollbackto savepoint 保存點名;:將事務(wù)回滾到指定的 Savepoint。
release savepoint 保存點名;:釋放指定的 Savepoint。
示例:
start transaction;
insert into cs(num) values(1);
savepoint insert_1;
insert into cs(num) values(2);
savepoint insert_2;
insert into cs(num) values(3);
savepoint insert_3;
insert into cs(num) values(4);
savepoint insert_4;
rollback to insert_2;
commit;
發(fā)現(xiàn)1,2數(shù)據(jù)被插入。
事務(wù)的隱式提交
在上一個事務(wù)沒提交或回滾時,運行下一個事務(wù),則上一個事務(wù)自動提交。
start transaction;
insert into cs(num) values(1);
insert into cs(num) values(2);
start transaction;
insert into cs(num) values(3);
insert into cs(num) values(4);
commit;
成功插入1,2,3,4。
4種隔離級別
- 讀未提交(Read Uncommitted):最低級別的隔離,事務(wù)中的修改即使未提交也能被其他事務(wù)看到,可能導(dǎo)致臟讀、不可重復(fù)讀和幻讀問題。
- 讀已提交(Read Committed):保證一個事務(wù)提交后對其他事務(wù)可見,避免了臟讀,但可能會導(dǎo)致不可重復(fù)讀和幻讀問題。
- 可重復(fù)讀(Repeatable Read):保證在同一事務(wù)內(nèi)多次讀取數(shù)據(jù)時,數(shù)據(jù)保持一致,避免了不可重復(fù)讀問題,但仍可能出現(xiàn)幻讀。
- 串行化(Serializable):最高級別的隔離,通過對讀取的數(shù)據(jù)添加共享鎖或排他鎖來確保事務(wù)之間的隔離性,避免了臟讀、不可重復(fù)讀和幻讀問題,但可能會影響并發(fā)性能。
表格從上到下,越來越高可用,但是性能越來越低。
隔離級別 | 是否解決臟讀 | 是否解決不可重復(fù)讀 | 是否解決幻讀 | 是否加鎖 |
---|---|---|---|---|
讀未提交 | 否 | 否 | 否 | 否 |
讀已提交 | 是 | 否 | 否 | 否 |
可重復(fù)讀 | 是 | 是 | 否 | 否 |
串行化 | 是 | 是 | 是 | 是 |
查看或設(shè)置MySQL隔離級別
- 查看:
select @@transaction_isolation;
或者show variables like 'transaction_isolation;'
- 設(shè)置:
set session transaction_isolation = 'read-uncommitted/read-committed/repeatable-read/serializable'
;
注意隔離級別是回話級別的,所以無法set glboal。
MySQL會發(fā)生什么讀?
因為mysql默認隔離級別是可重復(fù)讀(Repeatable Read),所以只會發(fā)生幻讀情況,臟讀和可重復(fù)度不會發(fā)生,除非改事務(wù)隔離級別。
臟寫(不允許發(fā)生)
- 簡介:一個事務(wù)修改某些數(shù)據(jù)時,另一個事務(wù)在未提交的情況下也修改了這些數(shù)據(jù),引起的導(dǎo)致數(shù)據(jù)的不一致性。
- 危害:造成數(shù)據(jù)在并發(fā)情況下嚴(yán)重不一致。
- 演示:試不出來,臟寫這么嚴(yán)重的bug,是不允許發(fā)生的情況。
臟讀(讀未提交隔離級別會發(fā)生)
- 簡介:一個事務(wù)尚未commit(提交,刷盤,持久化),卻讀取了事務(wù)修改后的值,引起數(shù)據(jù)讀取不準(zhǔn)確的情況。
- 危害:事務(wù)還未提交就被讀取了,該事務(wù)成功提交還好,要是回滾了,會造成讀取數(shù)據(jù)不一致的問題。
- 演示:因為臟讀是讀未提交(Read Uncommitted)才會發(fā)生的情況,所以要降低MySQL的隔離級別。
步驟 | 會話A | 會話B | 備注 |
---|---|---|---|
1 | set session transaction_isolation = 'read-committed'; | set session transaction_isolation = 'read-committed'; | 設(shè)置事務(wù)的隔離級別為讀未提交 |
2 | select @@transaction_isolation; | select @@transaction_isolation; | 檢查隔離級別是否設(shè)置成功 |
3 | select num from cs where id = 20; #20 | start transaction; update cs set num = 20 where id = 40; |
會話A num的初始值為20 |
4 | select num from cs where id = 20; #40 | / | 會話B并未commit,此時會話A中num的值為40,發(fā)生臟讀現(xiàn)象 |
5 | / | rollback | 結(jié)束本次事務(wù) |
6 | select num from cs where id = 20; #20 | / | num恢復(fù)為20 |
不可重復(fù)讀(讀未提交、讀已提交隔離級別會發(fā)生)
- 簡介:在事務(wù)A中讀取某些數(shù)據(jù),然后在事務(wù)B中修改這些數(shù)據(jù),此時事務(wù)A讀取這些數(shù)據(jù)還未發(fā)生變化,但是事務(wù)B提交后,并在事務(wù)A在未結(jié)束事務(wù)的前提下,那些數(shù)據(jù)發(fā)生了變化,不可重復(fù)讀不是禁止讀動作,而是重復(fù)讀數(shù)據(jù)不一致。
一句話概括,在同一個事務(wù)中,受其它事務(wù)提交的影響,讀取同一數(shù)據(jù)兩次得到的結(jié)果不一致的現(xiàn)象。 - 危害:破壞了事務(wù)內(nèi)數(shù)據(jù)的準(zhǔn)確性,例如事務(wù)內(nèi)的SQL有自增自減的邏輯,如果事務(wù)內(nèi)的初始值受其他事物提交從而發(fā)生變化,那么這是個巨大的問題。
- 演示:因為不可重復(fù)讀是讀已提交(Read Committed)才會發(fā)生的情況,所以要降低MySQL的隔離級別。
步驟 | 會話A | 會話B | 備注 |
---|---|---|---|
1 | set session transaction_isolation = 'read-uncommitted'; | set session transaction_isolation = 'read-uncommitted'; | 設(shè)置事務(wù)隔離級別為讀已經(jīng)提交 |
2 | select @@transaction_isolation; | select @@transaction_isolation; | 檢查隔離級別是否設(shè)置成功 |
3 | start transaction; | start transaction; | 雙方開啟事務(wù) |
4 | select num from cs where id = 20; #20 | select num from cs where id = 20; #20 | 兩個會話中num的值為20 |
5 | update cs set num = 40 where id = 20; | select num from cs where id = 20; #20 | 會話A將數(shù)據(jù)更新為40,此時會話B查詢的值仍為20 |
6 | commit | select num from cs where id = 40; #40 | 會話A提交事務(wù),會話B仍在事務(wù)中,但是得到的值變成了40,發(fā)生了不可重復(fù)讀 |
7 | / | commit | 結(jié)束事務(wù) |
幻讀(讀未提交、讀已提交、可重復(fù)讀隔離級別會發(fā)生)
- 簡介:同一個事務(wù)里前后查詢兩次相同范圍的數(shù)據(jù),后一次查詢查詢到了前一次看不到的東西,就好像出現(xiàn)了"幻影"一樣。(注意,如果把會話B的insert改為delete導(dǎo)致的數(shù)據(jù)減少,不算幻讀,算不可重復(fù)讀)。
- 危害:沒有充分的做好數(shù)據(jù)隔離,數(shù)據(jù)一致性存在問題。
- 演示:mysql 的默認隔離級別為REPEATABLE-READ,所以大概率不用調(diào)整隔離級別。
步驟 | 會話A | 會話B | 備注 |
---|---|---|---|
1 | select @@transaction_isolation; | select @@transaction_isolation; | 檢查隔離級別是否是REPEATABLE-READ |
2 | start transaction; | start transaction; | 雙方開啟事務(wù) |
3 | select * from cs; | select * from cs; | 兩個事務(wù)查看,都只有id為20的一條數(shù)據(jù) |
4 | insert into cs (id,num) values(21,21); | select * from cs; | 會話B查詢,仍舊只有id為20的一條數(shù)據(jù) |
5 | commit | / | 會話A提交事務(wù) |
6 | / | select * from cs; | 即使會話A提交了事務(wù),會話B查詢?nèi)耘f無法搜索到會話A插入的數(shù)據(jù),起始這一步已經(jīng)幻讀了,但是mysql不表明是幻讀,所以到第7步測試 |
7 | / | insert into cs (id,num) values(21,21); | 因為會話B select查不到id為21的數(shù)據(jù),所以插入id相同的數(shù)據(jù),但是報錯1062 - Duplicate entry '21' for key 'PRIMARY' |
8 | / | rollback; | 回滾以結(jié)束事務(wù)流程 |
如何解決幻讀?
- 或者使用串行化的隔離級別。在串行化隔離級別下,也會隱式的添加行(X)鎖。
- 添加間隙鎖,可以避免幻讀。
- mysql 的默認隔離級別為REPEATABLE-READ,又稱為RR,通過MVCC的機制,如果對數(shù)據(jù)進行快照讀,正因為讀取的不一定第最新的數(shù)據(jù),所以可以防止幻讀(注意不是解決幻讀),如果是當(dāng)前讀(最近數(shù)據(jù)),那么仍舊會發(fā)生幻讀現(xiàn)象。
當(dāng)前讀
當(dāng)前讀讀的就是數(shù)據(jù)最新的記錄,需要保證當(dāng)前讀的數(shù)據(jù)不能被修改,修改了就不是最新的記錄了(臟寫),因此需要加鎖,select for update、select lock in share mode以及DML(insert、update、delete)獲取的數(shù)據(jù)都是當(dāng)前讀的數(shù)據(jù)。
快照讀
快照讀顧名思義,讀取的就是由MVCC Read View控制的undo log的數(shù)據(jù),不加鎖,所以是讀取是非阻塞的。不加鎖的select都屬于快照讀。如果當(dāng)前事務(wù)的隔離級別是串行化,那么快照讀也變成了當(dāng)前讀。
舉個例子:常用的navicat,查看一個表,事務(wù)提交前的insert或update語句,表格內(nèi)仍舊顯示的原數(shù)據(jù),則用的快照讀。
MVCC
MVCC(Multi-Version Concurrency Control)是 MySQL 中一種實現(xiàn)事務(wù)隔離的機制,用于處理數(shù)據(jù)庫事務(wù)并發(fā)訪問時可能出現(xiàn)的讀寫沖突。事務(wù)的四種隔離級別,就是通過MVCC機制提供的底層支撐。
MVCC三板斧:隱藏字段、Undo log(存放歷史版本)、Read view(版本控制)
MVCC解決的是讀已提交和可重復(fù)讀級別的并發(fā)控制。
因為讀未提交,就算事務(wù)未提交,可以直接讀取最新的數(shù)據(jù)(臟讀),相當(dāng)于當(dāng)前讀,那就不分快照讀和當(dāng)前讀了。
串行化的隔離級別,強制事務(wù)串行執(zhí)行,也不存在快照讀和當(dāng)前讀的區(qū)分,因為讀取的都是事務(wù)執(zhí)行過后的最新數(shù)據(jù)。文章來源:http://www.zghlxwxcb.cn/news/detail-838026.html
事務(wù)各項指標(biāo)監(jiān)控
查看 InnoDB 存儲引擎中當(dāng)前活動的事務(wù)信息。文章來源地址http://www.zghlxwxcb.cn/news/detail-838026.html
SELECT * FROM information_schema.innodb_trx;
trx_id 事務(wù)的唯一標(biāo)識符。
trx_state 事務(wù)的狀態(tài),如 RUNNING、LOCK WAIT、ROLLING BACK 等。
trx_started 事務(wù)啟動的時間。
trx_requested_lock_id 請求的鎖的標(biāo)識符。
trx_wait_started 等待鎖的開始時間。
trx_weight 事務(wù)的權(quán)重,用于死鎖檢測。
trx_mysql_thread_id MySQL 線程 ID。
trx_query 與事務(wù)相關(guān)的 SQL 查詢語句。
trx_operation_state 事務(wù)內(nèi)部操作的狀態(tài)。
trx_tables_in_use 事務(wù)使用的表的數(shù)量。
trx_tables_locked 事務(wù)鎖定的表的數(shù)量。
trx_lock_structs 事務(wù)內(nèi)部使用的鎖結(jié)構(gòu)數(shù)量。
trx_lock_memory_bytes 用于事務(wù)鎖定的內(nèi)存字節(jié)數(shù)。
trx_rows_locked 事務(wù)鎖定的行數(shù)。
trx_rows_modified 事務(wù)修改的行數(shù)。
trx_concurrency_tickets 用于事務(wù)并發(fā)控制的票數(shù)。
trx_isolation_level 事務(wù)的隔離級別。
trx_unique_checks 是否啟用了唯一性檢查。
trx_foreign_key_checks 是否啟用了外鍵約束檢查。
trx_last_foreign_key_error 最后一個外鍵錯誤信息。
trx_adaptive_hash_latched 是否適應(yīng)性哈希被鎖定。
trx_adaptive_hash_timeout 適應(yīng)性哈希鎖定超時次數(shù)。
trx_is_foreign_key_with_check 是否用于外鍵約束檢查。
trx_is_foreign_key 是否用于外鍵約束。
到了這里,關(guān)于MySQL事務(wù)(4種事務(wù)隔離級別、臟寫、臟讀、不可重復(fù)讀、幻讀、當(dāng)前讀、快照讀、MVCC、事務(wù)指標(biāo)監(jiān)控)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!