MySQL缓存方案

  • 一、MySQL缓存方案目的分析
    • 1.1、缓存层的作用
    • 1.2、缓存层选择
    • 1.3、场景分析
  • 二、提升MySQL访问性能的方式
    • 2.1、MySQL主从复制
    • 2.2、读写分离
    • 2.3、连接池
    • 2.4、异步连接
  • 三、redis作为主数据库的常用方法
  • 四、缓存方案
    • 4.1、缓存和MySQL一致性状态分析
    • 4.2、制定读写策略
  • 五、同步方案
    • 5.1、canal
    • 5.2、go-mysql-transfer
  • 六、缓存方案的故障问题及解决
    • 6.1、缓存穿透
    • 6.2、缓存击穿
    • 6.3、缓存雪崩
    • 6.4、缓存方案的弊端
  • 七、总结

前提:读多写少,单个主节点能支撑项目数据量;数据的主要依据是mysql。

一、MySQL缓存方案目的分析

mysql 有自己缓冲层,它的作用也是用来缓存热点数据,这些数据包括索引、记录等。mysql 缓冲层是从自身出发,跟具体的业务无关。这里的缓冲策略主要是 lru。

mysql 数据主要存储在磁盘当中,适合大量重要数据的存储;磁盘当中的数据一般是远大于内存当中的数据。
一般业务场景的关系型数据库(mysql)是作为主要数据库的。

1.1、缓存层的作用

MySQL缓存方案用来缓存用户定义的热点数据 ,用户直接从缓存获取热点数据,降低数据库的读写压力。

1.2、缓存层选择

缓存数据库可以选用 redis,memcached;它们所有数据都存储在内存当中,当然也可以将内存当中的数据持久化到磁盘当中。

#mermaid-svg-BcnIdhBD3gK9caQd {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-BcnIdhBD3gK9caQd .error-icon{fill:#552222;}#mermaid-svg-BcnIdhBD3gK9caQd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BcnIdhBD3gK9caQd .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-BcnIdhBD3gK9caQd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BcnIdhBD3gK9caQd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BcnIdhBD3gK9caQd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BcnIdhBD3gK9caQd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BcnIdhBD3gK9caQd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BcnIdhBD3gK9caQd .marker.cross{stroke:#333333;}#mermaid-svg-BcnIdhBD3gK9caQd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BcnIdhBD3gK9caQd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BcnIdhBD3gK9caQd .cluster-label text{fill:#333;}#mermaid-svg-BcnIdhBD3gK9caQd .cluster-label span{color:#333;}#mermaid-svg-BcnIdhBD3gK9caQd .label text,#mermaid-svg-BcnIdhBD3gK9caQd span{fill:#333;color:#333;}#mermaid-svg-BcnIdhBD3gK9caQd .node rect,#mermaid-svg-BcnIdhBD3gK9caQd .node circle,#mermaid-svg-BcnIdhBD3gK9caQd .node ellipse,#mermaid-svg-BcnIdhBD3gK9caQd .node polygon,#mermaid-svg-BcnIdhBD3gK9caQd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BcnIdhBD3gK9caQd .node .label{text-align:center;}#mermaid-svg-BcnIdhBD3gK9caQd .node.clickable{cursor:pointer;}#mermaid-svg-BcnIdhBD3gK9caQd .arrowheadPath{fill:#333333;}#mermaid-svg-BcnIdhBD3gK9caQd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BcnIdhBD3gK9caQd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BcnIdhBD3gK9caQd .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-BcnIdhBD3gK9caQd .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-BcnIdhBD3gK9caQd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BcnIdhBD3gK9caQd .cluster text{fill:#333;}#mermaid-svg-BcnIdhBD3gK9caQd .cluster span{color:#333;}#mermaid-svg-BcnIdhBD3gK9caQd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BcnIdhBD3gK9caQd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

服务器
MySQL
redis

1.3、场景分析

(1)内存访问速度是磁盘访问速度10W倍,访问磁盘的速度比较慢,尽量使获取数据是从内存中获取。
(2)读的需求远远大于写的需求。主要解决读的性能;因为写没必要优化,必须让数据正确的落盘。如果写性能出现问题,那么请使用横向扩展集群方式来解决。
(3)MySQL自身缓冲层跟业务无关。由于 mysql 的缓冲层不由用户来控制,也就是不能由用户来控制缓存具体数据。
(4)MySQL作为项目主要数据库,便于统计分析。项目中需要存储的数据应该远大于内存的容量,同时需要进行数据统计分析,所以数据存储获取的依据应该是关系型数据库。
(5)缓存数据库作为辅助数据库,存放热点数据。缓存数据库可以存储用户自定义的热点数据。

二、提升MySQL访问性能的方式

(1)读写分离。
(2)连接池。
(3)异步连接。

(4)预处理。
(5)更换存储引擎。
(6)分库分表。(淘汰的技术)
(7)mycat。(淘汰的技术)
(8)tidb。

2.1、MySQL主从复制

  1. 主库更新事件 ( update、insert、delete ) 通过 io-thread写到 binlog。
  2. 从库请求读取 binlog,通过 io-thread 写入从库本地 relaylog(中继日志)。
  3. 从库通过 sql-thread 读取 relay-log,并把更新事件在从库中重放(replay)一遍。

复制流程:

  1. Slave 上面的 IO 线程连接上 Master,并请求从指定日志文件的指定位置(或者从最开始的日志)之后的日志内容。
  2. Master 接收到来自 Slave 的 IO 线程的请求后,负责复制的IO 线程会根据请求信息读取日志指定位置之后的日志信息,返回给 Slave 的 IO 线程。返回信息中除了日志所包含的信息之外,还包括本次返回的信息已经到 Master 端的 binlog 文件的名称以及 binlog 的位置。
  3. Slave 的 IO 线程接收到信息后,将接收到的日志内容依次添加到 Slave 端的 relay-log 文件的最末端,并将读取到的
    Master 端的 binlog 的文件名和位置记录到master-info 文件中,以便在下一次读取的时候能够清楚的告诉 Master 从何
    处开始读取日志。
  4. Slave 的 sql 进程检测到 relay-log 中新增加了内容后,会马上解析 relay-log 的内容成为在 Master 端真实执行时候的那
    些可执行的内容,并在自身执行。

