日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis:事务、管道、Lua脚本

發布時間:2025/3/21 数据库 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis:事务、管道、Lua脚本 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. Redis事務定義

Redis中的事務(transaction)是一組命令的集合。事務同命令一樣都是Redis的最小執行單位,一個事務中的命令要么都執行,要么都不執行。
事務的原理是先將屬于一個事務的命令發送給Redis,然后再讓Redis依次執行這些命令。

1.????Redis保證一個事務中的所有命令要么都執行,要么都不執行。如果在發送EXEC命令前客戶端斷線了,則Redis會清空事務隊列,事務中的所有命令都不會執行。而一旦客戶端發送了EXEC命令,所有的命令就都會被執行,即使此后客戶端斷線也沒關系,因為Redis中已經記錄了所有要執行的命令。

2.????除此之外,Redis的事務還能保證一個事務內的命令依次執行而不被其他命令插入。試想客戶端A需要執行幾條命令,同時客戶端B發送了一條命令,如果不使用事務,則客戶端B的命令可能會插入到客戶端A的幾條命令中執行。如果不希望發生這種情況,也可以使用事務。


2. Redis事務的錯誤和回滾

Redis的事務沒有隔離級別的概念(CAID中的I),在事務執行前所有的命令都未執行。對于執行過程中的錯誤按照類型分為兩種。

1. 語法錯誤

語法錯誤指命令不存在或者命令參數的個數不對,這個命令可能會有語法錯誤(參數的數量錯誤、命令名稱錯誤,等等),或者可能會有某些臨界條件(例如:如果使用maxmemory指令,為Redis服務器配置內存限制,那么就可能會有內存溢出條件)。?

可用Redis客戶端檢測第一種類型的錯誤,在調用EXEC命令之前,這些客戶端可以檢查被放入隊列的命令的返回值:如果命令的返回值是QUEUE字符串,那么就表示已經正確地將這個命令放入隊列;否則,Redis將返回一個錯誤。如果將某個命令放入隊列時發生錯誤,那么大多數客戶端將會中止事務,并且丟棄這個事務。
在Redis 2.6.5版本之前,如果發生了上述的錯誤,那么在客戶端調用了EXEC命令之后,Redis還是會運行這個出錯的事務,執行已經成功放入事務隊列的命令,而不會關心先前發生的錯誤。從2.6.5版本開始,Redis在遭遇上述錯誤時,服務器會記住事務積累命令期間發生的錯誤。然后,Redis會拒絕執行這個事務,在運行EXEC命令之后,便會返回一個錯誤消息。最后,Redis會自動丟棄這個事務,這樣便能輕松地混合使用事務和管道。在這種情況下,客戶端可以一次性地將整個事務發送至Redis服務器,稍后再一次性地讀取所有的返回值。


2.?運行錯誤

運行錯誤指在命令執行時出現的錯誤,比如使用散列類型的命令操作集合類型的鍵,這種錯誤在實際執行之前Redis是無法發現的,所以在事務里這樣的命令是會被Redis接受并執行的。如果事務里的一條命令出現了運行錯誤,事務里其他的命令依然會繼續執行(包括出錯命令之后的命令)。


Redis的回滾機制

Redis的事務沒有關系數據庫事務提供的回滾(rollback)功能。為此開發者必須在事務執行出錯后自己收拾剩下的攤子(將數據庫復原回事務執行前的狀態等,這里我們一般采取日志記錄然后業務補償的方式來處理,但是一般情況下,在redis做的操作不應該有這種強一致性要求的需求,我們認為這種需求為不合理的設計)。


3.?Redis的樂觀鎖和Watch

