MySQL
事务
事务的定义和特性(ACID),mysql怎么实现的事务特性
事务是一批语句的集合,这些集合具备ACID特性, 分别是原子性、一致性、隔离性和持久性。 其中原子性是指事务中的全部操作,要么全部成功,要么全部失败,在innoDB中是以undo log的方式实现的,隔离性是指事务之间的操作应该是相互隔离的,但如果要做到完全隔离的情况需要全过程加锁或串行化执行事务,这种实现方式会大大降低事务的tps,所以事务规范定义了四种隔离级别,读未提交、读已提交、重复性读和串行化。 innoDB内部使用了mvcc快照实现的读已提交和重复性读隔离级别,使用锁实现串行化的隔离级别。 持久性是指事务提交后做出的变化应该是永久的, 即使数据库发生断电或宕机也不影响已提交的事务,innoDB 会将提交的事务更新到磁盘保证持久性。但每次提交事务都更新磁盘会极大增加事务的耗时,所以innoDB提供了wal机制,异步刷盘提高事务的吞吐量,同时WAL提供了redolog机制来保证自己的持久性。 关于一致性网上的解释很多, 有从转账业务前后金额一致来说明的, 也有从数据库约束来说的,这个个人理解不同,在mysql技术内幕里面有句话总结的挺好,一致性是事务执行前后数据库状态应该和描述的现实生活一致。 事务通过其他三个特性来保障一致性的
事务的隔离级别,每个级别可能会发生的问题,innoDB怎么通过mvcc实现的隔离级别
读未提交是指可以读到别的事务未提交的修改,这是最低的隔离级别,等于没隔离--
读已提交是指在事务的执行过程中,可以读取到别的事务已经提交的修改, 可能会导致事务执行过程中读到的数据不一致
可重复读是指在事务执行期间读到的数据都是同一个数据, 不会读取到别的事务提交的修改。
串行化是指事务串行执行,完全隔离。
除了串行化,其余每个级别都可能会发生问题, 主要问题分三类,脏读、不可重复读、幻读,其中脏读是指读到其他事务未提交的修改, 因为可能其余事务有可能会回滚。不可重复读是指事务执行前后过程中,对同一行的数据读到不一样的结果,数据不支持重复读取。 幻读一般用于范围查询, 别的事务删除或修改了数据行,两次范围查询查到的总量不一致。 其中串行化没有任何问题, 可重复读在普通查询场景下, 不会出现问题,但当前读时会出现幻读和不可重复读情况。 读已提交和读未提交级别会出现所有问题。
串行化可以通过加锁实现,读未提交不需要任何干预,读未提交和可重复读是基于mvcc实现的,区别在于判断活跃数组的时机。
innoDB使用mvcc机制实现了非锁定一致性读,基本思路是给每个事务启动一个快照,事务的所有操作都是基于此快照执行的。 具体实现原理并不是copy整库,因为事务涉及到的行往往占的比例很小,大部分都是没有改动的行。 mvcc的原理是在每行后面加两个额外列, 一个是事务id,代表当前行最后一次被哪个事务修改。 另一个是回滚指针队列,每行数据可能被多个事务更新,所以回滚指针按照更新时间顺序依次组织。 innoDB内部会维护一个递增的全局事务id, 每个事务开启时需要从他这申请事务id。以可重复读这一隔离级别为例, 他的定义为可以读取到当前事务开启前已经提交的变更,但读取不到事务过程中其他事务提交的变更。 在事务开启时,全局事务会分配给他一个事务id,他自己会构建一个活跃事务数组,代表着当前未提交的事务,将事务数组递增排序后,可以得到活跃事务的高水平位。 数据库中每行记录中记录的事务id与高水平位配合,就可以判断该行记录是否可见。如果行事务id大于活跃事务高位,说明这个行事务是在本事务开启之后开启的,数据是不可见的。 如果行事务id低于高水平位,可以分为两种情况, 活跃事务数组包含这个行事务id,说明行是由正在执行的事务生成的,不可见, 需要按照回滚指针队列依次回滚,找到符合要求的行记录。 如果活跃事务数组不包含这个行事务id,说明行是由已经提交的事务生成的,是可见的。
读已提交和可重复读都是基于mvcc实现的,区别是可重复读会在事务开启时启动快照,直到事务提交都使用这一个快照, 而读已提交会给每个语句都生成快照, 事务执行过程中会有多个快照。
理论上来说,可重复读会在事务开启时启动一致性快照,后续每个查询都是基于此快照查询。 那么按理说不会出现幻读和不可重复读问题, 因为这些修改的事务id对于当前事务是不可见的,不会出现可见性问题。 但实际的场景往往会发生这两种问题, 因为普通查询是基于视图的一致性读,而数据更新往往是先读再写的, 这个读称为当前读,会直接获取已提交的最近记录,如果给读记录加锁也是当前读。 innoDB这么设计是为了防止覆盖更新的情况,例如两个事务同时对100库存扣减10,如果采用一致性读,事务A提交90后,事务B也提交90,最终库存为90,那事务A的更新就被覆盖了。事务的隔离性不影响影响最终数据的落地,因为在数据库看来,这两个事务就是依次更新的,必须连续在一起不能分开。 不能因为事务的隔离性影响一致性。
锁
mysql中的锁主要是保证并发请求的安全性,根据不同的定义锁有不同的分类, 从加锁范围来说有全局锁,表锁和行锁,从资源访问的角度分为读锁和写锁,也叫共享锁和互斥锁。
全局锁
其中全局锁是通过FTWRL给整库加读写锁,使整库处于只读状态,当初设计主要是用于整库备份使用,但这种锁是极度危险的,因为拒绝执行更新语句,业务几乎停摆,其次如果给从库加全局锁,会导致无法处理主库同步过来的binlog日志,导致主从数据不一致。所以mysql后来提供了mysqldump工具来实现实现非锁定一致性读来做备份,主要是依赖mvcc实现的。
表锁
表锁有两种, 一是普通的表锁,跟全局锁的作用一样。 还有一种是元数据锁,主要解决ddl(data definition languages )和dml语句之间的冲突,避免在查询数据的过程中发生表结构变更导致查询异常。 ddl语句会给表加元数据写锁,其余增删改查会给表加元数据读锁,其中写锁的优先级大于读锁。 在5.6之前的mysql版本中, 元数据锁可能会导致服务不可用。 因为所有等待元数据锁的请求都会被放入一个队列中, 写锁优于读锁, 在写锁执行完之前,所有的读锁都会等待, 如果ddl语句执行时间过长(例如重建表),会阻塞所有写锁,有的客户端还具备失败重试机制,那么就可能造成mysql的线程被占满,服务处于不可用状态, 5.6的时候mysql做了一个online ddl的优化, 主要改动是ddl语句不再长久持有元数据写锁,当表结构变更完成后就退化为读锁, 不影响临时表中数据的copy。至于为什么不直接解锁, 是为了防止另一个ddl写锁执行。
行锁
行锁是各个引擎自己实现的, 不是所有的引擎都支持,myisam就不支持, 行锁的粒度是最小的, 所以他的并发度是最好的,行锁一般会锁主键索引。行锁在语句执行时加锁,在整个事务提交时解锁,这就是两阶段锁。 所以在事务中尽量将需要加锁的语句往后放。 在innoDB中如果一个页中锁定的行过多, 会升级为页锁,减少维护锁的开销。
临键锁,行锁+间隙锁, 间隙锁是innoDB为了解决幻读问题引入的, 当数据更新时,不仅仅要给更新的记录加锁,也要给这行记录的前后间隙加锁, 为了防止这些间隙被插入数据造成幻读问题, 行锁和间隙锁合称临键锁。 临键锁针对不同情况下的加锁处理不一样,当唯一索引的等值查询,退化为行锁,非唯一索引的等值查询,退化为间隙锁。 而且只给访问到的记录加锁, 范围查询会给范围的前后值加锁,如果前后没有值加正负无穷。
意向锁, 这个主要是给表锁与行锁之间的冲突使用的, 如果要给一个表加表锁,需要看一下当前表中是否存在行锁, 但又不可能挨行遍历查看是否加锁,所以引入了意向锁的概念,当一个表加行锁的时候,会给表加意向锁,另一个需要加表锁的事务会直接判断当前表是否存在意向锁, 减少表锁和行锁的冲突检测时间。
mysql中的死锁及处理办法
因为mysql的锁是二阶段提交的,当事务提交时才释放持有的锁。 所有有可能会出现死锁问题, mysql的死锁解决办法有两种,分别是破幻不剥夺条件和环路等待条件, 不剥夺条件是指mysql某一个事务如果请求锁超时, 会失败回滚释放持有的锁。这个时间默认是50s,可以通过参数调整。 还有一个是死锁检测机制, 如果开启这个机制,mysql会检查是否存在环路等待, 如果存在就kill掉进程,破坏这个环路条件。
索引
索引是加速查询的一种方式, mysql中的索引分为主键索引(聚簇索引)和非主键索引(非聚簇索引),区别是主键索引存放完整的行记录, 被索引的是主键。 非主键索引存放主键,被索引的是查询条件。
一般的索引模型有hash表、数组、二叉树、平衡树、跳表、b树、b+树等,各有优缺点,mysql使用b+树存储。
hash表是按照hash函数计算被索引的数据hash值,根据取余的方式确定数据在hash表中的位置。 这种存储特别适合于等值查询,可以做到o(1)时间复杂度,innoDB也提供了自适应hash的方式加速等值查询。 但缺点也挺明显的, 不适合范围查询。
有序数组可以做到较好的范围查询能力,而且等值查询的时间复杂度在o(log n), 但数据的新增和删除往往需要移动大量数据,写入性能较差。
二叉查找树查询性能较高, 写入性能也比较好,但极端情况下可能会退化为单链表,查找的时间复杂度增加到o(n),针对这个退化问题,平衡二叉树有着比较好的解决方案, 他可以动态调整二叉树的子树,使其左右树高相差不会超过1。 这种平衡二叉树的确很适合查询, 但不太适合mysql的索引实现, 因为mysql数据是存在于磁盘的, 树的层高过大可能会导致访问磁盘的次数增多。 为了减少磁盘访问次数, 应该尽可能的降低树的层高,b树是一个多叉树, 每个树节点会存储多个元素,几阶B树就代表着每个节点不超过多少个元素,像红黑树就是一种4阶B树的实现,使用b+树可以将索引压缩到较小的层数,大大减少了磁盘io次数。 但b树的缺点也同样明显, 不支持范围查询,而且非叶子节点也存储数据, 中间的存储占用过高,无法将索引放到内存中加速查询。 所有innoDB使用b+树作为默认的索引存储结构, b+树是对b树的一种改进, 主要改进点有两个,一个是非叶子节点不存储完整数据,完整数据在叶子节点存储, 而且叶子节点使用链表链接,能有效支持范围查询。
联合索引、回表、覆盖索引、唯一索引、索引优化、建索引的建议
联合索引是指在innoDB的索引可以使用多个字段建立,会按照字段定义的顺序依次排序,这么做的好处是可以合并索引, 减少索引占用的空间。
回表是指mysql的二级索引存储的并不是完整的数据记录, 而是主键值,当查询的过程中需要用到当前索引中不包含的数据时,会返回主键索引查询数据。 反之,当要使用的字段在当前索引中全包括,就不需要回表,称之为覆盖索引。
唯一索引是给索引中的值增加唯一性约束,不可重复, 唯一索引将不能使用插入缓冲。 而且唯一索引的等值查询会退化为行锁,因为数据是唯一的。
索引优化,主要是MRR回表缓冲和ICP索引下推, 当某个查询需要多次回表时, innoDB会将需要回表的主键收集起来统一回表,将随机读优化为顺序读,减少io时间。 索引下推是如果查询的条件在索引中包含, 会优先使用索引中的字段过滤, 再回表查询, 减少回表的数量。
建索引的建议,尽量使用自增主键, 从性能方面考虑, 如果是自增主键,数据的增长将会依次在b+树尾端添加数据,是顺序写磁盘,而且发生页分裂时树的调整开销最小。而非自增主键将会随机写磁盘,效率比较低,而且引起页分裂的概率也会增加。 其次从存储方面考虑, 二级索引存储的是主键值,如果使用一些uuid之类的主键会导致二级索引的大小也会增加, 占用内存较高。
加索引的操作, 前缀索引、翻转索引、hash索引
Mysql 架构
连接器
连接器是链接客户端与mysql server的桥梁,负责权限验证、维护链接的功能。 mysql的权限在认证成功建立连接后就固定了,权限变更不会影响到已经建立的链接。 mysql的链接分为两种, 一种是短连接,每次执行完查询都会断开连接,等下次查询时再重新建立连接, 连接的建立一般都会涉及握手、安全验证等较多的操作, 所以一般建议使用长连接, 长连接是链接建立后一直持有链接,通过show processlist可以查看当前链接,如果链接空闲超过指定时长会断开连接, 长连接有个缺点是占用的内存可能一直不释放, 可以采用定期断开的方式,或者mysql提供了一个定时清理的开关, 重置长链接占用的缓存
查询缓存
查询缓存是将sql 和查询结果缓存下来, 下次命中后直接返回。 Mysql 不太建议使用查询缓存,8.0版本后直接将此模块弃用了, 因为sql 的任何变动,以及查询表的任何变动都会让查询缓存失效。 使用率低且占用大量内存。 不过针对于一些不经常变动且查询固定的表, 查询缓存还是有用武之地的,像系统配置表。 mysql提供了一个参数来控制是否开启查询缓存, 用户可以选择开启、关闭、还有一个手动模式,手动模式下默认关闭, 当查询语句中带上sql chche 关键字时才会缓存sql。
分析器
主要是进行词法分析和语法分析, 校验语法是否正确, 查询的表和列是否存在,是否有权限查询。
优化器
将sql进行优化, 包括调整条件顺序使其契合索引, 调整join表等。生成执行计划
执行器
根据执行计划调用指定引擎的数据查询结构, 完成sql执行。
innoDB原理
内存管理机制
innoDB 将部分数据页加载到内存中,使用缓冲池的方式实现, 数据的操作都在内存中进行,可以提高sql语句的执行效率。 缓冲池的设计需要考虑淘汰策略和持久化策略。
淘汰策略,因为硬件的限制, 内存的容量是远远小于磁盘的,所以要保障尽量在缓冲池中存储热点数据,提升缓存命中率。 innoDB采用了改进的lru算法来实现, 传统的lru会将最近使用的数据放在队列尾部,将长久未使用的数据放在队列头部, 当内存不足时从头部淘汰数据。 这种模式某次查询发生全表扫描,加载大量的数据页到内存中,会导致很多热点数据被淘汰,而全表扫描可能只是偶尔执行一次,这种情况下会大大降低缓存命中率, innoDB将lru算法进行了改进, 有点借鉴jvm分代的意思, 他将队列分为两部分,new和old区, 分界点是队列的3/8处,其中新加载的数据进入new区,当数据在new区中呆固定时长没有被淘汰,才会进入old区。 这个分界点和等待时长1s可以根据参数调整。 这样就能避免偶发的大查询使大量热点数据失效的问题。
第二个需要考虑的问题是持久化, 内存中的数据终究还是要写到磁盘的, 因为内存无法完成断电恢复问题。 持久化策略一般有两种, 一种是写完内存立马更新磁盘,当内存和磁盘都写入成功才认定修改成功, 这种方式能极大的保障数据的安全。 但由于每次更新都要进行磁盘的随机io,mysql的并发度会大大降低。 业界一般使用另一种WAL 技术来进行持久化, 基本思路是先写内存和重做日志redo log, 当内存存和更新日志都写入成功就认定事务执行成功。 由另一个线程异步将内存中的数据页持久到磁盘中,这样做可以将磁盘的随机写改为顺序写。 当数据库宕机时,可以通过redolog 来恢复数据。 Redo log 中记录了在磁盘哪个偏移上修改了什么。 Redo log 可能并不是每次都写到磁盘的, 他会先写到redo log buffer 中, 等待特定的时机将redo log 写入磁盘。其中刷redo log的时机有三种, 一是主线程每秒或每10秒刷一次, 二是事务提交时可能会写入,按照flush log commit参数控制, 可以配置每次事务提交都持久化到磁盘。 这个参数有三个选项,0为事务不刷盘,1为立刻刷新到磁盘,2为提交到操作系统,但不保证执行到磁盘,1和2 的区别是1保证持久化到磁盘,2不能保证, 2主要应对数据库系统挂了但是服务器没挂,一般生产中都会将此参数设置为1,保证事务的持久性。 三是mysql 刷脏页时,会将对应的redolog删除。 redolog 可以看成是内存脏页的备份,当数据库宕机时可以通过redolog和两次写来保障。
刷脏页的时机
主要是三个时机, 一是当redo log 的check机制执行redolog淘汰时,对应的脏页要更新到磁盘。
二是缓冲池空间不够用时, 主要是空闲页少于1024个, 或者脏页比例达到75%,会触发刷脏页,当数据页被lru淘汰出内存时,也会触发刷脏页。
还有一个刷新邻接页的特性,当某一个页被刷新时,所在区中的所有页都会被刷新。
一些优化特性
两次写
把内存中的数据页持久化到磁盘, 持久化的过程可能会被中断,导致16k的数据写了4k,一般称为部分写丢失。 如果磁盘中的数据页不可用, 那恢复时将无法通过redo log 恢复。 所以innoDB使用了两次写来解决这个问题, 基本思路是在写这个磁盘数据页之前, 先将数据页做一个备份,备份成功后再更新磁盘数据页。中间会异常的时机有两个, 一个是写脏页备份失败, 一个是写磁盘数据页失败。 当写脏页备份失败时, 磁盘数据页还未发生变更, 可以通过redolog 恢复。 如果写磁盘数据页失败, 可以通过脏页备份和redo log恢复。具体的操作方式是写数据页时,会写入double write buffer, 然后写入共享表空间, 再通过共享表空间分散到各个独立表空间。
自适应hash索引
针对某些热点数据页的等值查询, 会建立hash索引来加快查询。 需要注意的是不会对整个表建hash索引, 他要求等值sql必须连续访问100次, 而且某一热点页被访问了N次, 这个N为页中的数据记录数/16。 可以通过show innoDB status 查看自适应hash索引建立的个数和命中率,如果命中率较低可以通过参数关闭。
插入缓冲
mysql中的主键索引是以主键的方式组织的,如果主键设置为自增或者按照某些算法保障递增的话,写入时将是顺序写磁盘的, 但非主键索引是按照一个或多个列组织的, 绝大部分情况下的写入都是随机写, 所以需要插入缓冲进行批量插入。 innoDB提出了插入缓冲,当索引在缓冲池中,直接更新,如果不在缓冲池中,将写入一个insert buffer中,然后异步merge。 也是将随机写优化为了顺序写,提高了整体的执行效率。需要注意的是此缓冲不能用于唯一索引, 因为在插入缓冲区时, innoDB不会去访问索引来判断是否存在,这会失去唯一索引的意义。 5.7版本将此改造升级为了change buffer, 所有非聚集索引的增删改查都可以缓冲
刷新邻接页
刷脏页时,如果一个脏页被刷新, 他同一个区中的所有脏页也会被刷新, 因为内存和磁盘的数据交换一般是以区的方式进行的,表段区页行五级结构。 一个区是1M,一个页默认16k,一区大概有64个页。
常见问题
为什么mysql底层使用b+树而不使用跳表
b+树和跳表的结构很相似,底层都是链表来支持范围查询, 给链表上方构建索引来支持等值查询, 最关键的区别就是索引的非叶子节点是否可以存多个值, 跳表的索引节点只能存一个值,在底层链表过长的情况下就会导致索引过高,而b+树可以存多个节点, 索引的高度一般在2-4之间。 由于mysql是基于磁盘的数据库系统,索引的高度直接影响磁盘的io次数, 所以mysql使用b+树建立索引, 而redis是内存数据库,不用在意层高,而且跳表对于b+树的优势是索引调整开销小,插入快。
Checkpoint 机制
Checkpoint 是redolog更新机制, redo log大小是固定的,整个redolog 由四个文件以环的方式组织, 其中有两个指针代表空闲指针和已使用指针,空闲指针在前,已使用指针在后, 他俩中间的就是redo log的可用空间,当redolog数据写入时, 已使用指针就会前移, 当已使用指针快追上空闲指针时, 代表redo log空间快满了, 这时候就要暂停用户线程,将一部分redo log移除去,前移空闲指针。 Redo log的前移会将redo log对应内存的脏页刷新。
count(*) 快还是count(1)快
count(字段)会取出字段的所有数据, 然后将非空列排除,count(*) innoDB内部做了优化,不用做判空处理。
所以count(*) ≈ count(1) > count(主键) > count(字段)
mvcc
Mvcc 全称为多版本并发控制, 是innoDB引擎提供的非锁定一致性读, 用来优化读已提交和可重复读这两个隔离级别的并发查询。基本思路是生成一个快照,事务中的普通查询都会基于快照进行。 具体实现原理并没有将整个表备份,而是将每个数据行存储多个版本, 每个事务按照指定的规则读取某个版本的数据。 Mvcc 会生成自增式的全局事务id, 每个事务执行前都需依次申请事务id。 数据行会有两个隐藏列,分别是当前行最后一次更新的事务id, 以及一个回滚指针队列,回滚指针是指向undo log的指针, 记录了某个事务做了哪些修改, 通过回滚指针可以依次恢复每一个版本的行记录。 我们以可重复读级别为例,可重复读的核心是当事务开启后,别的事务的修改对我来说就不可见了。 具体解决方案是,当事务开启的时候,他会从全局事务id那里申请到本次事务id, 同时也会收集当前正在执行的事务id数组,将活跃事务id排序后取得一个高水平位。 在本事务执行的过程中, 如果某一行的事务id高于这个水平位, 说明这个版本的行记录是在当前事务开启之后的事务修改的,本事务不可见。 如果低于高水平位,说明这个修改这个行的记录的事务肯定是已经开启或结束了。 那就可以把这个事务id和活跃事务数组进行比较了, 如果在数组中说明这行记录还未提交, 当前事务不可见, 如果不在活跃事务数组中,说明已经提交,可见。 如果行记录的版本不可见,就需要按照回滚队列依次回滚, 直到找到当前事务可见的行再执行。 读已提交的实现思路和可重复读是一样的,区别是活跃事务数据的开启时机不同,可重复读是在每个事务开启时开启一个, 读已提交是在每个语句执行时都开启一个。
一行update怎么执行的
为了保障redo log 和 binlog 的一致性,mysql 内部使用二阶段提交的方式执行修改过程, 更新完内存后, 先写redo log,写完后会处于prepare阶段, 再写bin log, 写完bin log再将redo log的状态修改为commit ,整个事务才处于修改完成状态。
mysql的缓冲池了解吗
mysql 的缓冲池设计的初衷是减少磁盘读写带来的较大时间开销, 在innoDB中缓冲词主要有两个方面的用处, 一是将单独的读写磁盘缓存起来然后批量读写, 可以将磁盘的随机读写优化为多个顺序读写, 实际的应用主要是索引回表的MRR优化, 以及非唯一索引的插入缓冲。 缓冲池的第二个用处是将一些数据页从磁盘加载到内存中, 因为内存的读写效率是远远高于磁盘的, innoDB会将变更写到内存就返回成功, 由缓存池异步刷新磁盘。 缓冲池主要缓存的是mysql的数据页, 包括索引页、数据页、undo log页等, 设计缓冲池需要考虑几个核心的点, 缓存池的淘汰策略和持久化策略, 淘汰策略是因为内存的容量是远小于磁盘的, 需要设计一种数据淘汰来复用内存, innoDB采用的是改进的lru算法。 持久化策略是解决缓冲池与磁盘不一致的情况, 主要采用WAL机制实现脏页更新。 持久化是指缓冲池同步回磁盘的策略, 需要保障断电恢复问题, innoDB主要采用redolog 和两次写来实现。
场景分库分表方案
Sharding jdbc, mycat, 难点是路由问题。
高可用方案、主从复制延迟问题
高可用,搭主从。 主从复制延迟, 是指主库的binlog 同步到从库需要时间, 从库执行事务也需要时间。
一般binlog的同步延迟可以忽略不计。 主要是从库执行事务需要时间过长。 解决方案, 一是避免大事务和大表的DDl,耗时较久。二是从库有可能承担一些运营报表的查询, 耗时较高,可以通过binlog抽到clickhouse或者odps等olap平台去做,三是从库机器配置可能会低于主库, 对等配置就好。
深度分页方案
如果id连续可以用范围查询、如果不连续可以用子查询先把id查出来, 再根据id进行分页, 减少回表,三是尽量建覆盖索引。
mysql主从同步
同步方式主要是通过binlog同步, binlog有三种格式,statement,row,mixed, statement记录sql语句,row记录行变更。 主从主要是冗余容灾备份和读写分离提高查询性能。一般有一主多从和双主多从,双主多从可以缩短恢复时间。
整体执行过程为: 主库执行事务成功写入主库binlog, 从库从主库拉取binlog写入从库relay log,从库从relay log回放binlog
Mysql 复制有三种复制模式,和kafka的生产流程很像, 分别是全同步,半同步和异步, 全同步时所有从库都需要写成功,半同步是其中一个从库写成功就可以, 异步是不考虑从。 可以通过业务场景配置
优化同步延迟, 可以通过show svales stats 查看延迟的时间, 一般传输过程占用延迟时间很少, 主要是从库重放慢, 一般原因可以看从库机器配置是不是低,大事务和大表的ddl,还有从库可能承担一些运营报表查询较慢。