數(shù)據(jù)庫事務(wù)的四大特性以及事務(wù)的隔離級別整理

事務(wù)的四大特性

  • 原子性(atomicity)
    我們經(jīng)常說,一個(gè)事務(wù)執(zhí)行失敗了,就得回滾,其實(shí)這就是事務(wù)的原子性,一個(gè)完整事務(wù),要么全部執(zhí)行成功,如果有一個(gè)或者多個(gè)失敗,那么就要回滾,其實(shí)這也是另一個(gè)特性即一致性的基礎(chǔ)
  • 一致性(consistency)
    一致性,先舉個(gè)栗子,最容易理解的栗子,本來劉備有200元,關(guān)羽沒有錢,那么劉備給關(guān)羽轉(zhuǎn)賬100元,現(xiàn)在需要兩步執(zhí)行這個(gè)操作,先減去劉備賬戶里面的100元,第二步,給關(guān)羽賬號增加100元,那么這兩步算是一個(gè)事務(wù)。在事務(wù)發(fā)生前,關(guān)羽0 +劉備200=200元,事務(wù)結(jié)束后,關(guān)羽100+劉備100=200,數(shù)據(jù)沒有平白增加或者減少。而且不管這兄弟倆怎么轉(zhuǎn),這個(gè)總和不會發(fā)生變化,這就是事務(wù)的一致性。一致性就是說事務(wù)必須使用數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài)。想想我們講的第一個(gè)原子性,如果一半成功,一半失敗而沒有回滾,還會有數(shù)據(jù)的一致性嗎?
  • 隔離性(isolation)
    隔離性主要是針對并發(fā)訪問來講的,當(dāng)多個(gè)用戶修改數(shù)據(jù)表時(shí),數(shù)據(jù)庫為每一個(gè)用戶開啟的事務(wù),不能被其他的事務(wù)干擾,多個(gè)并發(fā)要互相隔離,即對于同一個(gè)資源,在同一個(gè)時(shí)間段只能有一個(gè)事務(wù)可以修改
  • 持久性(durability)
    持久性就是說一旦事務(wù)成功提交,那么對于數(shù)據(jù)庫來說,這種改變是持久的

事務(wù)的隔離級別

在mysql中,支持四種隔離級別,即ru,rc,rr,serializealbe,后面我們會一一講解,mysql默認(rèn)支持的的rr,

  • 此處給大家推薦一款好用的數(shù)據(jù)庫管理工具,jetbrains出品的datagrip;好用哇哇!,下面的sql語句都是在datagrip中運(yùn)行的

    image.png

  • READ UNCOMMITTED(未提交讀)

  • ru是指在一個(gè)事務(wù)執(zhí)行未完成的時(shí)候,數(shù)據(jù)對其他事務(wù)也是可見的,而且讀取的是已經(jīng)更改但是還沒有commit的數(shù)據(jù),這種保證不了數(shù)據(jù)準(zhǔn)確的隔離級別幾乎是不用的。也正如此,所以它的性能是最優(yōu)的

    • 創(chuàng)建相關(guān)的數(shù)據(jù)庫和數(shù)據(jù)表