Watch命令描述:
WATCH命令可以監控一個或多個鍵,一旦其中有一個鍵被修改(或刪除),之后的事務就不會執行。監控一直持續到EXEC命令(事務中的命令是在EXEC之后才執行的(事先都是存儲在隊列里)所以在MULTI命令后可以修改WATCH監控的鍵值


在Redis的事務中,WATCH命令可用于提供CAS(check-and-set)功能,且是基于樂觀鎖的思想。假設我們通過WATCH命令在事務執行之前監控了多個Keys,倘若在WATCH之后有任何Key的值發生了變化,EXEC命令執行的事務都將被放棄,同時返回Null multi-bulk應答以通知調用者事務執行失敗。例如,我們再次假設Redis中并未提供incr命令來完成鍵值的原子性遞增,如果要實現該功能,我們只能自行編寫相應的代碼。其偽碼如下:


[java]?view plain?copy

1.??val?=?GET?mykey??

2.??val?=?val?+?1??

3.??SET?mykey?$val??


以上代碼只有在單連接的情況下才可以保證執行結果是正確的,因為如果在同一時刻有多個客戶端在同時執行該段代碼,那么就會出現多線程程序中經常出現的一種錯誤場景--競態爭用(race condition)。比如,客戶端A和B都在同一時刻讀取了mykey的原有值,假設該值為10,此后兩個客戶端又均將該值加一后set回Redis服務器,這樣就會導致mykey的結果為11,而不是我們認為的12。為了解決類似的問題,我們需要借助WATCH命令的幫助,見如下代碼:

[java]?view plain?copy

1.??WATCH?mykey??

2.??val?=?GET?mykey??

3.??val?=?val?+?1??

4.??MULTI??

5.??SET?mykey?$val??

6.??EXEC??


和此前代碼不同的是,新代碼在獲取mykey的值之前先通過WATCH命令監控了該鍵,此后又將set命令包圍在事務中,這樣就可以有效的保證每個連接在執行EXEC之前,如果當前連接獲取的mykey的值被其它連接的客戶端修改,那么當前連接的EXEC命令將執行失敗。這樣調用者在判斷返回值后就可以獲悉val是否被重新設置成功。


由于WATCH命令的作用只是當被監控的鍵值被修改后阻止之后一個事務的執行而不能保證其他客戶端不修改這一鍵值,所以在一般的情況下我們需要在EXEC執行失敗后重新執行整個函數。執行EXEC命令后會取消對所有鍵的監控,如果不想執行事務中的命令也可以使用UNWATCH命令來取消監控。


實現一個hsetNX函數
我們實現的hsetNX這個功能是:僅當字段存在時才賦值。為了避免競態條件我們使用watch和事務來完成這一功能(偽代碼):


[java]?view plain?copy

1.??WATCH?key????

2.??isFieldExists?=?HEXISTS?key,?field????

3.??if?isFieldExists?is?1????

4.??MULTI????

5.??HSET?key,?field,?value????

6.??EXEC????

7.??else????

8.??UNWATCH????

9.??return?isFieldExists??


在代碼中會判斷要賦值的字段是否存在,如果字段不存在的話就不執行事務中的命令,但需要使用UNWATCH命令來保證下一個事務的執行不會受到影響。


?

4. Jedis的事務

Jedis對Redis的事務機制給出了具體實現,示例代碼如下:

[java]?view plain?copy

1.??public?static?void?testWach(){??

2.???????Jedis?jedis?=?RedisCacheClient.getInstrance().getClient();??

3.???????String?watch?=?jedis.watch("testabcd");??

4.???????System.out.println(Thread.currentThread().getName()+"--"+watch);??

5.???????Transaction?multi?=?jedis.multi();??

6.???????multi.set("testabcd",?"23432");??

7.???????try?{??

8.???????????Thread.sleep(3000);??

9.???????}?catch?(InterruptedException?e)?{??

10. ?????????e.printStackTrace();??

11. ?????}??

12. ?????List<Object>?exec?=?multi.exec();??

13. ?????System.out.println("---"+exec);??

14. ?????jedis.unwatch();??

15. ?}??

16. ?public?static?void?testWatch2(){??

17. ?????Jedis?jedis?=?RedisCacheClient.getInstrance().getClient();??

18. ?????String?watch?=?jedis.watch("testabcd2");??

19. ?????System.out.println(Thread.currentThread().getName()+"--"+watch);??

20. ?????Transaction?multi?=?jedis.multi();??

21. ?????multi.set("testabcd",?"125");??

22. ?????List<Object>?exec?=?multi.exec();??

23. ?????System.out.println("--->>"+exec);??

24. ?}??


三Redis的管道

Redis是一個響應式的服務,當客戶端發送一個請求后,就處于阻塞狀態等待Redis返回結果。這樣一次命令消耗的時間就包括三個部分:請求從客戶端到服務器的時間、結果從服務器到客戶端的時間和命令真正執行時間,前兩個部分消耗的時間總和稱為RTT(Round Trip Time),當客戶端與服務器存在網絡延時,RTT就可能會很大,這樣就會導致性能問題。

管道(Pipeline)就是為了改善這個情況的,利用管道,客戶端可以一次性發送多個請求而不用等待服務器的響應,待所有命令都發送完后再一次性讀取服務的響應,這樣可以極大的降低RTT時間從而提升性能。需要注意到是用pipeline方式打包命令發送,redis必須在處理完所有命令前先緩存起所有命令的處理結果。打包的命令越多,緩存消耗內存也越多。所以并不是打包的命令越多越好。(占內存)


pipeline和“事務”是兩個完全不同的概念,pipeline只是表達“交互”中操作的傳遞的方向性,pipeline也可以在事務中運行,也可以不在。無論如何,pipeline中發送的每個command都會被server立即執行,如果執行失敗,將會在此后的相應中得到信息;也就是pipeline并不是表達“所有command都一起成功”的語義(有的成功有的失敗);但是如果pipeline的操作被封裝在事務中,那么將有事務來確保操作的成功與失敗(只允許成功或者失敗)。Pipeline的示例代碼如下:


[java]?view plain?copy

1.??private?static?void?usePipeline(int?count){??

2.??????Jedis?jr?=?null;??

3.??????try?{??

4.??????????jr?=?new?Jedis("10.10.224.44",?6379);??

5.??????????Pipeline?pl?=?jr.pipelined();??

6.??????????for(int?i?=0;?i<count;?i++){??

7.???????????????pl.incr("testKey2");??

8.??????????}??

9.??????????????pl.sync();??

10. ????}?catch?(Exception?e)?{??

11. ????????e.printStackTrace();??

12. ????}??

13. ????finally{??

14. ????????if(jr!=null){??

15. ????????????jr.disconnect();??

16. ????????}??

17. ????}??

18. }??


使pipeline完成操作需要更低的耗時即可。

四Redis Lua腳本

Redis在2.6推出了腳本功能,允許開發者使用Lua語言編寫腳本傳到Redis中執行。使用腳本的好處如下:
1.減少網絡開銷:本來5次網絡請求的操作,可以用一個請求完成,原先5次請求的邏輯放在redis服務器上完成。使用腳本,減少了網絡往返時延。
2.原子操作:Redis會將整個腳本作為一個整體執行,中間不會被其他命令插入。
3.復用:客戶端發送的腳本會永久存儲在Redis中,意味著其他客戶端可以復用這一腳本而不需要使用代碼完成同樣的邏輯。


在實際工作過程中,可以使用lua腳本來解決一些需要保證原子性的問題,而且lua腳本可以緩存在redis服務器上,勢必會增加性能。


Lua語法

見Lua語言模型與 Redis應用中關于Lua語法的詳述。


Eval命令

從Redis2.6.0版本開始,通過內置的Lua解釋器,可以使用EVAL命令對Lua腳本進行求值。EVAL命令的格式如下:


[java]?view plain?copy

1.??EVAL?script?numkeys?key?[key?...]?arg?[arg?...]??


script參數是一段Lua腳本程序,它會被運行在Redis服務器上下文中,這段腳本不必(也不應該)定義為一個Lua函數。numkeys參數用于指定鍵名參數的個數。鍵名參數 key [key ...] 從EVAL的第三個參數開始算起,表示在腳本中所用到的那些Redis鍵(key),這些鍵名參數可以在 Lua中通過全局變量KEYS數組,用1為基址的形式訪問( KEYS[1] , KEYS[2] ,以此類推)。

在命令的最后,那些不是鍵名參數的附加參數 arg [arg ...] ,可以在Lua中通過全局變量ARGV數組訪問,訪問的形式和KEYS變量類似( ARGV[1] 、ARGV[2] ,諸如此類)。例如


[java]?view plain?copy

1.??>?eval?"return?{KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"?2?key1?key2?first?second??

2.??1)?"key1"??

