Python/Redis多处理

alb*_*sun 5 python multiprocessing redis

我正在使用多处理库中的Pool.map迭代一个大型XML文件,并将word和ngram计数保存到一组三个redis服务器中.(它完全位于内存中)但由于某种原因,所有4个cpu内核在整个时间内都处于空闲状态.服务器有足够的RAM和iotop显示没有磁盘IO发生.

我有3个python线程和3个redis服务器在三个不同的端口上作为守护进程运行.每个Python线程都连接到所有三个服务器.

每台服务器上的redis操作数远低于它的基准测试数.

我找不到这个程序的瓶颈?什么可能是候选人?

小智 5

网络延迟可能会导致您的python客户端应用程序中的空闲CPU时间增加。如果客户端到服务器之间的网络延迟仅为2毫秒,并且您执行了10,000个redis命令,则无论任何其他组件的速度如何,您的应用程序都必须处于空闲状态至少20秒。

使用多个python线程可以提供帮助,但是当向服务器发送阻止命令时,每个线程仍将处于空闲状态。除非您有很多线程,否则它们通常会同步,并且所有线程都阻塞等待响应。因为每个线程都连接到所有三台服务器,所以发生这种情况的可能性降低了,除非所有线程都被阻止等待同一台服务器。

假设您在服务器之间具有统一的随机分布式访问权限以服务您的请求(通过对键名称进行哈希处理以实现分片或分区),那么三个随机请求将哈希到同一Redis服务器的几率与服务器数量成反比。对于1台服务器,您有100%的时间会哈希到同一台服务器,其中2台是50%的时间,而3台是33%的时间。可能发生的情况是,有1/3的时间,所有线程都被阻塞,等待同一台服务器。Redis是处理数据操作的单线程,因此它必须一个接一个地处理每个请求。您观察到您的CPU使用率仅达到60%,这与您的请求在同一服务器的网络延迟上全部被阻止的可能性相一致。

继续假设您通过对键名称进行散列来实现客户端分片,可以通过为每个线程分配单个服务器连接来消除线程之间的争用,并在将请求传递给工作线程之前评估分区散列。这将确保所有线程都在等待不同的网络延迟。但是通过使用流水线可能会有更好的改进。

如果您不需要服务器的即时结果,可以使用redis-py模块的管道功能来减少网络延迟的影响。这对您来说可能是可行的,因为您似乎将数据处理的结果存储到redis中。要使用redis-py阻止这种情况,请使用方法定期获取现有redis连接对象的管道句柄,.pipeline()并针对该新句柄调用多个存储命令,就像对主redis.Redis连接对象的调用一样。然后调用.execute()以阻止答复。通过使用流水线将数十个或数百个命令一起批处理,您可以得到数量级的改进。.execute()在对管道句柄发出最终方法之前,您的客户端线程不会阻塞。

如果您同时应用这两个更改,并且每个工作线程仅与一台服务器通信,将多个命令一起流水传输(至少5-10个才能看到明显的效果),则客户端中的CPU使用率可能更高(接近100%)。cpython GIL仍将客户端限制为一个内核,但是听起来您已经通过使用多处理模块将其他内核用于XML解析。

在redis.io网站上有关于流水线很好的文章。