摘要:當一個事務執行完畢之后,才會處理其他客戶端的命令。延遲執行事務有助于提升性能因為在執行事務的過程中,會延遲執行已入隊的命令直到客戶端發送命令為止。
Redis的基本事務(basic transaction)需要用到MULTI命令和EXEC命令,這種事務可以讓一個客戶端在不被其他客戶端打斷的情況下執行多個命令。被NULTI命令和EXEC命令包圍的所有命令會一個接一個地執行,直到所有命令都執行完畢為止。當一個事務執行完畢之后,Redis才會處理其他客戶端的命令。
當Redis從一個客戶端那里接收到MULTI命令時,Redis會將這個客戶端之后發送的所有命令都放入到一個隊列里面,直到這個客戶端發送EXEC命令為止,然后Redis就會在不被打斷的情況下,一個接一個地執行存儲在隊列里面的命令。從語義上來說,Redis事務在Python客戶端上面是由流水線(pipeline)實現的:對連接對象調用pipeline()方法將創建一個事務,在一切正常的情況下,客戶端會自動地使用MULTI和EXEC包裹起用戶輸入的多個命令。為了減少Redis與客戶端之間的通信往返次數,提升執行多個命令時的性能,Python的Redis客戶端會存儲起事務包含的多個命令,然后在事務執行時一次性地將所有命令都發送給Redis。
在Python中使用事務來處理命令的并行執行問題:
def trans(): pipeline = conn.pipeline() # 創建事務型流水線對象 pipeline.incr("trans:") # 把針對"trans:"計數器的自增操作放入隊列 time.sleep(.1) # 等待100ms pipeline.incr("trans:", -1) # 把針對"trans:"計數器的自減操作放入隊列 print pipeline.execute()[0] # 執行被事務包裹的命令,并打印自增操作的執行結果 if 1: for i in xrange(3): # 啟動3個線程來執行被事務包裹的自增、休眠和自減3個操作 threading.Thread(target=trans).start() time.sleep(.5) # 等待500ms,讓操作有足夠的時間完成 # 打印結果: 1 1 1
Redis要在接收到EXEC命令之后,才會執行哪些位于MULTI和EXEC之間的入隊命令。
上述這種簡單的事務在EXEC命令被調用之前不會執行任何實際操作,所以用戶將沒辦法根據讀取到的數據來做決定。這種方式無法以一致的形式讀取數據將導致某一類型的問題變得難以解決,除此之外,因為在多個事務同時處理同一個對象時通常需要用到二階提交(two-phase commit), 所以如果事務不能以一致的形式讀取數據,那么二階提交將無法實現,從而導致一些原本可以成功執行的事務執行失敗。
延遲執行事務有助于提升性能
因為Redis在執行事務的過程中,會延遲執行已入隊的命令直到客戶端發送EXEC命令為止。包括python客戶端在內的很多Redis客戶端都會等到事務包含的所有命令都出現了之后,才一次性地將MULTI命令、要在事務中執行的一系列命令,以及EXEC命令全部發送給Redis,然后等待直到接收到所有命令的回復為止。這種“一次性發送多個命令,然后等待所有回復出現”的做法通常被稱為流水線(pipeline),它可以通過減少客戶端與Redis服務器之間的網絡通信次數來提升Redis在執行多個命令時的性能。
在用戶使用WATCH命令對鍵進行監視之后,直到用戶執行EXEC命令的這段時間,如果有其他客戶端搶先對任何被監視的鍵進行了替換、更新或刪除等操作,那么當用戶嘗試執行EXEC命令的時候,事務將失敗并返回一個錯誤(之后選擇重試事務或者放棄事務)。
UNWATCH命令可以在WATCH命令執行之后、MULTI命令執行之前對連接進行重置(reset);同樣地,DISCARD命令也可以在MULTI命令執行之后、EXEC命令執行之前對連接進行重置。這也就是說,用戶在使用WATCH監視一個或多個鍵,接著使用MULTI開始一個新的事務,并將多個命令入隊到事務隊列之后,仍然可以通過發送DISCARD命令來取消WATCH命令并清空所有已入隊命令。
將商品放到市場上銷售:
def list_item(conn, itemid, sellerid, price): inventory = "inventory:%s"%sellerid # 商家包裹 item = "%s.%s"%(itemid, sellerid) end = time.time() + 5 pipe = conn.pipeline() while time.time() < end: try: pipe.watch(inventory) # 監視商家包裹發生的變化 if not pipe.sismember(inventory, itemid): # 檢查商家是否仍然持有將要被銷售的商品 pipe.unwatch() return None pipe.multi() pipe.zadd("market:", item, price) # 將出售的商品添加到買賣市場 pipe.srem(inventory, itemid) pipe.execute() # 執行execute沒有引發WatchError異常,說明事務執行成功,并且對包裹鍵的監視也已經結束 return True except redis.exceptions.WatchError: # 商家的包裹已經發生變化,重試 pass return False
購買商品:
def purchase_item(conn, buyerid, itemid, sellerid, lprice): buyer = "users:%s"%buyerid seller = "users:%s"%sellerid item = "%s.%s"%(itemid, sellerid) inventory = "inventory:%s"%buyerid end = time.time() + 10 pipe = conn.pipeline() while time.time() < end: try: pipe.watch("market:", buyer) # 對商品買賣市場以及買家的個人信息進行監視 # 檢查購買的商品價格是否發生變化,以及賣家是否有足夠的錢購買 price = pipe.zscore("market:", item) funds = int(pipe.hget(buyer, "funds")) if price != lprice or price > funds: pipe.unwatch() return None pipe.multi() pipe.hincrby(seller, "funds", int(price)) pipe.hincrby(buyer, "funds", int(-price)) pipe.sadd(inventory, itemid) pipe.zrem("market:", item) pipe.execute() return True except redis.exceptions.WatchError: pass return False
加鎖有可能造成長時間的等待,所以Redis為了盡可能地減少客戶端的等待時間,并不會在執行WATCH命令時對數據進行加鎖。相反,Redis只會在數據已經被其他客戶端搶先修改了的情況下,通知執行WATCH命令的客戶端,這種做法稱為樂觀鎖(optimistic locking),而關系型數據庫實際執行的加鎖操作則被稱為悲觀鎖(pessimistic locking)。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/44556.html
摘要:如何使用操作詳解簡介是一個開源許可的,內存中的數據結構存儲系統,它可以用作數據庫緩存和消息中間件。解決辦法是即使查出的對象為空,也放入緩存時間設短一點。緩存雪崩,是指在某一個時間段,緩存集中過期失效。 如何使用StringRedisTemplate操作Redis詳解 Redis簡介 Redis 是一個開源(BSD許可)的,內存中的數據結構存儲系統,它可以用作數據庫、緩存和消息中間件。支...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 3504·2021-10-18 13:30
閱讀 2956·2021-10-09 09:44
閱讀 1972·2019-08-30 11:26
閱讀 2305·2019-08-29 13:17
閱讀 769·2019-08-29 12:17
閱讀 2259·2019-08-26 18:42
閱讀 482·2019-08-26 13:24
閱讀 2964·2019-08-26 11:39