python redis-py模块使用详解

redis-py驱动使用详解

说明: 本文基于3.3.8版本redis-py,代码基于python3.7。文章着重介绍redis-py的使用,试图从底层介绍redis-py的操作,不会过多介绍如何用redis-py如何使用Redis命令

安装

redis-py可以直接使用pip进行安装:

$ pip install redis

使用

基本使用

redis-py的使用很简单,只需要用redis.Redis建立连接,即可对Redis进行操作。例如:

import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.set('foo', 'bar')
r.get('foo')

默认情况下,Python3返回bytes,Python2返回str

多线程使用

redis-py是线程安全的Redis客户端,可以放心的在多线程代码中使用。

import redis
import threading


class RedaisExexThread(threading.Thread):

    def __init__(self, redis_client):
        threading.Thread.__init__(self)
        self.redis_client = redis_client

    def run(self):
        res = self.redis_client.get('foo')
        print(res)


def main():
    redis_client = redis.Redis(host='localhost', port=6379, db=0)

    thread1 = RedaisExexThread(redis_client)
    thread2 = RedaisExexThread(redis_client)
    thread3 = RedaisExexThread(redis_client)
    thread4 = RedaisExexThread(redis_client)

    thread1.start()
    thread2.start()
    thread3.start()
    thread4.start()


if __name__ == '__main__':
    main()
关于SELECT操作

redis-py中,并没有为Redis客户端实例实现SELECT操作,也就是如果要使用Redis其他库时,需要新建另一个Redis实例。这是由于Redis采用连接池维护Redis连接。在多线程时,若允许SELECT操作可能导致这个连接已经切换到其他Redis DB上;并且切换后连接直接进入连接池,而没有销毁。这可能导致操作的DB不是预期的DB。因此为了保证线程安全,Redis客户端不支持SELECT操作。

自定义连接池操作

使用Redis客户端时,支持直接传入一个Redis连接池,使用起来更加灵活。

import redis

def main():
    p = redis.ConnectionPool(host='localhost', port=6379, db=0)
    r = redis.Redis(connection_pool=p)

    res = r.get('foo')
    print(res)


if __name__ == '__main__':
    main()

使用阻塞连接池

前面介绍了Redis客户端可以通过连接池方式初始化,在redis-py中提供了多种连接池以满足实际需求,这里介绍使用阻塞连接池(BlockingConnectionPool)操作Redis。

阻塞连接池的特点是:当连接池中没有空闲的连接时,会等待timeout秒,直到获取到连接或超时报错。

import redis
import threading


class RedaisExexThread(threading.Thread):

    def __init__(self, redis_client):
        threading.Thread.__init__(self)
        self.redis_client = redis_client

    def run(self):
        res = self.redis_client.get('foo')
        print(res)


def main():
    pool = redis.BlockingConnectionPool(host='localhost', port=6379, db=0, max_connections=2, timeout=5)
    redis_client = redis.Redis(connection_pool=pool)

    thread1 = RedaisExexThread(redis_client)
    thread2 = RedaisExexThread(redis_client)
    thread3 = RedaisExexThread(redis_client)
    thread4 = RedaisExexThread(redis_client)
    thread5 = RedaisExexThread(redis_client)
    thread6 = RedaisExexThread(redis_client)
    thread7 = RedaisExexThread(redis_client)
    thread8 = RedaisExexThread(redis_client)
    thread9 = RedaisExexThread(redis_client)
    thread10 = RedaisExexThread(redis_client)

    thread1.start()
    thread2.start()
    thread3.start()
    thread4.start()
    thread5.start()
    thread6.start()
    thread7.start()
    thread8.start()
    thread9.start()
    thread10.start()


if __name__ == '__main__':
    main()

这里创建了一个阻塞连接池,其容量为2,超时等待时间为5秒。用10个线程并发的方式测试阻塞连接池,可以正常使用。感兴趣的可以用普通连接池测试下,看会出现什么问题。

另外根据文档描述,这个阻塞连接池可以给多个客户端使用,实际测试也是可以的。感兴趣可以自己试下。


Redis客户端(Redis 类)

Redis类是redis-py中与Redis服务器交互的类,数据库的操作都Redis类来实现,其是Redis协议的一个实现,也是Redis连接池的管理者。

