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')