由于MySQL的主从复制是异步的,所以同一时刻主数据库和从数据库的数据可能存在不一致的现象,这就造成可能从数据库中读取的数据不是最新的。

2.2、读写分离

#mermaid-svg-vngqyzLIxKRET2Cz {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-vngqyzLIxKRET2Cz .error-icon{fill:#552222;}#mermaid-svg-vngqyzLIxKRET2Cz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vngqyzLIxKRET2Cz .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-vngqyzLIxKRET2Cz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vngqyzLIxKRET2Cz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vngqyzLIxKRET2Cz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vngqyzLIxKRET2Cz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vngqyzLIxKRET2Cz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vngqyzLIxKRET2Cz .marker.cross{stroke:#333333;}#mermaid-svg-vngqyzLIxKRET2Cz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vngqyzLIxKRET2Cz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vngqyzLIxKRET2Cz .cluster-label text{fill:#333;}#mermaid-svg-vngqyzLIxKRET2Cz .cluster-label span{color:#333;}#mermaid-svg-vngqyzLIxKRET2Cz .label text,#mermaid-svg-vngqyzLIxKRET2Cz span{fill:#333;color:#333;}#mermaid-svg-vngqyzLIxKRET2Cz .node rect,#mermaid-svg-vngqyzLIxKRET2Cz .node circle,#mermaid-svg-vngqyzLIxKRET2Cz .node ellipse,#mermaid-svg-vngqyzLIxKRET2Cz .node polygon,#mermaid-svg-vngqyzLIxKRET2Cz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vngqyzLIxKRET2Cz .node .label{text-align:center;}#mermaid-svg-vngqyzLIxKRET2Cz .node.clickable{cursor:pointer;}#mermaid-svg-vngqyzLIxKRET2Cz .arrowheadPath{fill:#333333;}#mermaid-svg-vngqyzLIxKRET2Cz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vngqyzLIxKRET2Cz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vngqyzLIxKRET2Cz .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-vngqyzLIxKRET2Cz .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-vngqyzLIxKRET2Cz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vngqyzLIxKRET2Cz .cluster text{fill:#333;}#mermaid-svg-vngqyzLIxKRET2Cz .cluster span{color:#333;}#mermaid-svg-vngqyzLIxKRET2Cz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vngqyzLIxKRET2Cz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

DML操作
复制
复制
复制
复制
应用层

读写分离会设置多个从数据库,从数据库可能会在多个机器中。

写操作依然在主数据库中,主数据库提供数据的主要依据。

读写分离通过设置多个从数据库解决读压力。

读写分离主要依据MySQL的主从复制原理,因为MySQL的主从复制是异步复制的,所以读写分离只能保证数据的最终一致性,不能保证实时一致性。
如果读操作有强一致性要求,那么需要读操作去读主数据库。

2.3、连接池

连接池的定义:在服务端当中创建多个与数据库的连接线程。
解决的问题:并发提升数据库访问性能;同时复用连接,避免连接建立、断开依据安全验证的开销。
原理:利用MySQL的网络模型创建多个连接,每个连接复用去出路SQL语句。值得注意的是,如果发送一个事务(多条SQL语句),这个事务必须要在一个连接里面完成。

2.4、异步连接

在服务端创建一个连接,针对这个连接采用非阻塞IO。这种方式可以节省网络传输时间。

三、redis作为主数据库的常用方法

  1. 以redis为主,在redis中读写数据,MySQL作为数据备份,过程中可能需要分布式消息队列(kafka)进行异步同步。这种方式性能最高,但安全性较差。仅适合小项目。工程中要会在效率和安全直接做权衡。
#mermaid-svg-NNo8FbtS9xLA8Hae {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae .error-icon{fill:#552222;}#mermaid-svg-NNo8FbtS9xLA8Hae .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NNo8FbtS9xLA8Hae .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-NNo8FbtS9xLA8Hae .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NNo8FbtS9xLA8Hae .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NNo8FbtS9xLA8Hae .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NNo8FbtS9xLA8Hae .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NNo8FbtS9xLA8Hae .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NNo8FbtS9xLA8Hae .marker.cross{stroke:#333333;}#mermaid-svg-NNo8FbtS9xLA8Hae svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NNo8FbtS9xLA8Hae .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae .cluster-label text{fill:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae .cluster-label span{color:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae .label text,#mermaid-svg-NNo8FbtS9xLA8Hae span{fill:#333;color:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae .node rect,#mermaid-svg-NNo8FbtS9xLA8Hae .node circle,#mermaid-svg-NNo8FbtS9xLA8Hae .node ellipse,#mermaid-svg-NNo8FbtS9xLA8Hae .node polygon,#mermaid-svg-NNo8FbtS9xLA8Hae .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NNo8FbtS9xLA8Hae .node .label{text-align:center;}#mermaid-svg-NNo8FbtS9xLA8Hae .node.clickable{cursor:pointer;}#mermaid-svg-NNo8FbtS9xLA8Hae .arrowheadPath{fill:#333333;}#mermaid-svg-NNo8FbtS9xLA8Hae .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NNo8FbtS9xLA8Hae .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NNo8FbtS9xLA8Hae .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-NNo8FbtS9xLA8Hae .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-NNo8FbtS9xLA8Hae .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NNo8FbtS9xLA8Hae .cluster text{fill:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae .cluster span{color:#333;}#mermaid-svg-NNo8FbtS9xLA8Hae div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NNo8FbtS9xLA8Hae :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

数据备份
server
MySQL
redis
kafka
  1. 针对redis持久化较差的情况,最早使用leveldb伪装成从数据库,不断从redis中获取数据来实时持久化。
