批量加载
使用Redis协议批量写入数据
批量加载是将大量预先存在的数据加载到Redis中的过程。理想情况下,您希望快速高效地执行此操作。本文档描述了一些在Redis中批量加载数据的策略。
使用Redis协议进行批量加载
使用普通的Redis客户端进行批量加载并不是一个好主意,原因有几个:逐个发送命令的简单方法很慢,因为你需要为每个命令支付往返时间。虽然可以使用管道技术,但对于大量记录的批量加载,你需要在读取回复的同时写入新命令,以确保尽可能快地插入。
只有一小部分客户端支持非阻塞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协议生成和解析非常简单,文档在此。然而,为了生成用于批量加载的协议,你不需要理解协议的每一个细节,只需要知道每个命令以下列方式表示:
*<args><cr><lf>
$<len><cr><lf>
<arg0><cr><lf>
<arg1><cr><lf>
...
<argN><cr><lf>
其中
表示 "\r"(或 ASCII 字符 13),
表示 "\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 尝试尽可能快地向服务器发送数据。
- 同时,当数据可用时,它会读取数据并尝试解析它。
- 一旦从标准输入中没有更多数据可读,它会发送一个特殊的ECHO命令,附带一个随机的20字节字符串:我们确信这是发送的最后一个命令,并且我们确信可以通过检查是否接收到相同的20字节作为批量回复来匹配回复。
- 一旦发送了这个特殊的最终命令,接收回复的代码开始与这20个字节进行匹配。当匹配到相应的回复时,它可以成功退出。
使用这个技巧,我们不需要解析我们发送到服务器的协议来理解我们发送了多少命令,而只需要解析回复。
然而,在解析回复时,我们会计算所有已解析回复的计数器,以便在最后能够告诉用户通过批量插入会话传输到服务器的命令数量。