python redis事务_python redis事务源码及应用分析
在多個(gè)客戶端同時(shí)處理相同的數(shù)據(jù)時(shí),不謹(jǐn)慎的操作很容易導(dǎo)致數(shù)據(jù)出錯(cuò)。一般的關(guān)系型數(shù)據(jù)庫中有事務(wù)保證了數(shù)據(jù)操作的原子性,同樣Redis中也設(shè)置了事務(wù),可以理解為“將多個(gè)命令打包,然后一次性、按順序執(zhí)行”,防止數(shù)據(jù)出錯(cuò),同時(shí)提升性能。
Redis事務(wù)
關(guān)系型數(shù)據(jù)庫中,要使用事務(wù),首先向數(shù)據(jù)庫服務(wù)器發(fā)送BEGIN,然后執(zhí)行各個(gè)相互一致的寫操作和讀操作,最后,用戶可以選擇發(fā)送COMMIT來確認(rèn)之前所做的修改,或者發(fā)送ROLLBACK來放棄那些修改。
同樣,Redis中也有簡單的方法處理一連串相互一致的讀操作和寫操作。首先是以MULTI命令開始事務(wù),后續(xù)跟著一連串命令,最后以EXEC結(jié)束事務(wù)或者以DISCARD命令撤銷所有命令并結(jié)束事務(wù)。Redis官方文檔中對(duì)于事務(wù)以及事務(wù)常用命令做了詳細(xì)介紹。
事務(wù)原子性
Redis事務(wù)中的所有命令能夠保證被順序執(zhí)行(FIFO),中間不會(huì)被其他客戶端打斷。它本身應(yīng)對(duì)外部請(qǐng)求是單任務(wù)的,也是多線程安全的。關(guān)系型數(shù)據(jù)庫的事務(wù)具有原子性特點(diǎn),而Redis事務(wù)就沒法確定說是原子性的。看一下官方文檔中Errors inside a transaction一節(jié),事務(wù)中有兩種command errors:一種是在MULTI之后,EXEC執(zhí)行之前,由于命令的語法錯(cuò)誤或者服務(wù)器內(nèi)存不夠等原因,命令根本不會(huì)被放入暫存隊(duì)列,Redis會(huì)拒絕執(zhí)行接下來的EXEC操作,這和我們理解的原子性原理是一致的;
另外一種是在EXEC之后,當(dāng)隊(duì)列中的某條命令執(zhí)行失敗,Redis也會(huì)繼續(xù)執(zhí)行完其他命令,且不支持回滾,最關(guān)鍵的地方反而還不支持原子性!
Redis官方文檔中對(duì)其不支持回滾的特性也做了說明,Redis命令只會(huì)在語法錯(cuò)誤或者對(duì)某個(gè)Key執(zhí)行了不屬于該類型的相應(yīng)操作,而這些錯(cuò)誤應(yīng)該是在開發(fā)過程中就避免的 = =;Reids也正是因?yàn)椴恢С只貪L所以才能更簡單快速(好傲嬌~)
所以我們在使用Redis事務(wù)過程中一定要注意:Redis事務(wù)所指的原子性僅僅只針對(duì)將命令加入執(zhí)行隊(duì)列的過程,Redis事務(wù)不支持在命令執(zhí)行過程中的錯(cuò)誤回滾。《Redis設(shè)計(jì)與實(shí)現(xiàn)》中對(duì)Redis事務(wù)的ACID特性做了全面的講解。
Redis事務(wù)鎖Watch
Watch是Redis提供的事務(wù)鎖,是一種樂觀鎖。在MULTI命令之前執(zhí)行WATCH命令,當(dāng)EXEC提交之后,在實(shí)際執(zhí)行任何命令之前,如果發(fā)現(xiàn)被Watched的key值發(fā)生了改變,整個(gè)事務(wù)被丟棄,返回為空。
個(gè)人理解,使用Redis事務(wù)結(jié)合Watch命令,只是能保證在事務(wù)內(nèi)被watched的key不會(huì)被重復(fù)錯(cuò)誤修改,一定程度上能夠保證原子性,但也只是針對(duì)被watched的key。
Python Redis事務(wù)
python中通過管道Pipeline實(shí)現(xiàn)Redis事務(wù)。當(dāng)我們要使用事務(wù)時(shí),首先實(shí)例化一個(gè)Pipeline類,可以先實(shí)例化StrictRedis類,然后調(diào)用實(shí)例的pipeline()方法;也可以直接實(shí)例化Pipeline類。兩種方法都會(huì)創(chuàng)建BasePipeLine實(shí)例。Redis事務(wù)中對(duì)應(yīng)的MULTI, EXEC, DISCARD, WATCH操作都對(duì)應(yīng)到BasePipeLine中的相應(yīng)方法。#?1redis?=?Redis(host='127.0.0.1',?port=6379)
pipe?=?redis.pipeline()#?2pipe?=?Pipeline(host='127.0.0.1',?port=6379)
在python中使用事務(wù)的流程也基本不變,建立Pipeline以后,調(diào)用multi()方法,表示事務(wù)開始,然后依次執(zhí)行所有redis命令,然后調(diào)用execute()方法提交事務(wù)。同樣在事務(wù)開始之前,可以調(diào)用watch()方法監(jiān)控keys。try:
pipe.watch('key1')
pipe.multi()
pipe.set('key2',?2)
pipe.incr('key1')
pipe.set('key3',?3)
pipe.execute()except?redis.exceptions.WatchError:????#?發(fā)生watcherror時(shí)業(yè)務(wù)應(yīng)該怎樣處理,丟棄事務(wù)或者重試
pass
看一下python中execute()方法的源碼:def?execute(self,?raise_on_error=True):????????"Execute?all?the?commands?in?the?current?pipeline"
stack?=?self.command_stack????????if?not?stack:????????????return?[]????????if?self.scripts:????????????self.load_scripts()????????if?self.transaction?or?self.explicit_transaction:
execute?=?self._execute_transaction????????else:
execute?=?self._execute_pipeline
conn?=?self.connection????????if?not?conn:
conn?=?self.connection_pool.get_connection('MULTI',???????????????????????????????????????????????????????self.shard_hint)????????????#?assign?to?self.connection?so?reset()?releases?the?connection
#?back?to?the?pool?after?we're?done
self.connection?=?conn????????try:????????????return?execute(conn,?stack,?raise_on_error)
except?(ConnectionError,?TimeoutError)?as?e:
conn.disconnect()????????????if?self.watching:
raise?WatchError("A?ConnectionError?occured?on?while?watching?"
"one?or?more?keys")????????????return?execute(conn,?stack,?raise_on_error)????????finally:????????????self.reset()
命令執(zhí)行后,程序會(huì)捕獲ConnectionError和TimeoutError,當(dāng)連接超時(shí)并且沒有設(shè)置重試retry_on_timeout參數(shù),異常直接拋出,否則會(huì)進(jìn)行一次重試。最終調(diào)用reset()重置所有參數(shù)。
redis-benchmark
Pipeline能夠?qū)⒁欢衙钕仁占饋?#xff0c;再一起發(fā)送給Redis服務(wù)器處理,減少了和Redis的連接通信次數(shù)。但當(dāng)Pipeline中命令的數(shù)量過大,管道中所有命令和執(zhí)行結(jié)果會(huì)被緩存到Redis內(nèi)存,同時(shí)也會(huì)造成網(wǎng)絡(luò)通信變慢,得不償失。那么,Pipeline每次接收的命令數(shù)量是多少才能達(dá)到最優(yōu)的執(zhí)行效率(How many commands could redis-py pipeline have?)?
Redis提供了redis-benchmark命令,模擬N個(gè)客戶端同時(shí)發(fā)送M條命令,并返回執(zhí)行統(tǒng)計(jì)結(jié)果。我們可以通過參數(shù)設(shè)置模擬客戶端數(shù)量,請(qǐng)求總數(shù),是否使用Pipeline以及Pipeline中命令數(shù)量,指定命令類型等。
首先模擬無Pipeline情況下執(zhí)行情況,假設(shè)給定100000條請(qǐng)求,模擬get和set請(qǐng)求,執(zhí)行結(jié)果如下。普通情況下,平均每秒執(zhí)行102145.05條SET請(qǐng)求,平均每秒執(zhí)行98716.68條GET請(qǐng)求。$?redis-benchmark?-n?100000?-t?set,get?-q
SET:?102145.05?requests?per?second
GET:?98716.68?requests?per?second
加入Pipeline,每個(gè)Pipeline執(zhí)行100條命令,執(zhí)行結(jié)果如下。執(zhí)行效率顯著提高了,尤其是對(duì)于GET請(qǐng)求。$?redis28-benchmark?-n?100000?-t?set,get?-P?100?-q
SET:?552486.19?requests?per?second
GET:?800000.00?requests?per?second
如果把Pipeline的最大執(zhí)行命令數(shù)設(shè)置到10000,執(zhí)行結(jié)果如下。這種情況下,對(duì)效率沒有顯著提升了,而且如果每條命令數(shù)據(jù)所占的字節(jié)數(shù)變大,執(zhí)行效率更低。$?redis-benchmark?-n?100000?-t?set,get?-P?10000?-q
SET:?118764.84?requests?per?second
GET:?150602.42?requests?per?second
到底應(yīng)該把Pipeline的命令數(shù)設(shè)置為多大才合適,沒有確定的答案,有的人給的建議是100-1000,具體設(shè)置為多大需要在當(dāng)前Redis連接狀況,Redis內(nèi)存大小等基礎(chǔ)上不斷測試找到那個(gè)sweet spot。
作者:rainybowe
鏈接:https://www.jianshu.com/p/32bf08e885b0
總結(jié)
以上是生活随笔為你收集整理的python redis事务_python redis事务源码及应用分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python实现序列数据预处理_Pyth
- 下一篇: 华为ap配置_Win10频发蓝屏,深度D