#mermaid-svg-QfARtRGmzHlKQDsf {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-QfARtRGmzHlKQDsf .error-icon{fill:#552222;}#mermaid-svg-QfARtRGmzHlKQDsf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QfARtRGmzHlKQDsf .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-QfARtRGmzHlKQDsf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QfARtRGmzHlKQDsf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QfARtRGmzHlKQDsf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QfARtRGmzHlKQDsf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QfARtRGmzHlKQDsf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QfARtRGmzHlKQDsf .marker.cross{stroke:#333333;}#mermaid-svg-QfARtRGmzHlKQDsf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QfARtRGmzHlKQDsf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-QfARtRGmzHlKQDsf .cluster-label text{fill:#333;}#mermaid-svg-QfARtRGmzHlKQDsf .cluster-label span{color:#333;}#mermaid-svg-QfARtRGmzHlKQDsf .label text,#mermaid-svg-QfARtRGmzHlKQDsf span{fill:#333;color:#333;}#mermaid-svg-QfARtRGmzHlKQDsf .node rect,#mermaid-svg-QfARtRGmzHlKQDsf .node circle,#mermaid-svg-QfARtRGmzHlKQDsf .node ellipse,#mermaid-svg-QfARtRGmzHlKQDsf .node polygon,#mermaid-svg-QfARtRGmzHlKQDsf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QfARtRGmzHlKQDsf .node .label{text-align:center;}#mermaid-svg-QfARtRGmzHlKQDsf .node.clickable{cursor:pointer;}#mermaid-svg-QfARtRGmzHlKQDsf .arrowheadPath{fill:#333333;}#mermaid-svg-QfARtRGmzHlKQDsf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QfARtRGmzHlKQDsf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QfARtRGmzHlKQDsf .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-QfARtRGmzHlKQDsf .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-QfARtRGmzHlKQDsf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QfARtRGmzHlKQDsf .cluster text{fill:#333;}#mermaid-svg-QfARtRGmzHlKQDsf .cluster span{color:#333;}#mermaid-svg-QfARtRGmzHlKQDsf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-QfARtRGmzHlKQDsf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

获取数据
返回数据
server
redis
leveldb
持久化

随着技术提升,leveldb的方式被淘汰,使用了更完善的pika方式。pika内部使用的rocksdb,支持redis协议。

#mermaid-svg-xQwbq2pYeUWpGduz {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xQwbq2pYeUWpGduz .error-icon{fill:#552222;}#mermaid-svg-xQwbq2pYeUWpGduz .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xQwbq2pYeUWpGduz .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-xQwbq2pYeUWpGduz .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xQwbq2pYeUWpGduz .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xQwbq2pYeUWpGduz .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xQwbq2pYeUWpGduz .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xQwbq2pYeUWpGduz .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xQwbq2pYeUWpGduz .marker.cross{stroke:#333333;}#mermaid-svg-xQwbq2pYeUWpGduz svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xQwbq2pYeUWpGduz .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xQwbq2pYeUWpGduz .cluster-label text{fill:#333;}#mermaid-svg-xQwbq2pYeUWpGduz .cluster-label span{color:#333;}#mermaid-svg-xQwbq2pYeUWpGduz .label text,#mermaid-svg-xQwbq2pYeUWpGduz span{fill:#333;color:#333;}#mermaid-svg-xQwbq2pYeUWpGduz .node rect,#mermaid-svg-xQwbq2pYeUWpGduz .node circle,#mermaid-svg-xQwbq2pYeUWpGduz .node ellipse,#mermaid-svg-xQwbq2pYeUWpGduz .node polygon,#mermaid-svg-xQwbq2pYeUWpGduz .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xQwbq2pYeUWpGduz .node .label{text-align:center;}#mermaid-svg-xQwbq2pYeUWpGduz .node.clickable{cursor:pointer;}#mermaid-svg-xQwbq2pYeUWpGduz .arrowheadPath{fill:#333333;}#mermaid-svg-xQwbq2pYeUWpGduz .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xQwbq2pYeUWpGduz .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xQwbq2pYeUWpGduz .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-xQwbq2pYeUWpGduz .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-xQwbq2pYeUWpGduz .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xQwbq2pYeUWpGduz .cluster text{fill:#333;}#mermaid-svg-xQwbq2pYeUWpGduz .cluster span{color:#333;}#mermaid-svg-xQwbq2pYeUWpGduz div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xQwbq2pYeUWpGduz :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

server
pika

四、缓存方案

4.1、缓存和MySQL一致性状态分析

没有缓冲层之前,对数据的读写都是基于 mysql;所以不存在同步问题;这句话也不是必然,比如读写分离就存在同步问题(数据一致性问题)。
引入缓冲层后,对数据的获取需要分别操作缓存数据库和mysql,那么这个时候数据可能存在以下状态:

  1. mysql 有,缓存无。
  2. mysql 无,缓存有。
  3. 都有,但数据不一致。
  4. 都有,数据一致。
  5. 都没有。
