Mysql和ES、Redis数据同步方案汇总

2023-11-13

前言

我们一般会使用Mysql用来存储数据,用Es来做全文检索和特殊查询,用redis来做数据的缓存

那么如何将数据优雅的从Mysql同步到Es以及redis呢?

一、数据同步方案

1.同步双写

这是一种最为简单的方式,在将数据写到mysql时,同时将数据写到ES或者redis。

这个方法的缺点是代码严重耦合,需要手动维护Mysql和Es数据关系,非常不便于维护。
在这里插入图片描述

伪代码:

    /**
     * 新增商品
     */
    @Transactional(rollbackFor = Exception.class)
    public void addGoods(GoodsDto goodsDto) {
         //1、保存Mysql
         Goods goods = new Goods();
         BeanUtils.copyProperties(goodsDto,goods);
         goodsMapper.insert();
     
         //2、保存ES
         IndexRequest indexRequest = new IndexRequest("goods_index","_doc");
         indexRequest.source(JSON.toJSONString(goods), XContentType.JSON);
         indexRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
         highLevelClient.index(indexRequest);
    }

  • 优点:
    1、业务逻辑简单
    2、实时性高
  • 缺点:
    1、 硬编码,有需要写入mysql的地方都需要添加写入ES的代码;
    2、 业务强耦合;
    3、 存在双写失败丢数据风险;
    4、 性能较差:本来mysql的性能不是很高,再加一个ES,系统的性能必然会下降。
  • 双写失败风险,包括以下几种:
    1) ES系统不可用;
    2) 程序和ES之间的网络故障;
    3) 程序重启,导致系统来不及写入ES等。
    针对这种情况,有数据强一致性要求的,就必须双写放到事务中来处理,而一旦用上事物,则性能下降更加明显。

2.异步双写(MQ方式)

针对多数据源写入的场景,可以借助MQ实现异步的多源写入,这种情况下各个源的写入逻辑互不干扰,不会由于单个数据源写入异常或缓慢影响其他数据源的写入,虽然整体写入的吞吐量增大了,但是由于MQ消费是异步消费,所以不适合实时业务场景。

在执行完向Mysql中写入数据的逻辑后,发送MQ,告诉消费端这个数据需要写入Es,消费端收到消息后执行向Es写入数据的逻辑。这个方式的优点是Mysql和Es数据维护分离,开发Mysql和Es的人员只需要关心各自的业务。缺点是依然需要维护发送、接收MQ的逻辑,并且引入了MQ组件,增加了系统的复杂度。
在这里插入图片描述

伪代码:

    @Transactional(rollbackFor = Exception.class)
    public boolean parseJdDb(String keyword) throws IOException {
        //向数据库中插入
        List<JdGoods> jdGoods = htmlParseUtil.parseJd(keyword);
        //批量更新Mysql
        boolean b = this.saveBatch(jdGoods);
        log.info("异步同步至ES");
        rabbitTemplate.convertAndSend("es.exchange","es.renew.key", jdGoods);
        return b;
    }

优点:
1、性能高
2、不易出现数据丢失问题,主要基于MQ消息的消费保障机制,比如ES宕机或者写入失败,还能重新消费MQ消息。
3、多源写入之间相互隔离,便于扩展更多的数据源写入

缺点:
1、硬编码问题,接入新的数据源需要实现新的消费者代码
3、系统复杂度增加:引入了消息中间件
4、可能出现延时问题:MQ是异步消费模型,用户写入的数据不一定可以马上看到,造成延时。

3.基于Mysql表定时扫描同步

上面两种方案中都存在硬编码问题,也就是有任何对mysq进行增删改查的地方要么植入ES代码,要么替换为MQ代码,代码的侵入性太强。

如果对实时性要求不高的情况下,可以考虑用定时器来处理,具体步骤如下:
1、数据库的相关表中增加一个字段为timestamp的字段,任何crud操作都会导致该字段的时间发生变化;
2、原来程序中的CURD操作不做任何变化;
3、增加一个定时器程序,让该程序按一定的时间周期扫描指定的表,把该时间段内发生变化的数据提取出来;
4、逐条写入到ES中。

如下图所示:

该方案的典型实现是借助logstash实现数据同步,其底层实现原理就是根据配置定期使用sql查询新增的数据写入ES中,实现数据的增量同步。

具体实现可以参考:通过Logstash实现mysql数据定时增量同步到ES
在这里插入图片描述

优点:
1、不改变原来代码,没有侵入性、没有硬编码;
2、没有业务强耦合,不改变原来程序的性能;
3、Worker代码编写简单不需要考虑增删改查;
缺点:
1、时效性较差,由于是采用定时器根据固定频率查询表来同步数据,尽管将同步周期设置到秒级,也还是会存在一定时间的延迟。
2、对数据库有一定的轮询压力,一种改进方法是将轮询放到压力不大的从库上。

