有时,Redis实例需要在很短的时间内加载大量的预先存在或用户生成的数据,以便尽可能快地创建数百万个密钥。这被称为批量插入,本文的目标是提供有关如何尽可能快地向向Redis插入大量的数据。
使用Luke协议
使用正常模式的Redis客户端进行大容量插入是不明智的,因为一个个的插入会有大量的时间浪费在每一个命令往返时间上。
使用管道(pipelining)还比较靠谱,但是在大量插入数据的同时又需要执行其他新命令时,这时读取数据的同时需要确保尽可能快的写入数据。
只有一小部分的客户端支持非阻塞/输出(non-blocking I/O),并且并不是所有客户端能以最大限度的提高吞吐量到高效的方式来分析答复。
例如,如果需要生成一个10亿度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实用程序支持一种称为pipe mode的新模式,该模式是为了执行批量插入而设计的。
使用pipe mode运行的命令如下所示:
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>
其中<cr>表示“\ r”(或ASCII字符13),<lf>表示“\ n”(或ASCII字符10)。
例如,命令SET键值由以下协议表示:
*3<cr><lf> $3<cr><lf> SET<cr><lf> $3<cr><lf> key<cr><lf> $5<cr><lf> value<cr><lf>
或者用引用的字符串表示:
"*3\r$3\rSET\r$3\rkey\r$5\rvalue\r"
您需要为批量插入生成的文件只是由以上述方式表示的命令组成,一个接一个地。
以下Ruby函数生成有效的协议:
def gen_redis_proto(*cmd) proto = "" proto << "*"+cmd.length.to_s+"\r" cmd.each{|arg| proto << "$"+arg.to_s.bytesize.to_s+"\r" proto << arg.to_s+"\r" } 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
pipe mode的工作原理?
难点是保证redis-cli在pipe mode模式下执行和netcat一样快的同时,如何能理解服务器发送的最后一个回复。
这是通过以下方式获得的:
redis-cli --pipe尝试尽可能快地将数据发送到服务器。
读取数据时同时,解析它。
一旦没有更多的数据输入,它就会发送一个特殊的echo命令,后面跟着20个随机的字符。我们相信可以通过匹配回复相同的20个字符是同一个命令的行为。
一旦发送了这个特殊的最终命令,接收到回复的代码就开始匹配这20个字节的回复。当匹配时,它可以成功退出。
使用这个技巧,我们不需要解析我们发送到服务器的协议,以了解我们发送了多少个命令,而只是回复。
然而,在解析答复时,我们将所有解析的答案都计数在一个计数器上,以便在最后我们能够通过质量插入会话告诉用户传送给服务器的命令的数量。
原文:https://redis.io/topics/mass-insert
相关文章:
redis 持久化详解,RDB和AOF是什么?他们优缺点是什么?运行流程是什么?