#mermaid-svg-SeuGnY2to5PVQMOt {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SeuGnY2to5PVQMOt .error-icon{fill:#552222;}#mermaid-svg-SeuGnY2to5PVQMOt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SeuGnY2to5PVQMOt .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-SeuGnY2to5PVQMOt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SeuGnY2to5PVQMOt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SeuGnY2to5PVQMOt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SeuGnY2to5PVQMOt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SeuGnY2to5PVQMOt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SeuGnY2to5PVQMOt .marker.cross{stroke:#333333;}#mermaid-svg-SeuGnY2to5PVQMOt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SeuGnY2to5PVQMOt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SeuGnY2to5PVQMOt .cluster-label text{fill:#333;}#mermaid-svg-SeuGnY2to5PVQMOt .cluster-label span{color:#333;}#mermaid-svg-SeuGnY2to5PVQMOt .label text,#mermaid-svg-SeuGnY2to5PVQMOt span{fill:#333;color:#333;}#mermaid-svg-SeuGnY2to5PVQMOt .node rect,#mermaid-svg-SeuGnY2to5PVQMOt .node circle,#mermaid-svg-SeuGnY2to5PVQMOt .node ellipse,#mermaid-svg-SeuGnY2to5PVQMOt .node polygon,#mermaid-svg-SeuGnY2to5PVQMOt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SeuGnY2to5PVQMOt .node .label{text-align:center;}#mermaid-svg-SeuGnY2to5PVQMOt .node.clickable{cursor:pointer;}#mermaid-svg-SeuGnY2to5PVQMOt .arrowheadPath{fill:#333333;}#mermaid-svg-SeuGnY2to5PVQMOt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SeuGnY2to5PVQMOt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SeuGnY2to5PVQMOt .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-SeuGnY2to5PVQMOt .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-SeuGnY2to5PVQMOt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SeuGnY2to5PVQMOt .cluster text{fill:#333;}#mermaid-svg-SeuGnY2to5PVQMOt .cluster span{color:#333;}#mermaid-svg-SeuGnY2to5PVQMOt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SeuGnY2to5PVQMOt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

缓存和MySQL一致性状态分析
MySQL有,redis无
MySQL无,redis有
都有,但数据不一致
都有,数据一致
都没有

4 和 5显然是没问题的,现在需要考虑1、2以及3。

首先明确一点:获取数据的主要依据是 mysql,只需要将mysql 的数据正确同步到缓存数据库就可以了。
同理,缓存有,mysql 没有,这比较危险,此时可以认为该数据为脏数据;所以需要在同步策略中避免该情况发生;同时可能存在mysql 和缓存都有数据,但是数据不一致,这种也需要在同步策略中避免。

注意:以MySQL为主,保证缓存不可用,整个系统依然要保持正常工作;mysql 不可用的话,系统停摆,停止对外提供服务。

4.2、制定读写策略

#mermaid-svg-l17BAfNoZ2D73bbw {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-l17BAfNoZ2D73bbw .error-icon{fill:#552222;}#mermaid-svg-l17BAfNoZ2D73bbw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-l17BAfNoZ2D73bbw .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-l17BAfNoZ2D73bbw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-l17BAfNoZ2D73bbw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-l17BAfNoZ2D73bbw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-l17BAfNoZ2D73bbw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-l17BAfNoZ2D73bbw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-l17BAfNoZ2D73bbw .marker.cross{stroke:#333333;}#mermaid-svg-l17BAfNoZ2D73bbw svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-l17BAfNoZ2D73bbw .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-l17BAfNoZ2D73bbw .cluster-label text{fill:#333;}#mermaid-svg-l17BAfNoZ2D73bbw .cluster-label span{color:#333;}#mermaid-svg-l17BAfNoZ2D73bbw .label text,#mermaid-svg-l17BAfNoZ2D73bbw span{fill:#333;color:#333;}#mermaid-svg-l17BAfNoZ2D73bbw .node rect,#mermaid-svg-l17BAfNoZ2D73bbw .node circle,#mermaid-svg-l17BAfNoZ2D73bbw .node ellipse,#mermaid-svg-l17BAfNoZ2D73bbw .node polygon,#mermaid-svg-l17BAfNoZ2D73bbw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-l17BAfNoZ2D73bbw .node .label{text-align:center;}#mermaid-svg-l17BAfNoZ2D73bbw .node.clickable{cursor:pointer;}#mermaid-svg-l17BAfNoZ2D73bbw .arrowheadPath{fill:#333333;}#mermaid-svg-l17BAfNoZ2D73bbw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-l17BAfNoZ2D73bbw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-l17BAfNoZ2D73bbw .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-l17BAfNoZ2D73bbw .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-l17BAfNoZ2D73bbw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-l17BAfNoZ2D73bbw .cluster text{fill:#333;}#mermaid-svg-l17BAfNoZ2D73bbw .cluster span{color:#333;}#mermaid-svg-l17BAfNoZ2D73bbw div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-l17BAfNoZ2D73bbw :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

同步问题
读策略
写策略
安全优先
效率优先

读策略:先读缓存,若缓存有,直接返回;若缓存没有,读mysql;若 mysql 有,同步到缓存,并返回;若 mysql 没有,则返回没有。

#mermaid-svg-inGCm5G9v3w3RGm3 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 .error-icon{fill:#552222;}#mermaid-svg-inGCm5G9v3w3RGm3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-inGCm5G9v3w3RGm3 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-inGCm5G9v3w3RGm3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-inGCm5G9v3w3RGm3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-inGCm5G9v3w3RGm3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-inGCm5G9v3w3RGm3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-inGCm5G9v3w3RGm3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-inGCm5G9v3w3RGm3 .marker.cross{stroke:#333333;}#mermaid-svg-inGCm5G9v3w3RGm3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-inGCm5G9v3w3RGm3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 .cluster-label text{fill:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 .cluster-label span{color:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 .label text,#mermaid-svg-inGCm5G9v3w3RGm3 span{fill:#333;color:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 .node rect,#mermaid-svg-inGCm5G9v3w3RGm3 .node circle,#mermaid-svg-inGCm5G9v3w3RGm3 .node ellipse,#mermaid-svg-inGCm5G9v3w3RGm3 .node polygon,#mermaid-svg-inGCm5G9v3w3RGm3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-inGCm5G9v3w3RGm3 .node .label{text-align:center;}#mermaid-svg-inGCm5G9v3w3RGm3 .node.clickable{cursor:pointer;}#mermaid-svg-inGCm5G9v3w3RGm3 .arrowheadPath{fill:#333333;}#mermaid-svg-inGCm5G9v3w3RGm3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-inGCm5G9v3w3RGm3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-inGCm5G9v3w3RGm3 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-inGCm5G9v3w3RGm3 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-inGCm5G9v3w3RGm3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-inGCm5G9v3w3RGm3 .cluster text{fill:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 .cluster span{color:#333;}#mermaid-svg-inGCm5G9v3w3RGm3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-inGCm5G9v3w3RGm3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