其提供两种连接方式:

  • TCP连接:默认的连接方式
  • 本地socket(unix domain socket)连接:通过本机的文件socket连接

redis-py在与Redis数据库的连接层面采用连接池(Connection Pools)。Redis连接池由Redis客户端(即Redis类)来管理,默认情况下每个Redis客户端实例都维护了一个独立的Redis连接池。除此之外也可以通过指定Redis客户端实例的connection_pool参数的方式个性化定义Redis连接池,这样可以实现客户端分片,或者更好的管理连接等功能。

参数介绍

Redis客户端的参数较多,这里简单介绍下参数的默认值及功能。

参数 默认值 描述
host 'localhost' 域名
port 6379 端口
db 0 连接数据库
password None 密码
socket_timeout None float类型。 None - 一直阻塞读取数据,不会超时; 0.0 - 非阻塞模式,会报错,不建议使用; 非0 socket阻塞时间,超时会报错
socket_connect_timeout None 建立socket连接的超时时间,默认为socket_timeout
socket_keepalive None 是否启用TCP KEEPALIVE机制
socket_keepalive_options None TCP KEEPALIVE参数
connection_pool None Redis连接池,默认使用ConnectionPool
unix_socket_path None 以本地socket方式连接时,socket文件路径
encoding 'utf-8' 编码方式
encoding_errors 'strict' 出现编解码错误时的处理方案
charset None 已废弃,作用等同encoding
errors None 已废弃,作用等同encoding_errors
decode_responses False 是否对返回解码
retry_on_timeout False 是否超时重试
ssl False 启用SSL
ssl_keyfile None
ssl_certfile None
ssl_cert_reqs 'required'
ssl_ca_certs None
max_connections None 最大连接数
single_connection_client False 单连接模式,非连接池连接
health_check_interval 0 健康检测间隔时间

客户端初始化

Redis客户端是操作Redis数据库的基础,Redis客户端提供多种初始化方式,以便灵活使用。下面介绍几种访问方式:

  1. 基本访问:最基本的访问方式为,通过指定host, port, password, db的方式访问。
import redis
r = redis.Redis(host='localhost', port=6379, db=0)

print(r.set('foo', 'bar'))
print(r.get('foo'))
  1. 指定connect pool访问:通过实例化connect pool,将connect pool实例传入到Redis中实现访问。
import redis

pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)   # 注意这里使用的connection_pool

print(r.set('foo', 'bar'))
print(r.get('foo'))
  1. 通过文件socket方式连接:Reidis类支持通过Unix Domain Socket的方式连接Redis服务器
import redis

r = redis.Redis.from_url(unix_socket_path='/tmp/redis.sock')

print(r.set('foo', 'bar'))
print(r.get('foo'))
  1. 通过url访问:Redis类还支持通过URL直接访问。
import redis

redis_url = "redis://@localhost:6379/0" 
r = redis.Redis.from_url(redis_url)

print(r.set('foo', 'bar'))
print(r.get('foo'))

这里的url格式可以为:

  • redis://[:password]@localhost:6379/0 普通TCP方式连接
  • rediss://[:password]@localhost:6379/0 SSL TCP方式连接
  • unix://[:password]@/path/to/socket.sock?db=0 本地文件socket方式连接

回调(Response Callback)

通过Redis客户端操作Redis数据库时,执行命令的返回结果都会被回调函数处理,称这个回调函数为Response CallbackRedis 客户端中定义了对于每个命令默认的回调函数。

例如SET命令默认的回调函数为bool_ok。其内容如下:

def bool_ok(response):    
    return nativestr(response) == 'OK'

回调函数的作用是将操作Redis的返回值转换为Python相对容易处理的数据类型,如:bool、dict、list等。

通常情况下默认的回调函数可以满足使用需求,但是在某些特殊需求下,可以通过set_response_callback方法修改某个命令的回调。

这里修改了SET命令的默认回调函数,使其返回01:

import redis
from redis._compat import nativestr

def int_ok(response):
    return int(nativestr(response) == 'OK')

r = redis.Redis(host='localhost')
r.set_response_callback('SET', int_ok)

print(r.set('foo', 'bar'))
print(r.get('foo'))

执行后输出的结果为:

1
b'bar'

单连接模式(single connection client)

