有時候 Redis 實例需要在短時間內(nèi)加載大量的已存在數(shù)據(jù),或者用戶產(chǎn)生的數(shù)據(jù),這樣,上百萬的鍵將在很短的時間內(nèi)被創(chuàng)建。
這被稱為集中插入(mass insertion),這篇文檔的目的,就是提供如何最快地向 Redis 中插入數(shù)據(jù)的一些相關(guān)信息。
使用標(biāo)準(zhǔn)的 Redis 客戶端來完成集中插入并不是一個好主意,理由是:一條一條的發(fā)送命令很慢,因為你需要為每個命令付出往返時間的花費。可以使用管道(pipelining),但對于許多記錄的集中插入而言,你在讀取響應(yīng)的同時還需要寫新命令,以確保插入盡可能快。
只有少部分的客戶端支持非阻塞 I/O,也并不是所有的客戶端都能高效地解析響應(yīng)以最大化吞吐量?;谏鲜鲞@些原因,首選的集中導(dǎo)入數(shù)據(jù)到 Redis 中的方式,是生成按照 Redis 協(xié)議的原始(raw)格式的文本文件,以調(diào)用需要的命令來插入需要的數(shù)據(jù)。
例如,如果我需要生成一個巨大的數(shù)據(jù)集,擁有數(shù)十億形式為”keyN->ValueN” 的鍵,我將創(chuàng)建一個按照 Redis 協(xié)議格式,包含如下命令的文件:
SET Key0 Value0
SET Key1 Value1
...
SET KeyN ValueN
當(dāng)這個文件被創(chuàng)建后,剩下的工作就是將其盡可能快的導(dǎo)入到 Redis 中。過去的辦法是使用 netcat 來完成,命令如下:
(cat data.txt; sleep 10) | nc localhost 6379 > /dev/null
然而,這種集中導(dǎo)入的方式并不是十分可靠,因為 netcat 并不知道所有的數(shù)據(jù)什么時候被傳輸完,并且不能檢查錯誤。在 github 上一個不穩(wěn)定的 Redis 分支上,redis-cli 工具支持一種稱為管道模式(pipe mode)的模式,設(shè)計用來執(zhí)行集中插入。
使用管道模式運行命令如下:
cat data.txt | redis-cli --pipe
輸出類似如下的內(nèi)容:
All data transferred. Waiting for the last reply...
Last reply received from server.
errors: 0, replies: 1000000
redis-cli 工具也能夠確保僅僅將來自 Redis 實例的錯誤重定向到標(biāo)準(zhǔn)輸出。
Redis 協(xié)議非常容易生成和解析,可以參考其文檔(請關(guān)注后續(xù)翻譯文檔,譯者注)。但是,為了集中插入的目標(biāo)而生成協(xié)議,你不必了解協(xié)議的每一個細(xì)節(jié),僅僅需要知道每個命令通過如下方式來表示:
*<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 通過以下協(xié)議來表示:
*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 函數(shù)生成合法的協(xié)議。
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
使用上面的函數(shù),可以很容易地生成上面例子中的鍵值對。程序如下:
(0...1000).each{|n|
STDOUT.write(gen_redis_proto("SET","Key#{n}","Value#{n}"))
}
我們現(xiàn)在可以直接以 redis-cli 的管道模式來運行這個程序,來執(zhí)行我們的第一次集中導(dǎo)入會話。
$ 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 一樣的快,并且能理解服務(wù)器同時返回的最后一條響應(yīng)。
按照以下方式獲得:
使用這個技巧,我們不需要為了知道發(fā)送了多少命令而解析發(fā)送給服務(wù)端的協(xié)議,僅僅只需要知道響應(yīng)就可以。
但是,在解析響應(yīng)的時候,我們對所有已解析響應(yīng)進(jìn)行了計數(shù),于是最后我們可以告訴用戶,通過集中插入會話傳輸給服務(wù)器的命令的數(shù)。