Y
N
N
server
redis
判断数据存在与否
返回
读MySQL
判断数据存在与否
Y
同步数据到redis

写策略:从安全优先方面考虑;先删除缓存,再写 mysql,后面数据同步交由 go-mysql-transfer 等中间件处理(将问题 3 转化成 1)。
先删除缓存,为了避免其他服务读取旧的数据;也是告知系统这个数据已经不是最新,建议从 mysql 获取数据。但是对于服务 A 而言,写入 mysql 后,接着读操作必须要能读到最新的数据。

#mermaid-svg-rtDj6kGMH5awUdjO {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-rtDj6kGMH5awUdjO .error-icon{fill:#552222;}#mermaid-svg-rtDj6kGMH5awUdjO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rtDj6kGMH5awUdjO .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-rtDj6kGMH5awUdjO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rtDj6kGMH5awUdjO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rtDj6kGMH5awUdjO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rtDj6kGMH5awUdjO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rtDj6kGMH5awUdjO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rtDj6kGMH5awUdjO .marker.cross{stroke:#333333;}#mermaid-svg-rtDj6kGMH5awUdjO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rtDj6kGMH5awUdjO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rtDj6kGMH5awUdjO .cluster-label text{fill:#333;}#mermaid-svg-rtDj6kGMH5awUdjO .cluster-label span{color:#333;}#mermaid-svg-rtDj6kGMH5awUdjO .label text,#mermaid-svg-rtDj6kGMH5awUdjO span{fill:#333;color:#333;}#mermaid-svg-rtDj6kGMH5awUdjO .node rect,#mermaid-svg-rtDj6kGMH5awUdjO .node circle,#mermaid-svg-rtDj6kGMH5awUdjO .node ellipse,#mermaid-svg-rtDj6kGMH5awUdjO .node polygon,#mermaid-svg-rtDj6kGMH5awUdjO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rtDj6kGMH5awUdjO .node .label{text-align:center;}#mermaid-svg-rtDj6kGMH5awUdjO .node.clickable{cursor:pointer;}#mermaid-svg-rtDj6kGMH5awUdjO .arrowheadPath{fill:#333333;}#mermaid-svg-rtDj6kGMH5awUdjO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rtDj6kGMH5awUdjO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rtDj6kGMH5awUdjO .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-rtDj6kGMH5awUdjO .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-rtDj6kGMH5awUdjO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rtDj6kGMH5awUdjO .cluster text{fill:#333;}#mermaid-svg-rtDj6kGMH5awUdjO .cluster span{color:#333;}#mermaid-svg-rtDj6kGMH5awUdjO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rtDj6kGMH5awUdjO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

删除redis对应数据
同步数据
server
MySQL
redis
同步策略

写策略:从效率优先方面考虑;先写缓存,并设置过期时间(如 200ms),再写mysql,后面数据同步交由其他中间件处理。
这里设置的过期时间是预估时间,大致上是 mysql 到缓存同步的时间。在写的过程中如果 mysql 停止服务,或数据没写入 mysql,则200 ms 内提供了脏数据服务;但仅仅只有 200ms 的数据错乱,即效率优先的写策略也有安全性的问题,但只会影响200ms。

#mermaid-svg-7wnnGmy3tPedbz1X {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-7wnnGmy3tPedbz1X .error-icon{fill:#552222;}#mermaid-svg-7wnnGmy3tPedbz1X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7wnnGmy3tPedbz1X .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-7wnnGmy3tPedbz1X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7wnnGmy3tPedbz1X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7wnnGmy3tPedbz1X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7wnnGmy3tPedbz1X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7wnnGmy3tPedbz1X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7wnnGmy3tPedbz1X .marker.cross{stroke:#333333;}#mermaid-svg-7wnnGmy3tPedbz1X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7wnnGmy3tPedbz1X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7wnnGmy3tPedbz1X .cluster-label text{fill:#333;}#mermaid-svg-7wnnGmy3tPedbz1X .cluster-label span{color:#333;}#mermaid-svg-7wnnGmy3tPedbz1X .label text,#mermaid-svg-7wnnGmy3tPedbz1X span{fill:#333;color:#333;}#mermaid-svg-7wnnGmy3tPedbz1X .node rect,#mermaid-svg-7wnnGmy3tPedbz1X .node circle,#mermaid-svg-7wnnGmy3tPedbz1X .node ellipse,#mermaid-svg-7wnnGmy3tPedbz1X .node polygon,#mermaid-svg-7wnnGmy3tPedbz1X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7wnnGmy3tPedbz1X .node .label{text-align:center;}#mermaid-svg-7wnnGmy3tPedbz1X .node.clickable{cursor:pointer;}#mermaid-svg-7wnnGmy3tPedbz1X .arrowheadPath{fill:#333333;}#mermaid-svg-7wnnGmy3tPedbz1X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7wnnGmy3tPedbz1X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7wnnGmy3tPedbz1X .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-7wnnGmy3tPedbz1X .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-7wnnGmy3tPedbz1X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7wnnGmy3tPedbz1X .cluster text{fill:#333;}#mermaid-svg-7wnnGmy3tPedbz1X .cluster span{color:#333;}#mermaid-svg-7wnnGmy3tPedbz1X div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7wnnGmy3tPedbz1X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

设置key过期时间
过期
同步数据
数据同步成功
数据同步失败
server
redis
200ms
删除脏数据
同步策略
MySQL

五、同步方案

同步方案可以有:
(1)伪装从数据库。比如阿里开源的canal方案、kafka、go-mysql-transfer等。
(2)MySQL的触发器+udf。udf全称User-defined function,是MySQL提供的一种可扩展代码。UDF不具备事务,不能回滚;而且效率较低。

5.1、canal

canal会考虑分布式问题,如果一个canal宕机了,会有从canal顶替上来,保证服务正常提供。