使用Redis客户端实现数据库交互时,默认是使用连接池的进行连接管理。

除此之外Redis客户端也提供了单连接方式进行数据库交互。通过配置single_connection_client参数实现单连接交互,但底层上也是通过维护单个连接的连接池实现。

连接池(ConnectionPool 类)

redis-py中所有与数据库交互的连接都通过ConnectionPool维护。其实现的功能有:url访问串解析、连接维护(包括:分配连接、申请新连接、关闭连接等)、编解码器维护等。

除了标准的ConnectionPool连接池外,redis-py也提供了非阻塞连接池BlockingConnectionPool和哨兵连接池SentinelConnectionPool

参数介绍

参数 默认值 描述
connection_class Connection 连接实例类,用于初始化数据库连接
max_connections None 连接池的容量,默认为 2 ** 32,即4294967296
**connection_kwargs 连接参数,即Redis中大部分的参数

连接维护

ConnectionPool类最重要的功能是其中维护了Redis连接池。其实现了连接获取(get_connection),释放连接(release),创建连接(make_connection),关闭连接(disconnect),重置(reset)等一系列工作。其使用Python中的list作为连接池,通过list的poppush等操作完成连接池的维护。

URL访问串解析

以URL方式访问数据库时,URL串的解析工作最终是由ConnectionPool类实现,解析函数为from_url

编解码器维护

ConnectionPool中提供get_encoder函数,通过此函数可以根据客户端(Redis)或连接池(ConnectionPool)对应的编码参数生成编解码器(Encoder)。

非阻塞的连接池(BlockingConnectionPool 类)

BlockingConnectionPool是一个线程安全的非阻塞连接池。其基本使用和普通的连接池一致,只不过在其内部重新实现了对连接池的管理。

其连接池中默认的连接数量为50;并且增加了一个timeout参数,其默认值为20,当其设置为None代表无限等待下去。

当连接池中的连接全部被使用时,其不会直接报错,而是会最多等待timeout秒,若还是没有可用连接,则会报错。

BlockingConnectionPool类,连接默认采用LifoQueue即队列维护,这个队列就是连接池。

连接(Connection 类)

Connection类用于管理与Redis Server之间的TCP连接。连接在此层面进行维护。

除了基础的Connection外,redis-py也提供了SSL版本的SSLConnection以及Unix Domain Socket版本的UnixDomainSocketConnection

redis-py在此层面维护了TCP Socket连接、编解码器(Encoder)、Redis协议分析器(parser)、对命令封包(pack command)以及连接健康校验等功能。

连接管理

Connection类实现了对TCP连接的管理。包括connecton_connect_connectdisconnect等方法的实现。

  • connect:连接到Redis服务器。包括socket连接建立及连接初始化。
  • on_connect:初始化连接,包括初始化Redis协议分析器、账号认证(AUTH)以及库切换(SELECT)。
  • _connect:创建TCP Socket连接,并配置socket。
  • disconnect:断开与Redis服务器之间的连接。

健康检查(check health)

健康检查的功能是测试当前的连接是否可用,检测通过PING/PONG命令实现。

检查间隔(health_check_interval)的含义是当连接在health_check_interval秒内没有使用,下次使用时需要进行健康检查。

命令封包(pack command)

为了满足Redis协议,需要对待执行的命令进行封包,具体就是将命令串转换为Redis协议要求的格式。

编解码器(Encoder)

编解码器的功能是对输入的命令进行编码;对命令执行结果进行解码。redis-py的编解码器维护在连接层面。每个连接有一个独立的编解码器。

协议分析器(parser)

协议分析器用来读取Rrdis服务器的返回、解析Redis服务器的响应。每个连接维护了一个独立的协议分析类的实例。

redis-py中有两个parser可供选择,分别为PythonParserHiredisParser。默认情况下redis-py会优先使用HiredisParser;若其没有安装,则会选用PythonParser替代。

Hiredis是Redis团队维护的C库。Pieter Noordhuis大神(Redis主要代码贡献者)提供了Python版本的实现。据说使用Hiredis会将解析速度提升10倍,尤其是在使用LRANGESMEMBERS检索许多数据时效率极高。默认情况下,安装redis-py不会安装Hiredis,可以使用pip方式安装:

$ pip install hiredis

你可能感兴趣的:(python redis-py模块使用详解)