3.??2)?"key2"??

4.??3)?"first"??

5.??4)?"second"??


其中 "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 是被求值的Lua腳本,數字2指定了鍵名參數的數量,key1和key2是鍵名參數,分別使用 KEYS[1] 和 KEYS[2] 訪問,而最后的 first 和 second 則是附加參數,可以通過 ARGV[1] 和 ARGV[2] 訪問它們。


Call和pcall

在 Lua 腳本中,可以使用兩個不同函數來執行Redis命令,它們分別是:


1.????redis.call()

2.????redis.pcall()

這兩個函數的唯一區別在于它們使用不同的方式處理執行命令所產生的錯誤。當redis.call() 在執行命令的過程中發生錯誤時,腳本會停止執行,并返回一個腳本錯誤,錯誤的輸出信息會說明錯誤造成的原因,redis.pcall() 出錯時并不引發(raise)錯誤,而是返回一個帶 err 域的 Lua 表(table),用于表示錯誤。

Jedis調用

Jedis中調用腳本需要以字符串形式給出腳本主體,并遵從EVAL的數據規范,示例代碼如下:

[java]?view plain?copy

1.??String?script?="local?result={}?"?+???

2.??????????????????"?for?i,v?in?ipairs(KEYS)?do?"?+???

3.??????????????????"?result[i]?=?redis.call('get',v)?"?+???