#mermaid-svg-VbdurBIW3SXkSRLY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-VbdurBIW3SXkSRLY .error-icon{fill:#552222;}#mermaid-svg-VbdurBIW3SXkSRLY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VbdurBIW3SXkSRLY .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-VbdurBIW3SXkSRLY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VbdurBIW3SXkSRLY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VbdurBIW3SXkSRLY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VbdurBIW3SXkSRLY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VbdurBIW3SXkSRLY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VbdurBIW3SXkSRLY .marker.cross{stroke:#333333;}#mermaid-svg-VbdurBIW3SXkSRLY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VbdurBIW3SXkSRLY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VbdurBIW3SXkSRLY .cluster-label text{fill:#333;}#mermaid-svg-VbdurBIW3SXkSRLY .cluster-label span{color:#333;}#mermaid-svg-VbdurBIW3SXkSRLY .label text,#mermaid-svg-VbdurBIW3SXkSRLY span{fill:#333;color:#333;}#mermaid-svg-VbdurBIW3SXkSRLY .node rect,#mermaid-svg-VbdurBIW3SXkSRLY .node circle,#mermaid-svg-VbdurBIW3SXkSRLY .node ellipse,#mermaid-svg-VbdurBIW3SXkSRLY .node polygon,#mermaid-svg-VbdurBIW3SXkSRLY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VbdurBIW3SXkSRLY .node .label{text-align:center;}#mermaid-svg-VbdurBIW3SXkSRLY .node.clickable{cursor:pointer;}#mermaid-svg-VbdurBIW3SXkSRLY .arrowheadPath{fill:#333333;}#mermaid-svg-VbdurBIW3SXkSRLY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VbdurBIW3SXkSRLY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VbdurBIW3SXkSRLY .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-VbdurBIW3SXkSRLY .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-VbdurBIW3SXkSRLY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VbdurBIW3SXkSRLY .cluster text{fill:#333;}#mermaid-svg-VbdurBIW3SXkSRLY .cluster span{color:#333;}#mermaid-svg-VbdurBIW3SXkSRLY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VbdurBIW3SXkSRLY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

同步方案
同步
同步
canal client
canal
server
MySQL
redis

5.2、go-mysql-transfer

go-mysql-transfer只有一个节点,相对canal简单些,没有解决分布式问题。要增强go-mysql-transfer的高可用,可以引入etcd、zk等。

#mermaid-svg-HE2lKTC4i0QzyNTj {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj .error-icon{fill:#552222;}#mermaid-svg-HE2lKTC4i0QzyNTj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HE2lKTC4i0QzyNTj .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-HE2lKTC4i0QzyNTj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HE2lKTC4i0QzyNTj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HE2lKTC4i0QzyNTj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HE2lKTC4i0QzyNTj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HE2lKTC4i0QzyNTj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HE2lKTC4i0QzyNTj .marker.cross{stroke:#333333;}#mermaid-svg-HE2lKTC4i0QzyNTj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HE2lKTC4i0QzyNTj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj .cluster-label text{fill:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj .cluster-label span{color:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj .label text,#mermaid-svg-HE2lKTC4i0QzyNTj span{fill:#333;color:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj .node rect,#mermaid-svg-HE2lKTC4i0QzyNTj .node circle,#mermaid-svg-HE2lKTC4i0QzyNTj .node ellipse,#mermaid-svg-HE2lKTC4i0QzyNTj .node polygon,#mermaid-svg-HE2lKTC4i0QzyNTj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HE2lKTC4i0QzyNTj .node .label{text-align:center;}#mermaid-svg-HE2lKTC4i0QzyNTj .node.clickable{cursor:pointer;}#mermaid-svg-HE2lKTC4i0QzyNTj .arrowheadPath{fill:#333333;}#mermaid-svg-HE2lKTC4i0QzyNTj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HE2lKTC4i0QzyNTj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HE2lKTC4i0QzyNTj .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-HE2lKTC4i0QzyNTj .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-HE2lKTC4i0QzyNTj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HE2lKTC4i0QzyNTj .cluster text{fill:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj .cluster span{color:#333;}#mermaid-svg-HE2lKTC4i0QzyNTj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HE2lKTC4i0QzyNTj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

同步
同步
server
MySQL
go-mysql-transfer
redis

go-mysql-transfer的项目地址。

操作步骤:
(1)找到MySQL的my.ini配置文件,修改为主从模式。设置binlog-format=ROW和server-id=1。
(2)修改go-mysql-transfer中的app.yml文件的mysql配置和redis连接配置。

# mysql配置
addr: 127.0.0.1:3306
user: root
pass: root
charset : utf8
slave_id: 1001 #slave ID
flavor: mysql #mysql or mariadb,默认mysql#redis连接配置
redis_addrs: 127.0.0.1:6379 #redis地址,多个用逗号分隔
#redis_group_type: cluster   # 集群类型 sentinel或者cluster
#redis_master_name: mymaster # Master节点名称,如果group_type为sentinel则此项不能为空,为cluster此项无效
#redis_pass: 123456 #redis密码
#redis_database: 0  #redis数据库 0-16,默认0。如果group_type为cluster此项无效

(3)修改go-mysql-transfer中的app.yml文件的规则配置,设置热点数据。

