淺析數(shù)據(jù)庫事務(wù)隔離級別

源?/?? ? ? ??文/?王小伍
數(shù)據(jù)庫的事務(wù)隔離級別共分為4種,級別越高性能越差,合理的設(shè)置事務(wù)隔離級別才能保證數(shù)據(jù)正確的情況下性能最好。
隔離級別沒有合理的設(shè)置,會出現(xiàn)一些異常情況,比如臟讀、不可重復(fù)讀、幻讀。根據(jù)自身的業(yè)務(wù)場景去分析是否允許這些異常情況的存在,設(shè)置合理的隔離級別
概念介紹
臟讀
在一個(gè)事務(wù)中讀取到了其他事務(wù)未提交的數(shù)據(jù),就是臟讀。未提交就可能出現(xiàn)回滾,如果數(shù)據(jù)被回滾,那么該事務(wù)對回滾的數(shù)據(jù)進(jìn)行操作必然會導(dǎo)致數(shù)據(jù)錯(cuò)亂,所以臟讀情況要盡可能避免
可重復(fù)讀
在一個(gè)事務(wù)中,從開始到結(jié)束的任意一瞬間讀取到的數(shù)據(jù)應(yīng)該都是一致的。如果不一致,通常情況下是別的事務(wù)對這些數(shù)據(jù)進(jìn)行了更新操作
幻讀
幻讀常見于對數(shù)據(jù)進(jìn)行插入操作,比如事務(wù)A插入了一些數(shù)據(jù)還未來得及提交,事務(wù)B插入的相同條數(shù)的數(shù)據(jù)并提交,這時(shí)事務(wù)A進(jìn)行提交時(shí)會發(fā)現(xiàn)自己插入的數(shù)據(jù)不是預(yù)期的結(jié)果,感覺像出現(xiàn)了幻覺一樣
四種事務(wù)隔離級別
read-uncommitted
讀未提交,該級別的事務(wù)允許讀取其他事務(wù)未提交的數(shù)據(jù)。會出現(xiàn)臟讀、不可重復(fù)讀、幻讀
read-committed
讀已提交,該級別的事務(wù)只能讀取到其他事務(wù)已提交的數(shù)據(jù)。避免了臟讀,會出現(xiàn)不可重復(fù)讀、幻讀
repeatable-read
重復(fù)讀,是mysql默認(rèn)的隔離級別,該級別的事務(wù)只能讀取到其他事務(wù)已提交的數(shù)據(jù),并且在事務(wù)的開始到結(jié)束的整個(gè)過程,讀取到的數(shù)據(jù)都是一致的。避免了臟讀、不可重復(fù)讀,會出現(xiàn)幻讀
serializable
串行化,該級別的事務(wù)會對讀取到數(shù)據(jù)進(jìn)行鎖定,其他事務(wù)想要訪問這些數(shù)據(jù)只能等待,所以是串行,性能較差。避免了臟讀、不可重復(fù)讀、幻讀
演示準(zhǔn)備
使用命令行登錄mysql,-u指定用戶名;-P指定端口,mysql默認(rèn)端口是3306,我的數(shù)據(jù)庫使用的是3307;-p參數(shù)會提示輸入密碼
mysql -u root -P 3307 -p
創(chuàng)建demo數(shù)據(jù)庫,創(chuàng)建trans表,向表中添加兩條數(shù)據(jù);
create database demo;use demo;create table trans(id int(11) primary key,name varchar(50));insert trans values (1,'張三');insert trans values (2,'李四');select * from trans;

查看并關(guān)閉當(dāng)前會話的自動提交,1是開啟自動提交,0是關(guān)閉自動提交
select @@autocommit;set autocommit=0;

打開另一個(gè)命令行窗口,登錄mysql,并關(guān)閉自動提交
演示臟讀
在A窗口查看當(dāng)前會話的隔離級別,并設(shè)置為讀未提交。查看表,可以看到之前初始化的兩條數(shù)據(jù)
select @@tx_isolation;set tx_isolation='read-uncommitted';

在B窗口開啟一個(gè)事務(wù),更新id=1的名字為張三1
start transaction;update trans set name='張三1' where id=1;

在A窗口查看表,可以看到B窗口未提交的數(shù)據(jù)被讀取到了,這就是臟讀

將事務(wù)隔離級別提高即可避免出現(xiàn)臟讀。為方便后面的演示,在B窗口中把數(shù)據(jù)回滾為最初狀態(tài)
rollback;
演示不可重復(fù)讀
在A窗口設(shè)置事務(wù)隔離級別為讀已提交,然后開啟事務(wù),查詢表中數(shù)據(jù)
set tx_isolation='read-committed';
在B窗口更新數(shù)據(jù),并提交

在A窗口再次查看表,可以看到,同一個(gè)事務(wù)中兩次的查詢結(jié)果不一樣,這就是不可重復(fù)讀。假設(shè)在兩次查詢之間有更新操作,有可能造成更新的結(jié)果和預(yù)期的不一致

將隔離級別提高為reapeatable-read可避免不可重復(fù)讀,但級別越高性能越差。讀已提交是常用的事務(wù)隔離級別,有些業(yè)務(wù)需要可重復(fù)讀時(shí),加上樂觀鎖即可
演示幻讀
在A窗口開啟事務(wù),查詢數(shù)據(jù),可以看到兩條數(shù)據(jù)

在窗口B插入一條數(shù)據(jù),并提交

假設(shè)窗口A并不知道B窗口插入了一條數(shù)據(jù),A窗口也想插入這條數(shù)據(jù)就會報(bào)錯(cuò)

從A窗口的角度分析,在同一個(gè)事務(wù)中,剛開始查的時(shí)候沒有id=3的這條記錄,等到插入時(shí)報(bào)錯(cuò)主鍵沖突,感覺很魔幻,這就是幻讀。
其他小知識點(diǎn)
@@在mysql中表示內(nèi)置變量,內(nèi)置變量可對當(dāng)前會話生效或者全局生效。上文演示的所有內(nèi)置變量是僅當(dāng)前會話生效,也可以使用gloabl關(guān)鍵字使內(nèi)置變量全局生效。
select @@gloabl.autocommit;set @@global.autocommit=1;

?推薦閱讀

華為最美小姐姐被外派墨西哥后...

國內(nèi)有程序員電視劇了,結(jié)果看了一分鐘,就吐了...

男女洗澡前后區(qū)別,太形象了!
END


頂級程序員:topcoding
做最好的程序員社區(qū):Java后端開發(fā)、Python、大數(shù)據(jù)、AI
一鍵三連「分享」、「點(diǎn)贊」和「在看」
