Redis和MySQL同步

  1. 页面并发量和访问量并不多,MySQL足以支撑自己逻辑业务的发展。那么其实可以不加缓存。最多对静态页面进行缓存即可。
  2. 页面的并发量显著增多,数据库有些压力,并且有些数据更新频率较低反复被查询或者查询速度较慢。那么就可以考虑使用缓存技术优化。对高命中的对象存到key-value形式的Redis中,那么,如果数据被命中,那么可以不经过效率很低的db。从高效的redis中查找到数据。

MySQL同步到ES的解决方案

1.代码实现

技术难度低,实时性高,侵入性强

2. AOP

半侵入性

3. canal (java)

阿里巴巴开源MySQL binlog 增量订阅&消费组件
基于binlog 订阅 数据同步中间件

4. logstash

image.png

数据迁移

一般解决方案是使用类似Sqoop的工具,直连MySQL去Select数据存储到HDFS,然后把HDFS数据Load到Hive中。这种方法简单易操作,但随着业务规模扩大,不足之处也逐步暴露出来:

  • 直连MySQL查询,对于数据库压力较大(如订单表、支付表等),可能直接影响在线业务
  • 数据整体就位时间(尤其大表)不满足下游生产需求
  • 扩展性较差,对于分表、字段增减、变更等的支持较弱
  • 拉取的数据是该时刻的镜像,无法获取中间变化情况

image.png
整体数据流程如上图所示,数据收集部分使用定制化Canal组件(基于阿里开源项目)收集binlog日志并做格式转换,然后通过消息队列传输并落地到HDFS,最后对HDFS上的binlog进行清洗还原入库。

如果是增量接入,上述操作就完成了一次入库流程。针对全量接入或者回溯历史数据,因为缺少历史binlog日志(发起采集时才开始收集)无法还原历史数据,此时需要借助离线一次性拉取,流程如下:

  • 按照上述流程采集binlog日志增量入HDFS
  • 使用离线一次性拉取一份历史全量数据,按字段还原到Hive作为基点(即第一个接入周期的数据)
  • 使用前一个接入周期的全量数据和本周期的增量binlog做merge形成该周期内的数据。

相比一般解决方案,其优点比较明显,主要表现在:

  • 基于Binlog日志的数据还原,与在线业务解耦
  • 采集通过分布式队列实时传递,还原操作在集群上实现,及时性及可扩展性强
  • Binlog日志包括了增、删、改等明细动作,支持定制化的ET

DATAx

https://github.com/alibaba/DataX
Binlog从发起采集的一刻起才会在整个链路上存在,即以增量的方式传递,那么对于历史数据如何获取?实际场景中包括全量接入或增量历史数据回溯。

目前实现方式为通过DataX工具直连MySQL离线库,拉取一份截至到当前时间的全量数据,然后按列还原到Hive表的首个分区中。

全量采集场景下,下个分区的数据基于上个分区的数据和当前周期内的增量Binlog日志merge,即可产生该分区内的数据。

上面介绍了基于Binlog数据接入的整体流程,下面列举两个实际解决的业务问题。

canal

  • canal模拟mysql slave的交互协议,伪装自己为mysql slave,向master发送dump协议
  • mysql master收到dump请求,开始推送binary log到canal
  • canal解析binary log对象,并将解析的结果编码成JSON格式的文本串
  • 把解析后的文本串发送到消息队列并上报发送情况(如Kafka、DDMQ)

image.png
格式:

  1. {
  2. "binlog": "25521@mysql-bin.000070",
  3. "time": 1450236307000,
  4. "canalTime": 1450236308279,
  5. "db": "TestCanal",
  6. "table": "g_order_010",
  7. "event": "u",
  8. "columns": [
  9. {"n": "order_id", "t": "bigint(20)", "v": "126", "null": false, "updated": false},
  10. {"n": "driver_id", "t": "bigint(20)", "v": "123456", "null": false, "updated": false},
  11. { "n": "passenger_id", "t": "bigint(20)", "v": "654321", "null": false, "updated": false},
  12. {"n": "current_lng", "t": "decimal(10,6)", "v": "39.021400", "null": false, "updated": false},
  13. {"n": "current_lat", "t": "decimal(10,6)", "v": "120.423300", "null": false, "updated": false},
  14. { "n": "starting_lng", "t": "decimal(10,6)", "v": "38.128000", "null": false, "updated": false},
  15. { "n": "starting_lat", "t": "decimal(10,6)", "v": "121.445000", "null": false, "updated": false},
  16. { "n": "dest_name", "t": "varchar(100)", "v": "Renmin University", "origin_val": "知春路", "null": false, "updated": true}
  17. ],
  18. "keys": ["order_id"]
  19. }{
  20. "binlog": "25521@mysql-bin.000070",
  21. "time": 1450236307000,
  22. "canalTime": 1450236308279,
  23. "db": "TestCanal",
  24. "table": "g_order_010",
  25. "event": "u",
  26. "columns": [
  27. {"n": "order_id", "t": "bigint(20)", "v": "126", "null": false, "updated": false},
  28. {"n": "driver_id", "t": "bigint(20)", "v": "123456", "null": false, "updated": false},
  29. { "n": "passenger_id", "t": "bigint(20)", "v": "654321", "null": false, "updated": false},
  30. {"n": "current_lng", "t": "decimal(10,6)", "v": "39.021400", "null": false, "updated": false},
  31. {"n": "current_lat", "t": "decimal(10,6)", "v": "120.423300", "null": false, "updated": false},
  32. { "n": "starting_lng", "t": "decimal(10,6)", "v": "38.128000", "null": false, "updated": false},
  33. { "n": "starting_lat", "t": "decimal(10,6)", "v": "121.445000", "null": false, "updated": false},
  34. { "n": "dest_name", "t": "varchar(100)", "v": "Renmin University", "origin_val": "知春路", "null": false, "updated": true}
  35. ],
  36. "keys": ["order_id"]
  37. }

mysqldump