#规则配置
rule:-schema: eseap #数据库名称table: t_user #表名称#order_by_column: id #排序字段,存量数据同步时不能为空#column_lower_case:false #列名称转为小写,默认为false#column_upper_case:false#列名称转为大写,默认为falsecolumn_underscore_to_camel: true #列名称下划线转驼峰,默认为false# 包含的列,多值逗号分隔,如:id,name,age,area_id  为空时表示包含全部列#include_columns: ID,USER_NAME,PASSWORD#exclude_columns: BIRTHDAY,MOBIE # 排除掉的列,多值逗号分隔,如:id,name,age,area_id  默认为空#column_mappings: USER_NAME=account    #列名称映射,多个映射关系用逗号分隔,如:USER_NAME=account 表示将字段名USER_NAME映射为account#default_column_values: area_name=合肥  #默认的列-值,多个用逗号分隔,如:source=binlog,area_name=合肥#date_formatter: yyyy-MM-dd #date类型格式化, 不填写默认yyyy-MM-dd#datetime_formatter: yyyy-MM-dd HH:mm:ss #datetime、timestamp类型格式化,不填写默认yyyy-MM-dd HH:mm:ss#lua_file_path: lua/t_user.lua   #lua脚本文件#lua_script:   #lua 脚本value_encoder: json  #值编码,支持json、kv-commas、v-commas;默认为json#value_formatter: '{{.ID}}|{{.USER_NAME}}' # 值格式化表达式,如:{{.ID}}|{{.USER_NAME}},{{.ID}}表示ID字段的值、{{.USER_NAME}}表示USER_NAME字段的值#redis相关redis_structure: string # 数据类型。 支持string、hash、list、set、sortedset类型(与redis的数据类型一致)#redis_key_prefix: USER_ #key的前缀#redis_key_column: USER_NAME #使用哪个列的值作为key,不填写默认使用主键#redis_key_formatter: '{{.ID}}|{{.USER_NAME}}'#redis_key_value: user #KEY的值(固定值);当redis_structure为hash、list、set、sortedset此值不能为空#redis_hash_field_prefix: _CARD_ #hash的field前缀,仅redis_structure为hash时起作用#redis_hash_field_column: Cert_No #使用哪个列的值作为hash的field,仅redis_structure为hash时起作用,不填写默认使用主键#redis_sorted_set_score_column: id #sortedset的score,当数据类型为sortedset时,此项不能为空,此项的值应为数字类型#mongodb相关#mongodb_database: transfer #mongodb database不能为空#mongodb_collection: transfer_test_topic #mongodb collection,可以为空,默认使用表名称#elasticsearch相关#es_index: user_index #Index名称,可以为空,默认使用表(Table)名称#es_mappings: #索引映射,可以为空,为空时根据数据类型自行推导ES推导#  -#    column: REMARK #数据库列名称#    field: remark #映射后的ES字段名称#    type: text #ES字段类型#    analyzer: ik_smart #ES分词器,type为text此项有意义#    #format: #日期格式,type为date此项有意义#  -#    column: USER_NAME #数据库列名称#    field: account #映射后的ES字段名称#    type: keyword #ES字段类型#rocketmq相关#rocketmq_topic: transfer_test_topic #rocketmq topic,可以为空,默认使用表名称#kafka相关#kafka_topic: user_topic #rocketmq topic,可以为空,默认使用表名称#rabbitmq相关#rabbitmq_queue: user_topic #queue名称,可以为空,默认使用表(Table)名称#reserve_raw_data: true #保留update之前的数据,针对rocketmq、kafka、rabbitmq有用;默认为false

(4)写lua同步逻辑。
(5)启动mysql个redis。

六、缓存方案的故障问题及解决

6.1、缓存穿透

假设某个数据 redis 不存在,mysql 也不存在,而且一直尝试读怎么办?缓存穿透,数据最终压力依然堆积在 mysql,可能造成mysql 不堪重负而崩溃。

解决方案:
(1) 发现 mysql 不存在,将 redis 设置为 <key, nil>,设置过期时间 ,下次访问 key 的时候 不再访问 mysql 容易造成 redis 缓存很多无效数据。
2. 布隆过滤器,将 mysql 当中已经存在的 key,写入布隆过滤
器,不存在的直接 pass 掉;
(1)缓存设置 <key,nil>,告诉服务器mysql也没有数据,不用去访问mysql了。这种方式可能占大量内存。
(2)部署布隆过滤器。

6.2、缓存击穿

缓存击穿是某些数据 redis 没有,但是 mysql 有;此时当大量这类数据的并发请求,同样造成 mysql 过大。

#mermaid-svg-xc1okLFhz18ttm0W {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-xc1okLFhz18ttm0W .error-icon{fill:#552222;}#mermaid-svg-xc1okLFhz18ttm0W .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xc1okLFhz18ttm0W .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-xc1okLFhz18ttm0W .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xc1okLFhz18ttm0W .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xc1okLFhz18ttm0W .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xc1okLFhz18ttm0W .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xc1okLFhz18ttm0W .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xc1okLFhz18ttm0W .marker.cross{stroke:#333333;}#mermaid-svg-xc1okLFhz18ttm0W svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xc1okLFhz18ttm0W .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xc1okLFhz18ttm0W .cluster-label text{fill:#333;}#mermaid-svg-xc1okLFhz18ttm0W .cluster-label span{color:#333;}#mermaid-svg-xc1okLFhz18ttm0W .label text,#mermaid-svg-xc1okLFhz18ttm0W span{fill:#333;color:#333;}#mermaid-svg-xc1okLFhz18ttm0W .node rect,#mermaid-svg-xc1okLFhz18ttm0W .node circle,#mermaid-svg-xc1okLFhz18ttm0W .node ellipse,#mermaid-svg-xc1okLFhz18ttm0W .node polygon,#mermaid-svg-xc1okLFhz18ttm0W .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xc1okLFhz18ttm0W .node .label{text-align:center;}#mermaid-svg-xc1okLFhz18ttm0W .node.clickable{cursor:pointer;}#mermaid-svg-xc1okLFhz18ttm0W .arrowheadPath{fill:#333333;}#mermaid-svg-xc1okLFhz18ttm0W .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xc1okLFhz18ttm0W .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xc1okLFhz18ttm0W .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-xc1okLFhz18ttm0W .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-xc1okLFhz18ttm0W .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xc1okLFhz18ttm0W .cluster text{fill:#333;}#mermaid-svg-xc1okLFhz18ttm0W .cluster span{color:#333;}#mermaid-svg-xc1okLFhz18ttm0W div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xc1okLFhz18ttm0W :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}

服务器
mysql
redis
服务器
服务器
服务器

解决方案:
(1) 分布式锁。请求数据的时候获取锁,若获取成功,则操作后释放锁;若获
取失败,则休眠一段时间(200ms)再去获取,当获取成功,操作后释放锁。
(2) 将很热的 key,设置不过期。

