redis的事務不是原子性

Reference: https://blog.csdn.net/u011692780/article/details/81213010redis

 

1、事務的四大特性數據庫

關係型數據庫的事務具備四個特性:併發

1. 原子性.net

2. 一致性blog

3. 隔離性生命週期

4. 持久性隊列

2、而在咱們redis數據庫中,事務回事什麼樣子的呢?事務

首先咱們給出一個定義:redis的事務中,一次執行多條命令,本質是一組命令的集合,一個事務中全部的命令將被序列化,即按順序執行而不會被其餘命令插入監控

在redis中,事務的做用就是在一個隊列中一次性、順序性、排他性的執行一系列的命令。原理

事務的生命週期:

1. 事務的建立:使用MULTI開啓一個事務

2. 加入隊列:在開啓事務的時候,每次操做的命令將會被插入到一個隊列中,同時這個命令並不會被真的執行

3. EXEC命令進行提交事務

經常使用的關於事務的命令有:

1. MULTI:使用該命令,標記一個事務塊的開始,一般在執行以後會回覆OK,(但不必定真的OK),這個時候用戶能夠輸入多個操做來代替逐條操做,redis會將這些操做放入隊列中。

2. EXEC:執行這個事務內的全部命令

3. DISCARD:放棄事務,即該事務內的全部命令都將取消

4. WATCH:監控一個或者多個key,若是這些key在提交事務(EXEC)以前被其餘用戶修改過,那麼事務將執行失敗,須要從新獲取最新數據重頭操做(相似於樂觀鎖)。

5. UNWATCH:取消WATCH命令對多有key的監控,全部監控鎖將會被取消。

注意:關於樂觀鎖等概念:

   樂觀鎖:就像他的名字,不會認爲數據不會出錯,他不會爲數據上鎖,可是爲了保證數據的一致性,他會在每條記錄的後面添加一個標記(相似於版本號),假設A 獲取K1這條標記,獲得了k1的版本號是1,並對其進行修改,這個時候B也獲取了k1這個數據,固然,B獲取的版本號也是1,一樣也對k1進行修改,這個時候,若是B先提交了,那麼k1的版本號將會改變成2,這個時候,若是A提交數據,他會發現本身的版本號與最新的版本號不一致,這個時候A的提交將不會成功,A的作法是從新獲取最新的k1的數據,重複修改數據、提交數據。

  悲觀鎖:這個模式將認定數據必定會出錯,因此她的作法是將整張表鎖起來,這樣會有很強的一致性,可是同時會有極低的併發性(經常使用語數據庫備份工做,相似於表鎖)。

那麼,如今咱們來執行一次具體看看redis的事務機制:

首先我會開啓事務,並向數據庫中存儲4條數據,能夠看到沒執行一條命令的時候都會顯示入隊,並不會返回執行結果,說明redis中在事務提交以前,其內部的全部命令將不會被執行:

 

那麼,若是中間有命令出錯了會怎樣呢?如今我隨便打幾個字符試一試:

 

能夠看出,在第三條命令中我隨便打了幾個字符,提交事務的時候並無成功,這也很符合咱們對事務的理解,嗯~具備原子性。可是,有一個細節,那就是錯誤命令在我輸入的時候就已經報錯了,也就是說這個條錯誤命令在進入隊列的時候redis就已經知道這是一條錯誤命令,這樣,整個事務的命令將所有失敗,那麼,有沒有一種可能某個錯誤指令在進入隊列的時候redis尚未發現他的錯誤呢?咱們試一試下面這個例子:

 

問題出現了,咱們能夠看到,name+1這條指令實際上是錯誤的,可是提交事務的時候會發現,這條錯誤命令確實沒有執行,可是其餘正確的命令卻執行,這是爲何的?

緣由是在redis中,對於一個存在問題的命令,若是在入隊的時候就已經出錯,整個事務內的命令將都不會被執行(其後續的命令依然能夠入隊),若是這個錯誤命令在入隊的時候並無報錯,而是在執行的時候出錯了,那麼redis默認跳過這個命令執行後續命令。也就是說,redis只實現了部分事務。

下面咱們來看看剛剛提到的鎖的問題,咱們說過,redis的鎖CAS(check and set)相似於樂觀鎖,redis的實現原理是使用watch進行監視一個(或多個)數據,若是在事務提交以前數據發生了變化(估計使用了相似於樂觀鎖的標記),那麼整個事務將提交失敗,咱們能夠舉一個例子,咱們開啓兩個終端,模擬兩我的的操做,設置一條數據爲count,初始時100,如今A對其進行監控,而且爲count增長20

 

在沒有提交以前,B也獲取了這個count,爲其減小50,

 

那麼這個時候A若是提交事務,會出現失敗提示:

 

能夠看到,在A對數據的修改過程當中,B對數據進行了修改,那麼這條數據的「標記」就發生了變化,已經不是當初A取出數據的時候的標記了,這樣,A的事務也就提交失敗了。

最後經過上述的實驗,咱們總結redis事務的三條性質:

1. 單獨的隔離操做:事務中的全部命令會被序列化、按順序執行,在執行的過程當中不會被其餘客戶端發送來的命令打斷
2. 沒有隔離級別的概念:隊列中的命令在事務沒有被提交以前不會被實際執行
3. 不保證原子性:redis中的一個事務中若是存在命令執行失敗,那麼其餘命令依然會被執行,沒有回滾機制

3、Redis中的事務爲何沒有原子性與watch鎖

Redis事務能夠一次執行多個命令,它先以 MULTI 開始一個事務, 而後將多個命令入隊到事務中, 最後由 EXEC 命令觸發事務, 一併執行事務中的全部命令

    

    在傳統的關係型數據中,只要有任意一條指令失敗,則整個事務都會被撤銷回滾,而在Redis中,中間某條指令的失敗不會致使前面已作指令的回滾,也不會形成後續的指令不作,也所以得出 Redis 事務的執行並非原子性的。

    multi,表明一段事務的開始

    exec,表明一個事務的提交

    queued,表明某個指令在隊列中

    可是,也有例外,好比以下這種狀況

    

    discard,表明某個事務撤銷

    

    watch 鎖 ,在事務中不能改變被鎖的值  (exec提交後返回nil)

    

4、總結

     在redis中,對於一個存在問題的命令,若是在入隊的時候就已經出錯,整個事務內的命令將都不會被執行(其後續的命令依然能夠入隊),若是這個錯誤命令在入隊的時候並無報錯,而是在執行的時候出錯了,那麼redis默認跳過這個命令執行後續命令。也就是說,redis只實現了部分事務。

總結redis事務的三條性質:

1. 單獨的隔離操做:事務中的全部命令會被序列化、按順序執行,在執行的過程當中不會被其餘客戶端發送來的命令打斷2. 沒有隔離級別的概念:隊列中的命令在事務沒有被提交以前不會被實際執行3. 不保證原子性:redis中的一個事務中若是存在命令執行失敗,那麼其餘命令依然會被執行,沒有回滾機制--------------------- 做者:XCCS_澍 來源:CSDN 原文:https://blog.csdn.net/u011692780/article/details/81213010 版權聲明:本文爲博主原創文章,轉載請附上博文連接!

相關文章
相關標籤/搜索