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