目录
- 背景
- redis集群 + mysql兜底
- 优势与劣势
- 对一致性和QPS要求比较低的方案
- 对一致性要求比较高的方案
- 对redis和mysql更新数据一致性要求高的系统
- Canal + ES的方式
- canal整体架构
- ES双集群高可用
背景
- mysql承接的业务,业务逻辑上直接读取多张表,性能已经出现瓶颈,这时候可以考虑加缓存处理
redis集群 + mysql兜底架构
优势与劣势
- redis集群做缓存的方案,相比较Canal + Es(或者CK等)的方案优势在与只需要引入Redis集群这一个中间件,而Canal + Es需要引入两个中间件
- redis集群做缓存的方案劣势就是要处理事务一致性和缓存更新的一致性时技术方案需要更合理的设计,Canal + Es的方案简单很多
- 性能要求再高的可以做二次缓存,本地缓存 + redis缓存,如果对实时性要求没那么高。对实时性要求比较高,那就不加本地缓存
对一致性和QPS要求比较低的方案
- 如果不是高并发的系统,不需要额外处理redis缓存雪崩,缓存击穿,缓存穿透问题的架构设计
-
缓存设计之新增,红色大框的设计是为了解决缓存击穿问题,如果并发不高只需要红色小框set key1 value部分,如果要解决缓存雪崩可以在设计key的时候增加随机缓存时间,缓存雪崩需要增加null key的处理,或者布隆过滤器,按需设计
-
缓存设计之查询,大红框是为了解决缓存击穿问题,如果不需要可以只不用分布式锁,只需要小红框set key就OK
-
缓存设计之更新,网上有很多讨论和论文证明了先更mysql再删除缓存的一致性是最高的,这里不再展开
对一致性要求比较高的方案
- 在新增和更新mysql与redis时,可能会有数据不一致的情况,比如mysql更新成功,redis更新失败。这时候可以使用分布式事务保证最终一致性,在不引入分布式事务框架的前提下,可以使用本地事务消息的形式
-
mysql1和mysql2通过mysql事务保证一致性,当时mysql成功,redis失败时,可通过补偿线程轮询补偿,保证最终一致性
对redis和mysql更新数据一致性要求高的系统
- 在更新请求加分布式锁之前,恰好有一个查询请求获取分布式锁,而此时是没有锁的,所以它可以继续更新缓存。但就在他更新缓存之前,线程block了,此时更新请求来了,加了分布式锁,并删除了缓存。当更新请求完成操作后,查询请求的线程活过来了,此时它再执行更新缓存,就把脏数据写到缓存中了。主要的问题症结就在于删除缓存和更新缓存发生了并发冲突,只要将它们互斥,就能解决问题
-
如果需要保证redis不脏读旧数据,那么需要加入分布式锁处理,这个是保证很强的数据一致性,红框部分互斥
Canal + ES的架构
这边举例ES,根据qps可以替换成CK等集群
-
整体流程
-
Canal本质,伪装成mysql从库,解析binlog,将解析内容发送到mq(其中一种方式),我们自己消费mq,然后写入ES或者别的CK等
高可用canal架构
-
canal client这边也可以直接使用canal内置支持mq发送,不一定需要
canal整体架构
- server 代表一个 canal 运行实例,对应于一个 jvm
- instance 对应于一个数据队列 (1个 canal server 对应 1…n 个 instance )
- instance 下的子模块
- eventParser: 数据源接入,模拟 slave 协议和 master 进行交互,协议解析
- eventSink: Parser 和 Store 链接器,进行数据过滤,加工,分发的工作
- eventStore: 数据存储
- metaManager: 增量订阅 & 消费信息管理器
canal顺序性保证
- 单一数据库实例: 为了保证数据的顺序性,你应该在单个数据库实例上使用Canal。因为在分布式环境下,多个数据库实例之间的数据同步可能会出现数据顺序不一致的情况
- GTID: MySQL的GTID (Global Transaction ID) 可以保证全局的事务顺序。在开始事务时,MySQL会为每个新的事务分配一个唯一的GTID。Canal在同步数据时,会同时传递GTID信息,以此来保证数据的顺序性。
- 消息顺序: Canal是基于阿里巴巴的开源组件RocketMQ实现的,而RocketMQ本身是支持消息顺序性的。在RocketMQ中,可以通过将消息与特定的消费者组关联,或者通过将消息发送到特定的分区,来保证消息的顺序
- 数据校验: 可以通过校验数据的一致性来保证数据的顺序。例如,可以在写入数据后,通过Canal读取刚刚写入的数据,然后与原始数据进行比对,确保数据的完整性和顺序性
- 应用层逻辑: 在应用层,如果需要保证数据的顺序性,可以在处理数据时根据GTID或其他事务信息进行判断和校验。例如,如果一个事务未完成就尝试读取或处理数据,那么应用应该阻止这种操作