跳转到主要内容

批量插入

有时 KeyDB 实例需要在短时间内加载大量预先存在或用户生成的数据,以便尽快创建数百万个键。

这被称为“*批量插入*”,本文档的目的是提供有关如何尽可能快地向 KeyDB 馈送数据的信息。

使用协议,Luke#

出于几个原因,使用普通的 KeyDB 客户端执行批量插入不是一个好主意:逐条发送命令的朴素方法很慢,因为您必须为每个命令支付往返时间。可以使用管道,但对于大量记录的批量插入,您需要在读取回复的同时编写新命令,以确保尽可能快地插入。

只有一小部分客户端支持非阻塞 I/O,并且并非所有客户端都能有效地解析回复以最大化吞吐量。出于所有这些原因,将数据批量导入 KeyDB 的首选方法是生成一个包含原始格式 KeyDB 协议的文本文件,以便调用插入所需数据的命令。

例如,如果我需要生成一个包含形如 `keyN -> ValueN` 的数十亿个键的大型数据集,我将创建一个包含以下命令的 KeyDB 协议格式文件:

SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN

一旦创建了该文件,剩下的操作就是尽快将其馈送到 KeyDB。过去,执行此操作的方法是使用 netcat 执行以下命令:

(cat data.txt; sleep 10) | nc localhost 6379 > /dev/null

然而,这种批量导入的方法并不可靠,因为 netcat 实际上不知道何时传输了所有数据,也无法检查错误。keydb-cli 实用程序支持一个名为“*管道模式*”的新模式,该模式旨在执行批量插入。

使用管道模式时,要运行的命令如下所示:

cat data.txt | keydb-cli --pipe
所有数据传输完毕。正在等待最后一个回复...
已收到服务器的最后一个回复。
错误:0,回复:1000000

keydb-cli 实用程序还将确保只将从 KeyDB 实例收到的错误重定向到标准输出。

生成 KeyDB 协议#

KeyDB 协议非常容易生成和解析,并且 此处 有文档记录。但是,为了生成用于批量插入的协议,您不需要了解协议的每个细节,只需要知道每个命令的表示方式如下:

*<参数数量><回车符><换行符>
$<长度><回车符><换行符>
<参数0><回车符><换行符>
<参数1><回车符><换行符>
...
<参数N><回车符><换行符>

其中 <回车符> 表示“\r”(或 ASCII 字符 13),<换行符> 表示“\n”(或 ASCII 字符 10)。

例如,命令 SET key value 由以下协议表示:

*3<回车符><换行符>
$3<回车符><换行符>
SET<回车符><换行符>
$3<回车符><换行符>
key<回车符><换行符>
$5<回车符><换行符>
value<回车符><换行符>

或表示为带引号的字符串

"*3\r\n$3\r\nSET\r\n$3\r\nkey\r\n$5\r\nvalue\r\n"

您需要为批量插入生成的文件只是由以上方式表示的命令一个接一个地组成。

以下 Ruby 函数生成有效的协议:

def gen_KeyDB_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_KeyDB_proto("SET","mykey","Hello World!").inspect

使用上述函数,可以轻松生成上述示例中的键值对,通过此程序:

(0...1000).each{|n|
STDOUT.write(gen_KeyDB_proto("SET","Key#{n}","Value#{n}"))
}

我们可以直接运行程序到 keydb-cli 中,以执行我们的第一次批量导入会话。

$ ruby proto.rb | keydb-cli --pipe
所有数据传输完毕。正在等待最后一个回复...
已收到服务器的最后一个回复。
错误:0,回复:1000

管道模式在幕后是如何工作的#

keydb-cli 管道模式内部所需的魔法是尽可能快地工作,同时仍然能够知道服务器何时发送了最后一个回复。

通过以下方式实现:

  • keydb-cli --pipe 尝试尽可能快地将数据发送到服务器。
  • 同时,它在有数据可用时读取数据,并尝试解析它。
  • 一旦没有更多数据可从 stdin 读取,它会发送一个特殊的 ECHO 命令,其中包含一个随机的 20 字节字符串:我们确信这是最后发送的命令,并且我们确信可以通过接收相同的 20 字节作为批量回复来匹配回复。
  • 一旦发送了这个特殊的最终命令,接收回复的代码就开始将回复与这 20 字节进行匹配。当匹配的回复到达时,它就可以成功退出。

使用这个技巧,我们不需要解析发送到服务器的协议来了解我们发送了多少命令,只需要解析回复。

然而,在解析回复的同时,我们计数所有已解析的回复,以便最终能够告知用户批量导入会话传输到服务器的命令数量。