type
status
date
slug
summary
tags
category
icon
password
场景引入
如果你设计了一个类似 B 站的视频网站中的搜索系统,或许在初期 MySQL 的 like 模糊搜已经能满足你对视频的标题、简介的搜索需求。
如果后期系统视频的量级上来了,对性能有一定要求了,你或许已经开始考虑 ES 了。后面如果你还想让与搜索关键词相关的创作者名称、视频评论、关联标签也能级联视频搜出来,甚至弄一个各种业务维度的融合公式,那么 ES 这样的搜索引擎再适合不过了!
但是,如果数据写一份 MySQL,还要落一份 ES 来支撑筛选/搜索,要怎么写入两个异构的存储系统、并保证它们的一致性呢?
方案一:同步双写
优点
- 简单粗暴
- ES 同步写入快
缺点
- 业务耦合,扩展性差,代码侵入性强(要在写 MySQL 的地方后写 ES 的代码,要在代码设计上做好收敛)
- 影响性能,写入两个存储,响应时间变长(本来 MySQL 的写入性能不是很高,再加一个 ES,系统的性能必然会下降),吞吐也会下降
- 存在双写失败丢数据风险(比如写完MySQL,ES 就挂了)
方案二:异步双写
优点
- 性能高
- 不易出现数据丢失问题,主要基于 MQ 消息的消费保障机制,比如 ES 宕机或者写入失败,还能重新消费 MQ 消息
- 多源写入之间相互隔离(业务侧只管发消息),解耦数据生产者和消费者,便于扩展更多的数据源写入
缺点
- 同样存在业务耦合的问题,写完 DB 之后,还需要发个 MQ(代码设计上做好收敛,也可以关注一下所用 orm 框架有没有 hook 之类的能力,支持你“写后 xxx”)
- 接入新的数据源需要实现新的消费者代码
- 系统复杂度增加,引入了消息中间件
- MQ是异步消费模型,存在延迟问题
MQ 可以缓冲和 DB 的负载能力不一致问题。
方案 2.1:使用内存队列
如 golang 的 channel
- 把数据写入DB
- 把数据写入内存队列
- 消费线程异步从队列中消费数据写入ES(可以做批量优化,定时+定量)
优点
简单易实现,无需引入新的外部依赖
缺点
可能会丢消息;存在阻塞风险,容量有限
方案 2.2:使用分布式消息队列
如 RocketMQ、RabbitMQ、Kafka 一类的 MQ 中间件
- 把数据写入DB
- 把数据写入MQ
- 消费线程异步从MQ中消费数据写入ES
优点
数据可靠性高,支持数据持久化,不会出现数据丢失
缺点
系统架构更复杂,需要额外的 MQ 运维成本
方案三:定时同步
开定时任务回扫
优点
- 实现简单
- 适合作为其他方案的补充兜底,用于处理双写中的失败情况
缺点
- 无法保证数据的实时性,存在较大的同步延迟
- 对数据库的存储和计算压力较大,尤其是在数据量大时
方案四:数据订阅
如阿里的 Canal,订阅 MySQL 的 binlog(伪装成DB从节点),发 MQ 消息
优点
- 业务入侵较少
- 实时性较好
缺点
- 系统复杂度增加,引入了消息中间件和额外组件,有一定维护成本(至少得保证 Canal 不能挂掉)
- Author:王帅真
- URL:https://blog.qizong007.top/article/mysql-sync-es
- Copyright:All articles in this blog, except for special statements, adopt BY-NC-SA agreement. Please indicate the source!