如何在多线程程序中更高效地使用redis-py?

2 投票
2 回答
8320 浏览
提问于 2025-04-18 04:42

我有两个问题想问。

  1. 是创建一个全局的实例,然后在每个线程中重复使用,还是在每个线程中创建一个新的实例?

  2. 使用

    pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
    r = redis.Redis(connection_pool=pool)

    还是

    r = redis.StrictRedis(host='localhost', port=6379, db=0)

    文档中提到关于连接池的内容:你可以选择这样做,以便实现客户端分片或更细粒度地控制连接的管理。但我不太明白这里的客户端分片指的是什么。

更新
如果使用连接池,下面哪种方式是正确的?
A:

pool = redis.ConnectionPool(host='localhost', port=6379, db=0)

class DownloadThread(threading.Thread):
    def __init__(self,pool):
        threading.Thread.__init__(self)
        self.r = redis.Redis(connection_pool=pool)
    def run(self):
        while True:
            self.r .....

B:

pool = redis.ConnectionPool(host='localhost', port=6379, db=0)
r = redis.Redis(connection_pool=pool)

class DownloadThread(threading.Thread):
    def __init__(self,r):
        threading.Thread.__init__(self)
        self.r = r
    def run(self):
        while True:
            self.r .....

2 个回答

2

好吧,这里有几个问题,我会分别回答每一个。

  1. 如果你打算使用线程,那么你肯定需要每个线程都有自己的连接。因为如果多个线程共享同一个Redis连接,就会出现问题,比如一个线程在发送请求,而另一个线程在读取数据,这样就可能会出错(除非你使用互斥锁来控制)。我觉得连接池的实现是线程安全的,所以你应该选择使用连接池,或者自己创建一个连接池,可以用像queue.Queue这样的东西,把每个连接放进去,线程就可以从队列中取出和放入连接。

  2. 客户端分片是一种“穷人的”方式,用来在多个Redis实例上分散数据。通常的做法是对目标键应用某种哈希算法,比如crc32,然后用这个值对分片的数量取模。举个例子,可能会像这样:

--

>>> binascii.crc32("foo") % 3
1
>>> binascii.crc32("bar") % 3
2
>>> binascii.crc32("baz") % 3
0

这里假设我们有3个分片(或者说是3个独立的Redis服务器实例)。键foo在第二个分片(索引1)上,键bar在第三个分片(索引2)上,而baz则在第一个分片上。

Redis集群(在Redis 3.0.0的测试版中)旨在实现服务器端的分片,而不是客户端的分片。

3

Redis现在是线程安全的:https://github.com/andymccurdy/redis-py

Redis的客户端实例可以在多个线程之间安全地共享。内部在执行命令时,连接实例只会从连接池中获取,执行完后又直接放回连接池。执行命令的过程不会改变客户端实例的状态。

每个Redis()实例会自动创建一个连接池。

撰写回答