4.??????????????????"?end?"?+???

5.??????????????????"?return?result?";??

6.????

7.??Jedis?jedis?=?new?Jedis(ip,port);??

8.????

9.??jedis.eval(script,keyCount,String?…?params);??


注意,不要再Lua腳本中出現死循環和耗時的運算,否則redis將不接受其他的命令,這個redis就掛了,只能script kill,如果有寫入的話,只能shutdown nosave。?所以使用時要注意不能出現死循環、耗時的運算。redis是單進程、單線程執行腳本。


五 Redis事務、管道和腳本的區別

1. 事務和腳本從原子性上來說都能滿足原子性的要求,其區別在于腳本可借助Lua語言可在服務器端存儲的便利性定制和簡化操作,但腳本無法處理長耗時的操作。

2. 管道是無狀態操作集合,使用管道可能在效率上比使用script要好,但是有的情況下只能使用script。因為在執行后面的命令時,無法得到前面命令的結果,就像事務一樣,所以如果需要在后面命令中使用前面命令的value等結果,則只能使用script或者事務+watch。
?

總結

以上是生活随笔為你收集整理的Redis:事务、管道、Lua脚本的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日韩簧片在线观看 | 精品福利影院 | 粉嫩av一区二区三区天美传媒 | 变态视屏| 丰满的人妻hd高清日本 | 成人午夜精品福利免费 | 九色porn蝌蚪| 91a视频| 97超碰人人爱 | 精品久久久噜噜噜久久久 | 青草伊人久久 | 国产一区在线免费观看 | 亚洲国产精品久久久久爰色欲 | 亚洲精品久久久狠狠狠爱 | 狠狠爱夜夜操 | 免费国产| 日韩欧美在线不卡 | 国产精品日韩无码 | 久久精品国产亚洲av麻豆 | 亚洲精品国产精品乱码不99按摩 | 黄色片18| 国产精品视频一区二区三区, | 在线日韩 | 日本japanese极品少妇 | 僵尸叔叔在线观看国语高清免费观看 | 欧洲中文字幕日韩精品成人 | 国产亚洲小视频 | 精品日本一区二区三区在线观看 | 女人的天堂网站 | 视频一二三区 | 欧美精品久久久久久久 | 熊猫成人网| 懂色av一区二区在线播放 | 天天操夜夜摸 | 97av在线 | 中文字幕25页| 天天干天天操天天摸 | 变态另类一区二区 | 精品网站 | 国产福利视频网站 | 久久亚洲美女 | 日本黄色电影网址 | 国产精品揄拍100视频 | 夜夜操操操 | www中文字幕在线观看 | 国内爆初菊对白视频 | 欧美激情视频一区二区三区 | 亚洲爆爽av | 最新天堂中文在线 | eeuss鲁丝片一区二区三区 | 日韩经典中文字幕 | 国产一级在线播放 | 蜜臀在线观看 | 国毛片 | 丰满熟女人妻一区二区三区 | 国产成人精品视频一区二区 | 亚洲欧美自拍视频 | 久久精品欧美日韩 | 人乳videos巨大吃奶 | 久久成人乱码欧美精品一区二区 | 97久久国产精品 | 久久久com | 中文字幕免费在线 | 欧美日韩在线影院 | 国产91在线免费观看 | 日韩亚洲欧美在线观看 | 美女黄污网站 | a视频免费在线观看 | 自由 日本语 热 亚洲人 | 国产不卡视频一区二区三区 | 免费黄色在线网址 | 日本50路肥熟bbw | 日韩欧美麻豆 | 亚洲性自拍 | 九九久久99| 免费在线视频一区 | www.jizzcom| 亚洲最大黄网 | 免费看国产曰批40分钟粉红裤头 | 欧美极品少妇xxxxⅹ免费视频 | 男人天堂b | 樱空桃在线观看 | 亚洲视频在线免费播放 | 欧美日韩免费在线 | 夜夜嗨av禁果av粉嫩av懂色av | 五月婷婷综合激情 | 一级片久久 | 男人操女人下面 | 中文字幕乱码人妻一区二区三区 | 亚洲国产视频一区 | 国产xxxx性hd极品 | missav|免费高清av在线看 | 亚洲黄色在线观看视频 | 青青操在线观看视频 | 日日躁夜夜躁狠狠躁 | 亚洲国产片 | 婷婷av一区二区三区 | 欧美做爰全过程免费看 | 婷婷中文字幕在线 |