6.3、缓存雪崩

表示一段时间内,缓存集中失效(redis 无, mysql 有),导致请求全部走 mysql,有可能搞垮数据库,使整个服务失效。

缓存数据库在整个系统不是必须的,也就是缓存宕机不会影响整个系统提供服务。
解决方案:
(1)如果因为缓存数据库宕机,造成所有数据涌向 mysql。采用高可用的集群方案,如哨兵模式、cluster模式。
(2)如果因为设置了相同的过期时间,造成缓存集中失效。设置随机过期值或者其他机制错开失效时间。
(3) 如果因为系统重启的时候,造成缓存数据消失。重启时间短,redis 开启持久化(过期信息也会持久化)就行了; 重启时间长提前将热数据导入 redis 当中。

6.4、缓存方案的弊端

不能处理多语句的事务。redis不支持回滚,造成redis个MySQL的不一致。

七、总结

  1. binlog的作用是数据备份和主从复制;确保主从数据的一致。
  2. redolog的作用是确保事务持久化,确保本地数据一致。
  3. 缓存方案读策略:先读缓存,存在则直接返回;不存在则去访问MySQL,再写redis。
  4. 缓存方案写策略,从安全为主;先删除缓存层中对应数据,再写MySQL,最后将MySQL数据同步到缓存层。添加缓存层的目的是为了提升效率,这种方式为了安全较低了效率。
  5. 缓存方案写策略,从效率为主;先写缓存层并设置过期时间,再写MySQL,等待MySQL同步到缓存层中。过期时间=MySQL网络传输时间+MySQL处理时间。
  6. 缓存穿透的解决方法有:缓存设置 <key,nil>,告诉服务器mysql也没有数据,不用去访问mysql了;部署布隆过滤器。

MySQL缓存策略详解相关推荐

  1. Android WebView缓存策略详解

    WebView中存在着两种缓存:网页数据缓存(存储打开过的页面及资源).H5缓存(即appcache). 一.网页缓存 1.缓存构成 /data/data/package_name/cache/ /d ...

  2. Redis系列教程(六):Redis缓存和MySQL数据一致性方案详解

    需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库. 这个业务场景,主要 ...

  3. 高并发架构系列:Redis缓存和MySQL数据一致性方案详解

    需求起因 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节.所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库. 这个业务场景,主要 ...

  4. Mysql存储引擎详解(MyISAM与InnoDB的区别)

    Mysql存储引擎详解(MyISAM与InnoDB的区别) 存储引擎     MySQL中的数据用各种不同的技术存储在文件(或者内存)中.这些技术中的每一种技术都使用不同的存储机制.索引技巧.锁定水平 ...

  5. Redis系列教程(九):Redis的内存回收原理,及内存过期淘汰策略详解

    Redis内存回收机制 Redis的内存回收主要围绕以下两个方面: 1.Redis过期策略:删除过期时间的key值 2.Redis淘汰策略:内存使用到达maxmemory上限时触发内存淘汰数据 Red ...

  6. Mysql加锁过程详解(3)-关于mysql 幻读理解

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  7. mysql status改变_mysql 配置详解mysql SHOW STATUS 详解

    1. back_log 指定MySQL可能的连接数量.当MySQL主线程在很短的时间内得到非常多的连接请求,该参数就起作用,之后主线程花些时间(尽管很短)检查连接并且启动一个新线程. back_log ...

  8. Spring Data JPA 从入门到精通~Naming命名策略详解及其实践

    Naming 命名策略详解及其实践 用 JPA 离不开 @Entity 实体,我都知道实体里面有字段映射,而字段映射的方法有两种: 显式命名:在映射配置时,设置的数据库表名.列名等,就是进行显式命名, ...

  9. OKHTTP之缓存配置详解

    前言 在Android开发中我们经常要进行各种网络访问,比如查看各类新闻.查看各种图片.但有一种情形就是我们每次重复发送的网络请求其实返回的内容都是一样的.比如一个电影类APP,每一次向服务器申请某个 ...

最新文章

  1. 2673(2673)shǎ崽 OrOrOrOrz
  2. 查看是否打印GC收集细节
  3. P4151 [WC2011]最大XOR和路径(线性基应用)
  4. MySQL8.0与MySQL5.7 OLTP 性能测试对比
  5. 查看 linux 网络状态命令,Linux操作系统常用的网络状态查询命令
  6. 台积电6月营收环比大增 或预示苹果A14处理器已大规模出货
  7. sip协议的功能及其应用
  8. python4.2_python4.2参数传入
  9. 使用Native API查询Windows硬盘分区系统设备名称
  10. vue 音频文件打包后找不到文件
  11. python读excel表格数据绘制图表_Python读取Excel数据并生成图表过程解析
  12. Chorme浏览器中安装Axure插件的方法
  13. 【CGAL】提取中心线
  14. ROS-Control专题:PR2的六个概念【5】
  15. Html中a标签用法总结:创建email,电话,描点链接等。以及防止链接被搜索引擎收录
  16. 【建议珍藏】2023年最新Android大厂面经分析,最终入职得物
  17. python 传奇服务端_夜光带你走进python开发 (三十二)传奇语言
  18. 2020年2月26日训练日记
  19. 通过图新地球把大疆L1激光雷达点云成果和影像地形等其他GIS数据进行融合
  20. 去广告神器(Adblock Plus离线安装)

热门文章

  1. python黑马教程ppt_,python基础教程 PPT
  2. freemarker模板生成pdf文件
  3. 行业报告归档 2017.3.21
  4. EXPLAIN语法详解
  5. excel如何生成各种图
  6. Dynamics CRM2013 在Visual Studio中开启脚本的Xrm.Page智能提示
  7. createrepo 是一个对rpm 文件进行索引建立的工具
  8. ps系列 -- 给人物添加光影
  9. 低分辨率和畸变严重的棋盘格角点的自动检测
  10. Go语言基础之网络编程