12.用Redis pipeline提高性能
场景
最近用 Faiss 做向量相似度搜索,Faiss 的自定义 ID 只能使用 int64,但实际中,语料的 ID 可能不是整型,这就涉及到 id 映射的问题。 为了加快映射速度,redis 是首选,如果选择 key-value 结构,当 knn 比较多时,显然访问 redis 次数太多,加上正常的网络延迟,几乎没有性能可言;如果选择 hashmap 结构,虽然访问一次就够了,但此时数据回被打到一个 redis 实例里,显然数据量比较大时,会出问题。 因此,redis pipeline 是比较好的解决方法。
pipeline
redis 的 pipeline 功能在命令行中没有,但在各个编程语音的 client 中都有相应的实现。 pipeline 在某些场景非常有用,能节省 TCP 连接中 “交互往返” 的时间。TCP 具体交互过程不多说。例如,你想向集合 sadd 多个值,再向 list 追加一个值,最后从该 list 返回指定长度的值。如下所示:
redis.sadd('seta', 1)
redis.sadd('seta', 2)
redis.lpush('lista', 1)
redis.lrange('lista', 0, -1)
当使用pipeline时,你可以这样:
redis.sadd('seta', 1).sadd('seta', 2).lpush('lista', 1).lrange('lista', 0, -1)
pipeline 相当于 “批处理”,在一定程度上,可以较大的提升性能。 注意:pipeline 期间将 “独占” 链接,此时将不能今昔非 “管道” 类型的其他操作,直到 pipeline 关闭。如果你的 pipeline 的指令集很庞大,为了不干扰链接中的其他操作,你可以为 pipeline 操作新建 Client 链接,让 pipeline 和其他正常操作分开。 pipeline 能容忍的操作个数,与 socket-output 缓存区大小 / 返回结果的数据大小有很大的关系。这意味着,每个 redis-server 同时所能支撑的 pipeline 链接的个数,也是有限的,受限于 server 的物理内存或网络接口的缓冲能力。
示例:
import redis
r = redis.Redis(host='localhost', port=6379)
with r.pipeline(transaction=False) as p:
p.sadd('seta', 1).sadd('seta', 2).srem('seta', 2).lpush('lista', 1).lrange('lista', 0, -1)
result = p.execute()
剩下的你自己发挥。 除了管道(Pipelining),还有脚本(Scripting)。大量 pipeline 应用场景可通过 Redis 脚本(Redis 版本 >= 2.6)更高效的处理。脚本的一大优势是可通过最小的延迟读写数据,让读、计算、写等操作变得非常快(pipeline 在这种情况下不能使用,因为客户端在写命令前需要读命令返回的结果)。
应用程序有时可能在 pipeline 中发送 EVAL 或 EVALSHA 命令。Redis 通过 SCRIPT LOAD 命令(保证 EVALSHA 成功被调用)明确支持这种情况。