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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

redis事务原理,使用,详解

發布時間:2024/2/28 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 redis事务原理,使用,详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
聲明:本博客內容來自《Redis深度歷險》一書 ? ? ?? 為了確保連續多個操作的原子性,一個成熟的數據庫通常都會有事務支持,Redis 也不例外。Redis 的事務使用非常簡單,不同于關系數據庫,我們無須理解那么多復雜的事務模型,就可以直接使用。不過也正是因為這種簡單性,它的事務模型很不嚴格,這要求我們不能像使用關系數據庫的事務一樣來使用 Redis 務的基本使用 每個事務的操作都有 begincommit rollback begin 指示事務的開始 commit 指示事務的提交 rollback 指示事務的回滾 它大致的形式如下 begin(); try {command1();command2();....commit(); } catch(Exception e) {rollback(); } Redis 在形式上看起來也差不多,分別是 multi/exec/discardmulti 指示事務的開始,exec 指示事務的執行,discard 指示事務的丟棄 > multi OK > incr books QUEUED > incr books QUEUED > exec (integer) 1 (integer) 2 上面的指令演示了一個完整的事務過程,所有的指令在 exec 之前不執行,而是緩存在服務器的一個事務隊列中,服務器一旦收到 exec 指令,才開執行整個事務隊列,執行完畢后一次性返回所有指令的運行結果。因為 Redis 的單線程特性,它不用擔心自己在執行隊列的時候被其它指令打攪,可以保證他們能得到的「原子性」執行。QUEUED 是一個簡單字符串,同 OK 是一個形式,它表示指令已經被服務器緩存到隊列里了 原子性 事務的原子性是指要么事務全部成功,要么全部失敗,那么 Redis 事務執行是原子性的么? 下面我們來看一個特別的例子。 > multi OK > set books iamastring QUEUED > incr books QUEUED > set poorman iamdesperate QUEUED > exec 1) OK 2) (error) ERR value is not an integer or out of range 3) OK > get books "iamastring" > get poorman "iamdesperate 上面的例子是事務執行到中間遇到失敗了,因為我們不能對一個字符串進行數學運算,事務在遇到指令執行失敗后,后面的指令還繼續執行,所以 poorman 的值能繼續得到設置 到這里,你應該明白Redis 的事務根本不能算「原子性」,而僅僅是滿足了事務的「隔離性」,隔離性中的串行化——當前執行的事務有著不被其它事務打斷的權利 discard(丟棄) Redis 為事務提供了一個 discard 指令,用于丟棄事務緩存隊列中的所有指令,在 exec 執行之前 > get books (nil) > multi OK > incr books QUEUED > incr books QUEUED > discard OK > get books (nil) 我們可以看到 discard 之后,隊列中的所有指令都沒執行,就好像 multi discard 中間的所有指令從未發生過一樣 優化 上面的 Redis 事務在發送每個指令到事務緩存隊列時都要經過一次網絡讀寫,當一個事務內部的指令較多時,需要的網絡 IO 時間也會線性增長。所以通常 Redis 的客戶端在執行事務時都會結合 pipeline 一起使用,這樣可以將多次 IO 操作壓縮為單次 IO 操作。比如我們在使用 Python Redis 客戶端時執行事務時是要強制使用 pipeline pipe = redis.pipeline(transaction=true) pipe.multi() pipe.incr("books") pipe.incr("books") values = pipe.execute() Watch 考慮到一個業務場景,Redis 存儲了我們的賬戶余額數據,它是一個整數。現在有兩個并發的客戶端要對賬戶余額進行修改操作,這個修改不是一個簡單的 incrby 指令,而是要對余額乘以一個倍數。Redis 可沒有提供 multiplyby 這樣的指令。我們需要先取出余額然后在內存里乘以倍數,再將結果寫回 Redis 這就會出現并發問題,因為有多個客戶端會并發進行操作。我們可以通過 Redis 的分布式鎖來避免沖突,這是一個很好的解決方案 但是分布式鎖是一種悲觀鎖,那是不是可以使用樂觀鎖的方式來解決沖突呢? Redis 提供了這種 watch 的機制,它就是一種樂觀鎖。有了 watch 我們又多了一種可以用來解決并發修改的方法。 watch 的使用方式如下 while True:do_watch()commands()multi()send_commands()try:exec()breakexcept WatchError:continue watch 會在事務開始之前盯住 1 個或多個關鍵變量,當事務執行時,也就是服務器收到了 exec 指令要順序執行緩存的事務隊列時,Redis 會檢查關鍵變量自 watch 之后,是否被修改了 (包括當前事務所在的客戶端)。如果關鍵變量被人動過了,exec 指令就會返回 null 回復告知客戶端事務執行失敗,這個時候客戶端一般會選擇重試 > watch books OK > incr books # 被修改了 (integer) 1 > multi OK > incr books QUEUED > exec # 事務執行失敗 (nil) 當服務器給 exec 指令返回一個 null 回復時,客戶端知道了事務執行是失敗的,通常客戶端 (redis-py) 都會拋出一個 WatchError 這種錯誤,不過也有些語言 (jedis) 不會拋出異常,而是通過在 exec 方法里返回一個 null,這樣客戶端需要檢查一下返回結果是否為 null來確定事務是否執行失敗 注意事項 Redis 禁止在 multi exec 之間執行 watch 指令,而必須在 multi 之前做好盯住關鍵變量,否則會出錯 接下來我們使用 Python 語言來實現對余額的加倍操作 import redis def key_for(user_id):return "account_{}".format(user_id) def double_account(client, user_id):key = key_for(user_id)while True:client.watch(key)value = int(client.get(key))value *= 2 # 加倍pipe = client.pipeline(transaction=True)pipe.multi()pipe.set(key, value)try:pipe.execute()break # 總算成功了except redis.WatchError:continue # 事務被打斷了,重試return int(client.get(key)) # 重新獲取余額 client = redis.StrictRedis() user_id = "abc" client.setnx(key_for(user_id), 5) # setnx 做初始化 print double_account(client, user_id) 下面我們再使用 Java 語言實現一遍 import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class TransactionDemo {public static void main(String[] args) {Jedis jedis = new Jedis();String userId = "abc";String key = keyFor(userId);jedis.setnx(key, String.valueOf(5)); # setnx 做初始化System.out.println(doubleAccount(jedis, userId));jedis.close();}public static int doubleAccount(Jedis jedis, String userId) {String key = keyFor(userId);while (true) {jedis.watch(key);int value = Integer.parseInt(jedis.get(key));value *= 2; // 加倍Transaction tx = jedis.multi();tx.set(key, String.valueOf(value));List<Object> res = tx.exec();if (res != null) {break; // 成功了}}return Integer.parseInt(jedis.get(key)); // 重新獲取余額}public static String keyFor(String userId) {return String.format("account_{}", userId);} } 我們常常聽說 Python 的代碼要比 Java 簡短太多,但是從這個例子中我們看到 Java的代碼比 python 的代碼也多不了多少,大約只多出 50% 思考 為什么 Redis 的事務不能支持回滾?

