由于redis是单线程模型,虽然避免了多线程下线程切换所耗费的韶光,单一顺序的实行命令也很快,但是在大批量数据导入的场景下,发送命令所花费的韶光和吸收做事器相应结果耗费的韶光就会被放大。

如果须要导入100万条数据,那光是命令实行韶光,就须要花费100万(t1 + t2)。

除了逐条命令发送,当然redis设计肯定也会考虑这个问题,以是涌现了pipelining管道模式。

phpredis数据导出Mysql百万级数据量级下若何高效的迁徙到Redis Node.js

但是pipelining在命令行中是没有的,使得我们又须要编写新的处理代码,来吸收批量的相应。
但是只有很少很少的客户端代码支持,比如php-redis的扩展就不支持异步。

pipelining管道模式,实在便是减少了TCP连接的交互韶光,当一批命令实行完毕后,一次性发送结果。

实在现事理是采取FIFO(前辈先出)的行列步队来担保数据的顺序性。

只有一小部分客户端支持非壅塞I/O,并不是所有的客户端都能够以一种有效的办法解析应答,以最大化吞吐量。

由于这些缘故原由,将弘大数据导入到Redis的首选方法是天生一个包含Redis协议数据格式,批量的发送过去。

数据导入Redis热身采取nc命令导入数据

nc是netcat的简写,nc的浸染有:

(1)实现任意TCP/UDP端口的侦听,增加-l参数后,nc可以作为server以TCP或UDP办法侦听指定端口

(2)端口的扫描,nc可以作为client发起TCP或UDP连接

(3)机器之间传输文件

(4)机器之间网络测速

采取pipe模式导入数据

然而,利用nc监听并不是一个非常可靠的办法来实行大规模的数据导入,由于netcat并不真正知道何时传输了所有数据,也无法检讨缺点。
在2.6或更高版本的Redis中,Redis -cli脚本支持一种称为pipe管道模式的新模式,这种模式是为了实行大规模插入而设计的。
利用管道模式的命令运行如下:

由上图,可以看到pipe命令的返回结果,txt文件中有多少行命令,返回的replies数便是多少,errors表示个中实行缺点的命令条数。

redis协议学习

协议的格式为:

<参数数量> \r\n$<参数 1 的字节数量> \r\n<参数 1 的数据> \r\n...$<参数 N 的字节数量> \r\n<参数 N 的数据> \r\n

比如:插入一条hash类型的数据。

HSET id book1 book_description1

根据Redis协议,统共有4个部分,以是开头为4,别的内容阐明如下:

内容长度协议命令HSET4$4id2$2book15$5book_description117$17

把稳一下:HSET命令本身也作为协议的个中一个参数来发送。

布局出来的协议数据构造:

4\r\n$4\r\nHSET\r\n$2\r\nid\r\n$5\r\nbook1\r\n$17\r\nbook_description1\r\n格式化一下:4\r\n$4\r\nHSET\r\n$2\r\nidvvvv\r\n$5\r\nbook1\r\n$17\r\nbook_description1\r\nRESP协议 bulk

Redis客户机利用一种称为RESP (Redis序列化协议)的协议与Redis做事器通信。

redis-cli pipe模式须要和nc命令一样快,并且办理了nc命令不知道何时命令结束的问题。

在发送数据的同时,它同样会去读取相应,考试测验去解析。

一旦输入流中没有读取到更多的数据之后,它就会发送一个分外的20比特的echo命令,标识末了一个命令已经发送完毕如果在相应结果中匹配到这个相同数据后,解释本次批量发送是成功的。

利用这个技巧,我们不须要解析发送给做事器的协议来理解我们发送了多少命令,只须要解析应答即可。

在解析应答时,redis会对解析的应答进行一个计数,在末了能够见告用户大量插入会话向做事器传输的命令的数量。
也便是上面我们利用pipe模式实际操作的相应结果。

将输入数据源换成mysql

上面的例子中,我们以一个txt文本为输入数据源,利用了pipe模式导入数据。

基于上述协议的学习和理解,我们只须要将mysql中的数据按照既定的协议通过pipe模式导入Redis即可。

实际案例--从Mysql导入百万级数据到Redis首先造数据

由于环境限定,以是这里没有用真实数据来实现导入,那么我们就先利用一个存储过程来造一百万条数据把。
利用存储过程如下:

DELIMITER $$USE `cb_mon`$$DROP PROCEDURE IF EXISTS `test_insert`$$CREATE DEFINER=`root`@`%` PROCEDURE `test_insert`()BEGIN DECLARE i INT DEFAULT 1; WHILE i<= 1000000 DO INSERT INTO t_book(id,number,NAME,descrition) VALUES (i, CONCAT("00000",i) , CONCAT('book',i) , CONCAT('book_description',i)); SET i=i+1; END WHILE ; COMMIT; END$$DELIMITER ;

调用存储过程:

CALL test_insert();

查看表数据:

按协议布局查询语句

按照上述redis协议,我们利用如下sql来布局协议数据

SELECT CONCAT( "4\r\n", "$", LENGTH(redis_cmd), "\r\n", redis_cmd, "\r\n", "$", LENGTH(redis_key), "\r\n", redis_key, "\r\n", "$", LENGTH(hkey), "\r\n", hkey, "\r\n", "$", LENGTH(hval), "\r\n", hval, "\r" )FROM (SELECT "HSET" AS redis_cmd, id AS redis_key, NAME AS hkey, descrition AS hval FROM cb_mon.t_book ) AS t limit 1000000

并将内容保存至redis.sql 文件中。

编写脚本利用pipe模式导入redis

编写shell脚本。
由于我在主机上是通过docker安装的redis和mysql,以下脚本供参考:

#!/bin/bashstarttime=`date +'%Y-%m-%d %H:%M:%S'`docker exec -i 899fe01d4dbc mysql --default-character-set=utf8 --skip-column-names --raw < ./redis.sql| docker exec -i 4c90ef506acd redis-cli --pipeendtime=`date +'%Y-%m-%d %H:%M:%S'`start_seconds=$(date --date="$starttime" +%s);end_seconds=$(date --date="$endtime" +%s);echo "脚本实行耗时: "$((end_seconds-start_seconds))"s"

实行截图:

可以看到百万级的数据导入redis,只花费了7秒,效率非常高。

把稳事变

如果mysql表特殊大,可以考虑分批导入,或者将表拆分,否则在导入过程中可能会发生

lost connection to mysql server during query

由于max_allowed_packed和超时时间限定,查询数据的过程中,可能会造成连接断开,以是在数据表的数据量特殊大的时候,须要分页或者将表拆分导入。

总结

本篇文章紧张磋商了,Mysql百万级数据量级下,如何高效的迁移到Redis中去,逐步实现目标的过程中,总结了如下几点

redis单线程实行命令,避免了线程切换所花费的韶光,但是在超大数据量级下,其发送、相应吸收的时延不可忽略。
网络nc命令的运用处景,及在数据导入时存在的缺陷。
redis RESP协议的理解和运用。
百万量级Mysql数据的Redis快速导入案例。

原出处:https://segmentfault.com/a/1190000021719490