业界目前较为流行的方案:Canal实现Mysql数据同步至Redis、Elasticsearch

4.基于Binlog实时同步

上面三种方案要么有代码侵入,要么有硬编码,要么有延迟,那么有没有一种方案既能保证数据同步的实时性又没有代入侵入呢?
当然有,可以利用mysql的binlog来进行同步。其实现原理如下:
在这里插入图片描述
具体步骤如下:
1) 读取mysql的binlog日志,获取指定表的日志信息;
2) 将读取的信息转为MQ;
3) 编写一个MQ消费程序;
4) 不断消费MQ,每消费完一条消息,将消息写入到ES中。

优点:
1、没有代码侵入、没有硬编码;
2、原有系统不需要任何变化,没有感知;
3、性能高;
4、业务解耦,不需要关注原来系统的业务逻辑。
缺点:
1、构建Binlog系统复杂;
2、如果采用MQ消费解析的binlog信息,也会像方案二一样存在MQ延时的风险。
在这里插入图片描述

二、数据迁移同步工具选型

数据迁移同步工具的选择比较多样,下表仅从 MySQL 同步 ES 这个场景下,对一些笔者深度使用研究过的数据同步工具进行对比,用户可以根据自己的实际需要选取适合自己的产品。

特性\产品 Canal DTS CloudCanal
是否支持自建ES
ES对端版本支持丰富度 中 支持ES6和ES7 高 支持ES5,ES6和ES7 中 支持ES6和ES7
嵌套类型支持 join/nested/object object nested/object
join支持方式 基于join父子文档&反查 基于宽表预构建&反查
是否支持结构迁移
是否支持全量迁移
是否支持增量迁移
数据过滤能力 中 -仅全量可添加where条件 高 -全增量阶段where条件 高 -全增量阶段where条件
是否支持时区转换
同步限流能力
任务编辑能力
数据源支持丰富度
架构模式 订阅消费模式 需先写入消息队列 直连模式 直连模式
监控指标丰富度 中 性能指标监控 中 性能指标监控 高 性能指标、资源指标监控
报警能力 针对延迟、异常的电话报警 针对延迟、异常的钉钉、短信、邮件报警
任务可视化创建&配置&管理能力
是否开源
是否免费 否 是社区版、 SAAS版免费
是否支持独立输出 否依赖云平台整体输出
是否支持SAAS化使用

总结