總結

以上是生活随笔為你收集整理的redis事务原理,使用,详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 国产精品福利小视频 | 日本少妇一区二区 | 好吊色视频988gao在线观看 | 久久亚洲综合 | 亚洲成人另类 | 啪啪啪毛片 | 日韩av一 | 精品国产1区 | 国产精品777 | 夜夜精品视频 | 美女扒开腿让男人捅 | 亚洲欧美日韩精品久久 | 日韩一级影视 | 午夜影院h| 浮力影院国产第一页 | 天天免费视频 | 日本一道在线 | 久久美女精品 | 国产日产精品一区二区 | 国产经典久久 | 婷婷色中文 | 又黄又爽网站 | 日韩免费影视 | 神马午夜嘿嘿 | 日本欧美三级 | 亚洲欧美国产精品专区久久 | 久久免费小视频 | 亚洲骚图 | 亚洲综合一二三 | 在线免费不卡视频 | 中文字幕一区二区三区精彩视频 | 国产爽爽视频 | 久久精品国产亚洲AV高清综合 | 少妇高清精品毛片在线视频 | 日韩制服在线 | 国产熟女高潮视频 | 麻豆出品 | 免费色网 | 日韩国产欧美精品 | 一区二区在线免费观看视频 | 成人免费高清在线观看 | 国产精品成人aaaaa网站 | 色爱AV综合网国产精品 | 桃色一区 | 对白刺激国产子与伦 | 香蕉视频免费 | 午夜国产一区二区 | 久久这里只有精品99 | 午夜国产视频 | 污视频免费在线观看网站 | 欧美日韩国产高清 | 538国产精品视频一区二区 | 在线免费观看网站入口在哪 | 亚洲激情偷拍 | 精品人妻中文无码av在线 | 男人干女人视频 | 精品国产av 无码一区二区三区 | 99在线播放 | 欧美日韩成人在线播放 | 日本xxxxxxxxx | 精品无码久久久久久久久 | 91亚洲国产成人精品一区 | 91成年人网站 | 国产aaaaaaa | 精品久久无码中文字幕 | 夜夜嗨国产 | 超碰在线个人 | 亚洲啪啪av | 美女脱裤子让男人捅 | 欧美日韩国产成人精品 | 五月花婷婷 | 香蕉视频A| 日本老熟妇毛茸茸 | 一级欧美在线 | sese欧美| 日韩av一卡二卡 | 草视频在线 | 成人国产精品免费 | 五月婷婷综| 亚洲情欲网 | 韩国黄色网址 | 亚洲最大激情网 | 狼人综合伊人 | 亚洲电影中文字幕 | 国产麻豆免费观看 | √8天堂资源地址中文在线 欧美精品在线一区二区 | 狠狠躁夜夜躁人 | 老司机在线看片 | 亚洲天堂网在线观看视频 | 精彩久久| 久久久久久久久久久久久久免费看 | 免费无遮挡无码永久在线观看视频 | 国产精品日韩一区二区 | 木下凛凛子av一区二区三区 | 欧美性生交xxxxx久久久 | 无套白嫩进入乌克兰美女 | 求个黄色网址 | 8x8ⅹ国产精品一区二区二区 | 日韩精品国产一区 |