create schema test;
use test;
CREATE TABLE `t` (
                     `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
                     `point` int(11) DEFAULT NULL,
                     PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

  • 下面粘貼sql語句,注意在同一行的代碼先左后右執(zhí)行,是在兩個(gè)sql窗口執(zhí)行的,在執(zhí)行到第6行的時(shí)候發(fā)現(xiàn),console2開啟了事務(wù),但是還沒有commit,但是console1已經(jīng)查詢到未提交的結(jié)果了。這就是讀未提交


    image.png
  • READ COMMITTED(讀已提交)
    大部分的數(shù)據(jù)庫默認(rèn)隔離級別就是rc,就是說一個(gè)事務(wù)只能看見已經(jīng)提交的修改結(jié)果,如果沒有提交,那么只能看見原來的結(jié)果,而不會看到修改未提交的結(jié)果,但是這會出現(xiàn)一個(gè)問題,什么問題呢?就是 在一個(gè)事務(wù)中,如果另一個(gè)事務(wù)修改了數(shù)據(jù)并且提交,那么在第一個(gè)事務(wù)中針對同一個(gè)查詢,就會查詢出來兩個(gè)不同的數(shù)據(jù),即不可重復(fù)讀


    image.png
  • 新增一條如果沒有提交也是讀取不到的


    image.png
  • 這個(gè)時(shí)候,如果兩個(gè)窗口同時(shí)對一條數(shù)據(jù)更改會發(fā)生什么情況呢?第二條更新命令會一直等第一條命令的提交,未提交就處于等待狀態(tài)


    image.png
  • REPEATABLE READ(可重復(fù)讀)
    可重復(fù)讀是mysql默認(rèn)的隔離級別,rr解決了臟讀的問題,但是可能會出現(xiàn)幻讀,下面先來演示一下幻讀的實(shí)現(xiàn)
  • 看一下已經(jīng)解決了讀未提交的問題和不可重復(fù)讀的問題


    image.png
  • 幻讀其實(shí)是解決不可重復(fù)讀的一個(gè)缺點(diǎn),為什么?首先事務(wù)1已經(jīng)執(zhí)行了一個(gè)插入操作,新增id3,但是為了可重復(fù)讀,事務(wù)2看不到新增的數(shù)據(jù),所以當(dāng)事務(wù)2增加id3的時(shí)候報(bào)錯(cuò),因?yàn)閕d3在數(shù)據(jù)表中已經(jīng)切切實(shí)實(shí)的存在了??芍貜?fù)讀,有點(diǎn)像把某一個(gè)時(shí)刻的數(shù)據(jù)作為快照寫入了緩存,在commit之前所有的讀取都是源自緩存,而非真實(shí)的表


    image.png

SERIALIZABLE(可串行化)

其實(shí)我們可以先自己想一下,如何在解決重復(fù)讀的時(shí)候還能解決幻讀呢?是不是感覺有點(diǎn)不可能,既然不幻讀,那就實(shí)現(xiàn)不了可重復(fù)讀,然鵝,但是,前面的操作都是基于兩個(gè)事務(wù),但是如我們把兩個(gè)事務(wù)再關(guān)聯(lián)一下呢,是不是就可以解決了,這就是串行的意思,可串行化解決了臟讀,幻讀,可重復(fù)讀等問題,但是,勢必會影響效率,"可串行化"會在讀取的每一行數(shù)據(jù)上都加鎖,所以可能會導(dǎo)致大量的鎖等待和超時(shí)問題,所以在實(shí)際的生產(chǎn)環(huán)境中也很少會用到這個(gè)隔離級別,只有在非常需要確保數(shù)據(jù)的一致性切可以接受沒有并發(fā)的情況下,才會考慮使用這個(gè)隔離級別。

  • 演示一下,先看一下目前的情況


    image.png
    • 怎么實(shí)現(xiàn)串行化呢,說起來有點(diǎn)惡心,這次不是緩存快照了,讓你讀實(shí)時(shí)數(shù)據(jù),但是更新操作我給你停了。我讓你等到?jīng)]有事務(wù)了,或者其他事務(wù)都提交了,才讓你這個(gè)寫操作執(zhí)行,嗯,就是這樣。感覺有點(diǎn)不太高明


      image.png
  • 用了鎖,有人讀也上鎖,有人寫也上鎖,效率能沒有影響嗎,寫之前先看有沒有讀鎖,有讀鎖就等待。這種方式就是 簡單,粗暴


    image.png
  • 綜合下來,還是看使用的業(yè)務(wù)場景選擇不同的隔離級別,個(gè)人感覺大部分業(yè)務(wù)還是用rc比較好。你覺得呢?
    附sql腳本
    console1.sql
use test;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
#READ UNCOMMITTED(未提交讀)
start transaction ;
select * from t where id =1;
update t set point=50 where id =1;
commit ;
#READ COMMITTED(讀已提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
select * from t where id =1;
start transaction ;
update t set point=80 where id =1;
insert into t values (null,200);
select * from t where id =2;
commit ;

#REPEATABLE READ(可重復(fù)讀)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
select * from t ;
start transaction ;
update t set point=100 where id=1;
commit ;

start transaction ;
select * from t ;
insert into t values (null,300);
select * from t ;
commit


## SERIALIZABLE(可串行化)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
start transaction ;
select * from t;
insert into t values (null,123);

console2.sql

use test;
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
#READ UNCOMMITTED(未提交讀)
start transaction ;
select * from t where id =1;
select * from t where id =1;
commit ;
#READ COMMITTED(讀已提交)
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
select * from t where id =1;
start transaction ;
update t set point=10 where id =1;
select * from t where id =1;
select * from t where id =2;
commit ;

#REPEATABLE READ(可重復(fù)讀)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ ;
select * from t ;
start transaction ;
select * from t where id=1;
select * from t where id=1;

start transaction ;
select * from t ;
select * from t ;
insert into t values (3,300);
commit


## SERIALIZABLE(可串行化)
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE ;
start transaction ;
select * from t;
commit;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容