本文主要对Mysql和ES进行数据同步的常见方案进行了汇总说明。

  1. 同步双写是最简单的同步方式,能最大程度保证数据同步写入的实时性,最大的问题是代码侵入性太强。
  2. 异步双写引入了消息中间件,由于MQ都是异步消费模型,所以可能出现数据同步延迟的问题。好处是在大规模消息同步时吞吐量更、高性能更好,便于接入更多的数据源,且各个数据源数据消费写入相互隔离互不影响。
  3. 基于Mysql表定时扫描同步 ,原理是通过定时器定时扫描表中的增量数据进行数据同步,不会产生代码侵入,但由于是定时扫描同步,所以也会存在数据同步延迟问题,典型实现是采用 Logstash 实现增量同步。
  4. 基于Binlog实时同步 ,原理是通过监听Mysql的binlog日志进行增量同步数据。不会产生代码侵入,数据同步的实时也能得到保障,弊端是Binlog系统都较为复杂。典型实现是采用 canal 实现数据同步。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mysql和ES、Redis数据同步方案汇总 的相关文章

  • 比特纳米。重置mysql根密码

    我如何重置 MySQL 中的 root 密码和帐户 因为我按照如何为其他服务器授予权限的说明操作 并且意外地将 root 用户 Mysql 绑定到其他 IP 地址 现在看来我无法在 localhost 上以管理员身份登录 Thanks 您有
  • Flask-login:无法理解它是如何工作的

    我试图理解如何Flask Login https flask login readthedocs org en latest works 我在他们的文档中看到他们使用预先填充的用户列表 我想使用数据库存储的用户列表 但是 我不明白其中的一些
  • PDO PHP 连接,致命错误

    我的连接类 firstcode php class DB functions public db function construct try db new PDO mysql localhost dbname xxx charset ut
  • 如何按键中的值对 Redis 哈希进行排序

    Redis 有没有一种好方法来获取按值排序的哈希中的键 我查看了文档 但没有找到直接的方法 另外有人可以解释一下redis中的排序是如何实现的 以及什么吗 本文档 http redis io commands SORT using hash
  • 如何在 Play java 中创建数据库线程池并使用该池进行数据库查询

    我目前正在使用 play java 并使用默认线程池进行数据库查询 但了解使用数据库线程池进行数据库查询可以使我的系统更加高效 目前我的代码是 import play libs Akka import scala concurrent Ex
  • 如果没有过期的内容,Redis maxmemory-policy volatile-lru 是否会被驱逐?

    我有一个 redis 服务器 设置了maxmemory policy set to volatile lru 文档表明 当达到内存限制时 这将从设置过期的条目集中逐出 在这种情况下 redis 是否只驱逐过期的项目 如果内存中的所有内容都设
  • 显示标准化数据

    跟进问题 添加 2 个不同表的总和 https stackoverflow com questions 39717541 adding sum from 2 different tables 我创建了3个表 members videos v
  • 在 C# 中,当有人插入、删除或修改记录时,如何从 MySQL 获取事件?

    我正在 WPF Net 中开发一个程序 我需要知道何时有人对数据库的任何表进行更改 这个想法是在数据库发生更改时从数据库接收一个事件 我读了很多文章 但找不到解决我的问题的方法 亲切的问候 最好的解决方案是使用消息队列 在您的应用程序向数据
  • 如何在mysql中选择具有相同值集的列?

    我的桌子是 patients pid name city disease did dname has disease did pid 我想列出具有相同疾病组的患者 pid 和 did 分别是患者和疾病表中的主键 并且是 has diseas
  • 在elasticSearch中查询时定义分析器

    我对 Elasticsearch 还很陌生 只需要一些说明 我们可以在查询搜索服务器时定义一个分析器吗 我尝试使用 文本 和 字段 查询 效果很好 Query curl XPOST http localhost 9200 test user
  • 不允许在 php 中连接到此 MariaDB 服务器

    我尝试在 php 中连接远程服务器数据库 但出现以下错误 Host xx xxx xx xx is not allowed to connect to this MariaDB server in 我的连接代码是这样的 servername
  • 如何优化这个MySQL慢(非常慢)查询?

    我有一个 2 GB 的 mysql 表 包含 500k 行 我在没有负载的系统上运行以下查询 select from mytable where name in n1 n2 n3 n4 bunch more order by salary
  • Elasticsearch 前缀匹配消失且未添加 (QueryString)

    结转自Elasticsearch QueryStrings 部分匹配 NOT 查询 https stackoverflow com questions 40100006 elasticsearch querystrings partiall
  • 合并两个 MYSQL SELECT 查询[重复]

    这个问题在这里已经有答案了 可能的重复 如何将两个 Post Category 表 MYSQL SELECT 查询合并为一个 https stackoverflow com questions 12972130 how to combine
  • MySQL“LIKE”搜索不起作用

    我通过 LOAD DATA INFILE 在 MySQL 中导入了一个 txt 数据库 一切似乎都正常 唯一的问题是 如果我使用以下查询在数据库上搜索记录 SELECT FROM hobby WHERE name LIKE Beading
  • 不带 GROUP BY 的聚合查询

    这个查询似乎在我的旧机器上完美运行 但是 在我的 MySQL 5 7 14 和 PHP 5 6 25 的新机器上 它会抛出错误 致命错误 未捕获异常 PDOException 并带有消息 SQLSTATE 42000 语法错误或访问冲突 1
  • libmysqlclient.a 和 libmysqlclient_r.a 有什么区别?

    我应该使用哪个来链接 mysqlclient 库 它们之间有什么区别 我似乎找不到答案 谢谢 较新版本的 MySQL 客户端发行版不包含 r 版本 有些可能有从 libmyqslclient r a 到 libmyqslclient a 的
  • 使用Perl/DBI/MySQL/InnoDB查找外键信息

    我想以编程方式查找 MySQL 数据库中特定 InnoDB 表的外键 我正在使用 Perl 我偶然发现 dbh gt foreign key info 我刚刚尝试使用它 但似乎有点错误 它不会返回 ON DELETE 和 ON UPDATE
  • 如何在 MySQL 中测试 Select for Update

    我正在表演SELECT FOR UPDATE或 InnoDB 表的行级锁定 我的目的是只有一个请求可以读取同一行 因此 如果两个用户同时请求相同的数据 其中只有一个人获取数据 即第一个触发查询的人 但是我如何测试锁定是否已放置 因为我正在通
  • 用 pandas DataFrame 替换 mysql 数据库表中的行

    Python 版本 2 7 6 熊猫版本 0 17 1 MySQLdb 版本 1 2 5 在我的数据库中 PRODUCT 我有一张桌子 XML FEED 表 XML FEED 很大 数百万条记录 我有一个 pandas DataFrame

随机推荐