最近在做一些MySQL + Redis结合的工作,其中涉及到了MySQL的数据迁移到Redis的东西,小记一下。
首先,截止2016.11.21,我在Windows环境下都无法用Redis-cli.exe利用pipeline批量插入数据。原因大概是因为字符串等各种格式的问题,同样的命令在Ubuntu的终端下运行良好。Redis版本是Windows开源团队的Redis-x64-3.2.100。地址是MSOpenTech\Redis。
Redis 在Windows中的服务端是可以变为Windows服务自启动的,具体命令如下:
redis-server --service-install redis.windows.conf
安装完之后不会自启动,但是可以调用下面命令来进行启动。
redis-server --service-start
导入初期我是直接写了个工具,遍历了MySQL中的所有数据然后插入Redis的,我选择了直接用C#写一个控制台程序,选用的客户端是StackExchange.Redis,然后利用各类的Async进行异步操作(实际上就是使用pipeline),速度还是比较可以的。
后面发布的时候找了一下,Redis本身的客户端可以也利用pipeline来进行批量插入(Mass Insertiong),官方文档(Redis Mass Insertion
h)[http://redis.io/topics/mass-insert],然后结合pipeline,就可以利用脚本将MySQL中的数据转换为Redis-cli可以识别的格式,直接批量插入即可。
在文中,作者给出了示例,用如下的格式进行格式化输入即可:
*<args><cr><lf>
$<len><cr><lf>
<arg0><cr><lf>
<arg1><cr><lf>
...
<argN><cr><lf>
# Where <cr> means "\r" (or ASCII character 13) and <lf> means "\n" (or ASCII character 10).
举个简单的例子,如果要插入一个String类型的key-value值,那么就是
*3\r\n #表明命令中包含的参数个数
$3\r\n #表明第一个参数的长度
SET\r\n #第一个参数,注意各项关键字也都是参数,如这里的SET
$3\r\n #表明第二个参数的长度
123\r\n #第二个参数
$4\r\n #表明第三个参数的长度
4567\r\n #第三个参数
执行完毕的结果就是插入一个key为123,Value为4567的String类型的Key-Value啦。
而这里涉及到的一个东西就是Redis的Protocol,具体可以查看这里Redis Protocol specification。我也是从这里知道了到底上面那串命令的含义,以及如何处理数据库中空的列的数据。(卖个关子)
我参考了这篇文章
浅谈 Redis 与 MySQL 的耦合性以及利用管道完成 MySQL 到 Redis 的高效迁移 ,里面有讲到如何利用SQL语句将需要的数据格式化成为Redis-cli可以识别的格式,实际上只要两行代码就解决了问题,非常简单。
文章里面展示了这两句核心代码:
# 这是SQL语句,格式化数据用
cat mysql_to_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,
\'david\' AS redis_key,
myname AS hkey,
mymoney AS hval
FROM david_lin
) AS t;
#这是命令行/终端语句, 直接执行就可以导入啦
mysql -uroot -poracle test --skip-column-names --raw < mysql_to_redis.sql | redis-cli --pipe
只要简单执行上述的两行代码,就可以直接迁移。
然而在我的使用过程中发现了一个问题,由于业务关系,我的基础业务数据库中会存在空列,而这个又是关键数据,需要迁移到Redis中的。单纯利用上述的SQL语句,进行操作的时候,Redis-cli会抛出“Unknown Command NULL on Redis.”
我们知道,Redis是可以存储二进制数据的(而实际上Redis存储的Value都是二进制格式存储的),所以空数据理论上是可以存入的。然而根据观察,Redis的空数据,表现为空字符串形式,即”。所以,我们需要将我们数据库中为NULL的数据,转换成为”进行输出。
而Redis-cli接受空字符串的时候,是可以根据参数长度进行判断的。具体表现为“$0\r\n\r\n”,正好一致。
所以上述的命令应该修改为如下:
cat mysql_to_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,
\'david\' AS redis_key,
myname AS hkey,
if(mymoney is not null, mymoney, '') AS hval
FROM david_lin
) AS t;
看出来区别了吗~ 因为NULL数据长度肯定为0,所以上面一部分不需要修改,只需要修改下面的SELECT语句即可。
这样就可以将带有空行的数据也一次导入啦~!
另外需要注意的是,上述终端命令需要根据自己的实际情况,将数据库所在的ip和端口等都填上哦。