批量加载
提示
来自deepseek解释
原文链接:https://redis.io/docs/latest/develop/clients/patterns/bulk-loading/
批量加载是将大量已有数据加载到 Redis 中的过程。理想情况下,您希望快速高效地完成此操作。本文档介绍了一些在 Redis 中批量加载数据的策略。
使用 Redis 协议进行批量加载
使用普通的 Redis 客户端执行批量加载并不是一个好主意,原因如下:逐个命令发送的简单方法很慢,因为您需要为每个命令支付往返时间。可以使用流水线(pipelining),但在批量加载大量记录时,您需要在读取回复的同时写入新命令,以确保尽可能快地插入数据。
只有少部分客户端支持非阻塞 I/O,而且并非所有客户端都能够高效地解析回复以最大化吞吐量。基于所有这些原因,将数据批量导入 Redis 的首选方式是生成一个包含 Redis 协议原始格式的文本文件,以便调用插入所需数据所需的命令。
例如,如果我需要生成一个包含数十亿个键的大型数据集,键的形式为:keyN -> ValueN,那么我将创建一个包含以下 Redis 协议格式命令的文件:
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
一旦创建了此文件,剩下的操作就是尽可能快地将它提供给 Redis。过去,使用 netcat 执行此操作的方法如下:
(cat data.txt; sleep 10) | nc localhost 6379 > /dev/null
然而,这并不是一种非常可靠的大规模导入方法,因为 netcat 实际上并不知道所有数据何时传输完毕,也无法检查错误。在 Redis 2.6 或更高版本中,redis-cli 实用程序支持一种新的模式,称为 管道模式,该模式专为执行批量加载而设计。
使用管道模式时,要运行的命令如下所示:
cat data.txt | redis-cli --pipe
这将产生类似于以下的输出:
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000000
redis-cli 实用程序还会确保仅将从 Redis 实例收到的错误重定向到标准输出。
生成 Redis 协议
Redis 协议非常容易生成和解析,并在此处进行了文档说明。然而,为了批量加载的目的生成协议,您不需要理解协议的每个细节,只需要知道每个命令按以下方式表示:
*<参数数量><CR><LF>
$<参数长度><CR><LF>
<参数0><CR><LF>
<参数1><CR><LF>
...
<参数N><CR><LF>
其中 <CR> 表示 "\r"(或 ASCII 字符 13),<LF> 表示 "\n"(或 ASCII 字符 10)。
例如,命令 SET key value 由以下协议表示:
*3<CR><LF>
$3<CR><LF>
SET<CR><LF>
$3<CR><LF>
key<CR><LF>
$5<CR><LF>
value<CR><LF>
或者表示为带引号的字符串:
"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"
您需要为批量加载生成的文件仅由以上述方式表示的命令组成,一个接一个。
以下 Ruby 函数生成有效的协议:
def gen_redis_proto(*cmd)
proto = ""
proto << "*"+cmd.length.to_s+"\r\n"
cmd.each{|arg|
proto << "$"+arg.to_s.bytesize.to_s+"\r\n"
proto << arg.to_s+"\r\n"
}
proto
end
puts gen_redis_proto("SET","mykey","Hello World!").inspect使用上述函数,可以轻松生成前面示例中的键值对,使用以下程序:
(0...1000).each{|n|
STDOUT.write(gen_redis_proto("SET","Key#{n}","Value#{n}"))
}我们可以直接将该程序通过管道传递给 redis-cli 以执行我们的第一次大规模导入会话:
$ ruby proto.rb | redis-cli --pipe
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000
管道模式在底层是如何工作的
redis-cli 管道模式内部所需的魔力在于:既要像 netcat 一样快,同时还要能够理解服务器何时发送了最后一条回复。
这是通过以下方式实现的:
- redis-cli --pipe 尝试尽快将数据发送到服务器。
- 同时,它在数据可用时读取数据,并尝试解析。
- 一旦没有更多数据从 stdin 读取,它会发送一个特殊的 ECHO 命令,附带一个随机的 20 字节字符串:我们确信这是发送的最后一条命令,并且我们可以通过检查是否收到相同的 20 字节作为批量回复来匹配回复。
- 一旦发送了这条特殊的最终命令,接收回复的代码开始将这些回复与这 20 字节进行匹配。当匹配的回复到达时,它可以成功退出。
通过这个技巧,我们不需要解析发送给服务器的协议来了解我们发送了多少条命令,只需解析回复即可。
然而,在解析回复时,我们会统计所有已解析的回复数量,以便最后能够告知用户此次大规模插入会话传输到服务器的命令数量。