1. 程式人生 > >redis專項練習三redis的事務和樂觀鎖

redis專項練習三redis的事務和樂觀鎖

一、redis的事務

  Redis目前對事務的支援相對簡單。Redis只能保證一個client發起的事務中的命令可以連續的執行,而中間不會插入其他的client命令。當一個client在一個連結中發出multi命令時,這個連結會進入一個事務上下文,該連線後續的命令不會立即執行,而是先放到一個佇列中,當執行exec命令時,redis會順序的執行佇列中的所有命令。注意redis的事務和樂觀鎖只針對於redis單機例項,redis的叢集沒有事務相關的概念。

  redis事務設定的命令有 MULTI (multi)(開啟事務), exec(執行佇列中的redis命令),discard(取消)Redis支援簡單的事務

 1、Redis與 mysql事務的對比

 

Mysql

Redis

開啟

start transaction

muitl

語句

普通sql

普通命令

失敗

rollback 回滾

discard 取消

成功

commit

exec

注: rollback與discard 的區別

如果已經成功執行了2條語句, 第3條語句出錯.

Rollback後,前2條的語句影響消失.

Discard只是結束本次事務,前2條語句(在exec命令之前已經成功的命令)造成的影響仍然還在

2、客戶端命令:

#先設定金額為10000
127.0.0.1:6379> set money 10000
OK
#獲取金額
127.0.0.1:6379> get money
"10000"
#執行事務操作
127.0.0.1:6379> MULTI
OK
#將所有的redis命令都存在到佇列中,在執行discard(取消執行佇列中的命令)
#或者exec操作(執行存放在佇列中的redis命令)
127.0.0.1:6379> set money 9000
QUEUED
127.0.0.1:6379> get money
QUEUED
#執行操作
127.0.0.1:6379> exec

 3、java程式碼實現

    /**
     * redis的事務操作
     */
    @Test
    public void testTransaction(){
        ValueOperations<String,String> stringOper = redisTemplate.opsForValue();
        stringOper.set("張三","10000");
        stringOper.set("李四","10000");

        //開啟redis的事務支援
        redisTemplate.setEnableTransactionSupport(true);
        //開啟事務
        redisTemplate.multi();

         //執行轉賬操作 張三 減去500 李四加上500
        stringOper.increment("張三",-500);
        stringOper.increment("李四",500);

        //取消操作 則上面的轉賬命令不會執行,則兩人的金額不會發生改變
        //redisTemplate.discard();
        //執行操作,轉賬成功
        redisTemplate.exec();

        String  zhangsanMoney = stringOper.get("張三");
        String lisimoney = stringOper.get("李四");

        log.info("redis discard取消(沒有發生變數) 轉賬後張三的資金:{},李四的資金:{}",zhangsanMoney,lisimoney);
    }

 4、注意 

在mutil後面的語句中, 語句出錯可能有2種情況

1: 語法就有問題,

這種,exec時,報錯, 所有語句得不到執行

2: 語法本身沒錯,但適用物件有問題. 比如 zadd 操作list物件

Exec之後,會執行正確的語句,並跳過有不適當的語句.

(如果zadd操作list這種事怎麼避免? 這一點,由程式設計師負責)

二、redis的樂觀鎖

1、redis的樂觀鎖

樂觀鎖:redis大多數是基於資料版本(version)的記錄機制實現的。即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表新增一個version欄位來實現。在讀取資料時,將此版本號一同讀出,之後更新時對此版本號加1。此時,將提交資料的版本號與資料庫表對應記錄的當前版本號進行對比,如果提交的資料版本號大於資料庫當前版本號,則予以更新,否則認為是過期資料。

watch監控:watch命令會監控給定的key,當exec時如果監視的key從呼叫watch後發生過變化,則整個事務會失敗。也可以呼叫watch多次監視多個key,這樣就對指定事務key加樂觀鎖了。注意watch的key是對整個連結有效的,事務也一樣。如果連結斷開,監視和事務都會被自動清除。當然exex、discard、unwatch命令都會自動清除連結中的所有監視。

2、客戶端命令

  2.1 只事務實現兩個人搶一張票

 client 1

#開啟事務
127.0.0.1:6379> MULTI
OK
#票數自減
127.0.0.1:6379> DECR ticket
QUEUED
#等待客戶端2操作 在執行該操作
#客戶端2操作完成後,再執行則票數在0的基礎上-1 則票數為-1 不合理
127.0.0.1:6379> exec
1) (integer) -1
127.0.0.1:6379> get ticket
"-1"

client 2

#客戶端2操作
127.0.0.1:6379> MULTI
OK
#票數自減
127.0.0.1:6379> decr ticket
QUEUED
執行 票數為0
127.0.0.1:6379> exec
1) (integer) 0

出現 票數為負數的情況,則無法確保業務正確性。

2、使用樂觀鎖配合事務完成搶票操作

 client 1

#使用watch命令來監聽ticket  如果在針對該key操作的時候 發現key已經被操作過
#則這次事務所涉及的redis操作都被取消
127.0.0.1:6379> watch ticket
OK
127.0.0.1:6379> MULTI
OK
#在票數自減之前,client2已經對ticket進行了操作,則該次事務取消
127.0.0.1:6379> decr ticket
QUEUED
#操作為 nil 說明別的客戶端已經操作過,無法進行相關的操作
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get ticket
"0"

client 2

#客戶端2操作
127.0.0.1:6379> MULTI
OK
#票數自減
127.0.0.1:6379> decr ticket
QUEUED
執行 票數為0
127.0.0.1:6379> exec
1) (integer) 0

  則不會出現業務超售情況,確保了事務,watch命令在一次key的修改事務完成後失效,如果需要繼續監聽,則重新執行watch命令。

 

有關redis的事務和樂觀鎖的知識先寫到這裡,以後有什麼新的理解,在重新補充,第一次學習redis,如果有什麼錯誤的地方,希望大家指出,手打不易,看完請點贊,您的點贊是我繼續更新的動力。