有一些Redis实例需要在很短的时间内加载大量的的已经存在的或者用户产生的数据,因此数以百万计的键将被尽可能快的创建。
这被称为大量插入,这篇文档的目标是如何尽可能快的给Reis提供数据。
使用Luke网关
使用普通的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并不是实时的知道什么时候所有的数据被传输完,并且不能检查错误。在Redis2.6或以后的版本中,redis-cli
实用程序支持一个被称为pip 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 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个字节匹配回复。当匹配的回复到达时,它可以成功的退出。
使用这个窍门,我们不需要解析我们发送到服务器的网关,为了理解我们发送了多少命令,