本场 Chat 分享主要介绍如何进行 MySQL 数据的快速迁移至 Redis,以官方文档为引,实际案例为导向,逐步实现任务目标,在操作过程中,记录下一些容易趟坑的知识点,便于后续加深理解。
本场 Chat 内容将涉及如下:
- Redis 单线程执行命令,避免了线程切换所消耗的时间,但是在超大数据量级下,其发送、响应接收的时延不可忽视。
- 网络 NC 命令的应用场景,及在数据导入时存在的缺点。
- Redis RESP 协议的理解和应用。
- 百万量级 MySQL 数据的 Redis 快速导入案例。
随着系统的运行,数据量变得越来越大,单纯的将数据存储在 mysql 中,已然不能满足查询要求了,此时我们引入 Redis 作为查询的缓存层,将业务中的热数据保存到 Redis,扩展传统关系型数据库的服务能力,用户通过应用直接从 Redis 中快速获取常用数据,或者在交互式应用中使用 Redis 保存活跃用户的会话,都可以极大地降低后端关系型数据库的负载,提升用户体验。
传统命令的缺点使用传统的 redis client 命令在大数据量的导入场景下存在如下缺陷:
由于 redis 是单线程模型,虽然避免了多线程下线程切换所耗费的时间,单一顺序的执行命令也很快,但是在大批量数据导入的场景下,发送命令所花费的时间和接收服务器响应结果耗费的时间就会被放大。
假如需要导入 100 万条数据,那光是命令执行时间,就需要花费 100 万*(t1 + t2)。
除了逐条命令发送,当然 redis 设计肯定也会考虑这个问题,所以出现了 pipelining 管道模式。
但是 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)机器之间网络测速
然而,使用 nc 监听并不是一个非常可靠的方式来执行大规模的数据导入,因为 netcat 并不真正知道何时传输了所有数据,也无法检查错误。在 2.6 或更高版本的 Redis 中,Redis -cli 脚本支持一种称为 pipe 管道模式的新模式,这种模式是为了执行大规模插入而设计的。使用管道模式的命令运行如下:
由上图,可以看到 pipe 命令的返回结果,txt 文件中有多少行命令,返回的 replies 数就是多少,errors 表示其中执行错误的命令条数。
协议的格式为:
* \r\n$ \r\n \r\n...$ \r\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\n
RESP 协议 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
关注
打赏