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,如果有什麼錯誤的地方,希望大家指出,手打不易,看完請點贊,您的點贊是我繼續更新的動力。