一、介紹
有時候,Redis實例需要在很短的時間內加載大量先前存在或用戶生成的數據,以便盡可能快地創建數百萬個鍵。這就是所謂的批量插入,本文檔的目標是提供有關如何以盡可能快的速度向Redis提供數據的信息。如果想查看英文原文,地址如下:https://redis.io/topics/mass-insert
二、操作詳解
話不多說,直接進入主題了。
1、使用協議,盧克(Use the protocol, 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 命令並不會真正知道所有數據何時傳輸完畢,並且也無法檢查發生的錯誤。在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實例收到的錯誤重定向到標准輸出。
2、生成Redis協議(Generating Redis Protocol)
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 -h 192.168.127.130 -p 6379 --pipe All data transferred. Waiting for the last reply... Last reply received from server. errors: 0, replies: 1000
3、管道模式如何在引擎下工作(How the pipe mode works under the hoods)
redis-cli管道模式的速度和netcat一樣快,與此同時,仍然能夠明白服務器最后一次發送回復的時間。
這是通過以下方式獲得的:
3.1、redis-cli --pipe Redis客戶端會盡可能快的向服務器發送數據。
3.2、同時,會盡可能快的讀取並解析數據文件中的內容。
3.3、一旦從標准輸入設備讀取數據完畢,它將會發送一個帶有20個字節的字符串的特殊的ECHO命令到服務器:我們確信這是最新發送的命令,如果我們收到作為批量回復的相同的20個字節的消息,我們確信可以做“答復匹配檢查”。
3.4、這個特殊的最終命令一經發送,Redis服務器端將接收到回復和這20個字節的回復消息做匹配。如果匹配,它可以成功退出,表示插入完畢。
使用這個技巧,我們不需要解析我們發送給服務器的協議,以了解我們發送了多少條命令,僅僅是一個答復而已。
但是,在解析回復時,我們會對所有解析的回復進行計數,以便在最后我們能夠告訴用戶傳輸到服務器的命令的數量在這次批量插入的會話中。
4、示例代碼操作
4.1、准備數據文件,格式是文本文件,名稱是:redis_commands.txt。
我在Windows環境下生成了一個txt文件,一條數據一行,代碼如下:
SET Key0 Value0
SET Key1 Value1
SET Key2 Value2
SET Key3 Value3
SET Key4 Value4
SET Key5 Value5
SET Key6 Value6
SET Key7 Value7
SET Key8 Value8
SET Key9 Value9
SET Key10 Value10
...
SET KeyN ValueN
我生成了500萬的數據,因為這個文本文件我是在Windows環境下生成的,所以需要格式轉換。
4.2、如果使用Windows環境下生成的文件,需要進行格式轉換,如果是在Linux環境下生成的文件就不需要格式轉換,如果文本文件比較大,執行轉換時間會有幾秒,等待即可。
執行格式轉換
[root@linux ~]# unix2dos redis_commands.txt
unix2dos:converting file redis_commands.txt to DOS format ...
以上代碼進行格式轉換完畢
需要說明一點,unix2dos這個命令需要先安裝,如果沒有安裝,會提示:command not found。
執行以下命令安裝:
[root@linux ~]# yum install unix2dos
4.3、進行數據批量插入
[root@linux ~]# cat redis_commands.txt | redis-cli -h 192.168.127.130 -p 6379 [-a "password"] -n 0 --pipe All data transferred.Waiting for the last reply... Last reply received from server. errors:0,replies:10000000
批量插入數據成功,一千萬的數據大概要花費50幾秒左右。
三、總結
好的,今天就寫到這里,大批量數據插入的就是這么容易。只要理解了,其實也不是很難,技術就是一層窗戶紙,一捅就破,但是沒人捅就比較麻煩。下一篇文章,我們將寫一些關於redis協議格式的文章,如果要涉及大批量數據插入,就會涉及到redis規范協議的問題。