redis-py提供兩個類Redis和StrictRedis用於實現Redis的命令,StrictRedis用於實現大部分官方的命令,並使用官方的語法和命令(好比,SET命令對應與StrictRedis.set方法)。Redis是StrictRedis的子類,用於向後兼容舊版本的redis-py。python
StrictRedis:redis
select#沒有實現,參見下面線程安全小節數據庫
del#與python的del衝突,用delete代替緩存
CONFIG GET|SET#config_get config_set安全
MULTI/EXEC#被當作Pipeline的一部分,執行時默認被 MULTI 和EXEC狀態包裹,能夠用transaction=False 關閉SUBSCRIBE/LISTEN#和Pipeline相似,PubSub做爲單獨的類實現,它生成潛在的鏈接狀態,此時沒法執行非pubsub命令,在redis-client端執行PubSub會返回一個pubsub實例,能夠用它來訂閱頻道,監聽信息,注意只能在client端執行服務器
SCAN/SSCAN/HSCAN/ZSCAN#*scan命令在redis文件中實現,每一個方法都有一個迭代器方法,這樣純粹時爲了方便用戶,不用記錄迭代器的遊標,用scan_iter/sscan_iter/hscan_iter/zscan_iter實現框架
StrictRedis的子類Redis重寫了一些方法實現了後向兼容異步
LREM#num和value順序顛倒了,以便num能夠提供缺省值0socket
ZADD#在value前指明score,實現時意外交換了順序,人們已經使用時才發現,Redis指望*args格式是name1, score1, name2, score2, …
SETEX#time和value的順序顛倒tcp
redis使用鏈接池管理與redis-server的鏈接,默認每一個redis實例會建立本身的鏈接池,能夠用已經存在的鏈接池覆蓋實例redis的connection_pool參數來覆蓋這一行爲,你能夠經過這種方式實現客戶端的切分或鏈接池的管理
>>> pool = redis.ConnectionPool(host='localhost', port=6379, db=0) >>> r = redis.Redis(connection_pool=pool)
ConnectionPools管理着鏈接實例的集合,redis實線兩種類型的鏈接,默認使用基於tcp socket的鏈接,
UnixDomainSocketConnection 容許和服務器運行在同一個設備上的客戶端經過 unix 套接字進行鏈接。要使用 UnixDomainSocketConnection 鏈接, 只須要經過unix_socket_path 參數傳遞一個 unix 套接字文件的字符串。另外,確保redis.conf 文件配置了unixsocket 參數(缺省狀況下是註釋掉的):
>>> r = redis.Redis(unix_socket_path='/tmp/redis.sock')
也能夠本身建立 Connection 子類。這個特性能夠在使用異步框架時用於控制 socket 的行爲。要使用本身的Connection 初始化客戶端類,須要建立一個鏈接池,通 connection_class 參數把本身的類傳遞進去。傳遞的其它關鍵字參數會在初始化時傳遞給自定義的類:
>>> pool = redis.ConnectionPool(connection_class=YourConnectionClass, your_arg='...', ...)
解析器
解析類提供了控制如何對 Redis 服務器的響應進行解析的途徑。redis-py 提供了兩個解析類, PythonParser和 HiredisParser。缺省狀況下,若是安裝了 hiredis 模塊, redis-py 會嘗試使用 HiredisParser,不然使用 PythonParser。
Hiredis 是由 Redis 核心團隊維護的 C 庫。 Pieter Noordhuis 建立了 Python 的實現。分析 Redis 服務器的響應時,Hiredis 能夠提供 10 倍的速度提高。性能提高在獲取大量數據時優爲明顯,好比 LRANGE 和SMEMBERS 操做。
和 redis-py 同樣,Hiredis 在 Pypi 中就有,能夠經過 pip 或 easy_install 安裝:
$ pip install hiredis
或:
$ easy_install hiredis
響應回調函數
客戶端類使用一系列回調函數來把 Redis 響應轉換成合適的 Python 類型。不少回調函數在 Redis 客戶端類的字典 RESPONSE_CALLBACKS 中定義。
經過 set_response_callback 方法能夠把自定義的回調函數添加到單個實例。這個方法接受兩個參數:一個命令名和一個回調函數。經過這種方法添加的回調函數只對添加到的對象有效。要想全局定義或重載一個回調函數,應該建立 Redis 客戶端的子類並把回調函數添加到類的 RESPONSE_CALLBACKS
響應回調函數至少有一個參數:Redis 服務器的響應。要進一步控制如何解釋響應,也可使用關鍵字參數。這些關鍵字參數在對 execute_command 的命令調用時指定。經過 「withscores」 參數,ZRANGE 演示了回調函數如何使用關鍵字參數。
線程安全
Redis 客戶端實例能夠安全地在線程間共享。從內部實現來講,只有在命令執行時才獲取鏈接實例,完成後直接返回鏈接池,命令永不修改客戶端實例的狀態。
可是,有一點須要注意:SELECT 命令。SELECT 命令容許切換當前鏈接使用的數據庫。新的數據庫保持被選中狀態,直到選中另外一個數據庫或鏈接關閉。這會致使在返回鏈接池時,鏈接可能指定了別的數據庫。
所以,redis-py 沒有在客戶端實例中實現 SELECT 命令。若是要在同一個應用中使用多個 Redis 數據庫,應該給第一個數據庫建立獨立的客戶端實例(可能也須要獨立的鏈接池)。
在線程間傳遞 PubSub 和 Pipeline 對象是不安全的。
Pipeline
Pipeline 是 Redis 基類的一個子類,支持在一個請求裏發送緩衝的多個命令。經過減小客戶端和服務器之間往來的數據包,能夠大大提升命令組的性能。
Pipeline 的使用很是簡單:
>>> r = redis.Redis(...) >>> r.set('bing', 'baz') >>> # 使用 pipeline()方法建立 pipeline 對象 >>> pip3 = r.pipeline() >>> # 下面的set方法被緩存 >>> pipe.set('foo', 'bar') >>> pipe.get('bing') >>> # 調用execute發送全部緩存命令到服務器端,並返回響應的list,每一個命令對應着list的一項 >>> pipe.execute() [True, 'baz'] 爲了方便使用,全部緩衝到 pipeline 的命令返回 pipeline 對象自己。所以調用能夠鏈式鏈接: >>> pipe.set('foo', 'bar').sadd('faz', 'baz').incr('auto_number').execute() [True, True, 6]
另外,pipeline 也能夠保證緩衝的命令組作爲一個原子操做。缺省就是這種模式。要使用命令緩衝,但禁止pipeline 的原子操做屬性,能夠關掉 transaction:
>>> pipe = r.pipeline(transaction=False)
一個常見的問題是:在進行原子事務操做前須要從 Redis 中獲取事務中要用的數據。好比,假設 INCR 命令不存在,但咱們須要用 Python 建立一個原子版本的 INCR。
一個不成熟作法是先獲取值(GET),在 Python 中加1, 設置(SET)新值。可是,這不是原子操做,由於多個客戶端可能在同一時間作這件事,每個都經過 GET 獲取同一個值。
WATCH 命令提供了在開始事務前監視一個或多個鍵的能力。若是這些鍵中的任何一個在執行事務前發生改變,整個事務就會被取消並拋出 WatchError 異常。要實現咱們的客戶 INCR 命令,能夠按下面的方法操做:
>>> with r.pipeline() as pipe: ... while 1: ... try: ... # 對序列號的鍵進行 WATCH ... pipe.watch('OUR-SEQUENCE-KEY') ... # WATCH 執行後,pipeline 被設置成當即執行模式直到咱們通知它 ... # 從新開始緩衝命令。 ... # 這就容許咱們獲取序列號的值 ... current_value = pipe.get('OUR-SEQUENCE-KEY') ... next_value = current_value + 1 ... # 如今咱們能夠用 MULTI 命令把 pipeline 設置成緩衝模式 ... pipe.multi() ... pipe.set('OUR-SEQUENCE-KEY', next_value) ... # 最後,執行 pipeline (set 命令) ... pipe.execute() ... # 若是執行時沒有拋出 WatchError,咱們剛纔所作的確實「原子地」 ... # 完成了 ... break ... except WatchError: ... # 必定是其它客戶端在咱們開始 WATCH 和執行 pipeline 之間修改了 ... # 'OUR-SEQUENCE-KEY',咱們最好的選擇是重試 ... continue
注意,由於在整個 WATCH 過程當中,Pipeline 必須綁定到一個單鏈接,必須調用 reset() 方法確保鏈接返回鏈接池。若是 Pipeline 用做上下文管理器(如上面的例子所示,with r.pipeline() as pipe:), reset() 會自動調用。固然,也能夠用手動的方式明確調用 reset():
>>> pipe = r.pipeline() >>> while 1: ... try: ... pipe.watch('OUR-SEQUENCE-KEY') ... ... ... pipe.execute() ... break ... except WatchError: ... continue ... finally: ... pipe.reset()
注:·WATCH 執行後,pipeline 被設置成當即執行模式
·用 MULTI 命令把 pipeline 設置成緩衝模式
存在一種方便的方法「transaction」用來處理WatchError的處理和重試的模式。它調用含有一個參數的可執行對象,一個管道對象,和任意數量要 WATCH的鍵,其中可執行對象接受一個 pipeline 對象作爲參數。上面的客戶端 INCR 命令能夠重寫以下(更易可讀):
>>> def client_side_incr(pipe): ... current_value = pipe.get('OUR-SEQUENCE-KEY') ... next_value = current_value + 1 ... pipe.multi() ... pipe.set('OUR-SEQUENCE-KEY', next_value) >>> r.transaction(client_side_incr, 'OUR-SEQUENCE-KEY')