learn.lianglianglee.com/文章/MySQL 的半同步是什么?.md.html
2022-05-11 19:04:14 +08:00

697 lines
43 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<!-- saved from url=(0046)https://kaiiiz.github.io/hexo-theme-book-demo/ -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no">
<link rel="icon" href="/static/favicon.png">
<title>MySQL 的半同步是什么?.md.html</title>
<!-- Spectre.css framework -->
<link rel="stylesheet" href="/static/index.css">
<!-- theme css & js -->
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
<div class="book-brand">
<a href="/">
<img src="/static/favicon.png">
<span>技术文章摘抄</span>
</a>
</div>
<div class="book-menu uncollapsible">
<ul class="uncollapsible">
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/文章/AQS 万字图文全面解析.md.html">AQS 万字图文全面解析.md.html</a>
</li>
<li>
<a href="/文章/Docker 镜像构建原理及源码分析.md.html">Docker 镜像构建原理及源码分析.md.html</a>
</li>
<li>
<a href="/文章/ElasticSearch 小白从入门到精通.md.html">ElasticSearch 小白从入门到精通.md.html</a>
</li>
<li>
<a href="/文章/JVM CPU Profiler技术原理及源码深度解析.md.html">JVM CPU Profiler技术原理及源码深度解析.md.html</a>
</li>
<li>
<a href="/文章/JVM 垃圾收集器.md.html">JVM 垃圾收集器.md.html</a>
</li>
<li>
<a href="/文章/JVM 面试的 30 个知识点.md.html">JVM 面试的 30 个知识点.md.html</a>
</li>
<li>
<a href="/文章/Java IO 体系、线程模型大总结.md.html">Java IO 体系、线程模型大总结.md.html</a>
</li>
<li>
<a href="/文章/Java NIO浅析.md.html">Java NIO浅析.md.html</a>
</li>
<li>
<a href="/文章/Java 面试题集锦(网络篇).md.html">Java 面试题集锦(网络篇).md.html</a>
</li>
<li>
<a href="/文章/Java-直接内存 DirectMemory 详解.md.html">Java-直接内存 DirectMemory 详解.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决.md.html</a>
</li>
<li>
<a href="/文章/Java中9种常见的CMS GC问题分析与解决.md.html">Java中9种常见的CMS GC问题分析与解决.md.html</a>
</li>
<li>
<a href="/文章/Java中的SPI.md.html">Java中的SPI.md.html</a>
</li>
<li>
<a href="/文章/Java中的ThreadLocal.md.html">Java中的ThreadLocal.md.html</a>
</li>
<li>
<a href="/文章/Java线程池实现原理及其在美团业务中的实践.md.html">Java线程池实现原理及其在美团业务中的实践.md.html</a>
</li>
<li>
<a href="/文章/Java魔法类Unsafe应用解析.md.html">Java魔法类Unsafe应用解析.md.html</a>
</li>
<li>
<a href="/文章/Kafka 源码阅读笔记.md.html">Kafka 源码阅读笔记.md.html</a>
</li>
<li>
<a href="/文章/Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html">Kafka、ActiveMQ、RabbitMQ、RocketMQ 区别以及高可用原理.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB Buffer Pool.md.html">MySQL · 引擎特性 · InnoDB Buffer Pool.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB IO子系统.md.html">MySQL · 引擎特性 · InnoDB IO子系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 事务系统.md.html">MySQL · 引擎特性 · InnoDB 事务系统.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 同步机制.md.html">MySQL · 引擎特性 · InnoDB 同步机制.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB 数据页解析.md.html">MySQL · 引擎特性 · InnoDB 数据页解析.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · InnoDB崩溃恢复.md.html">MySQL · 引擎特性 · InnoDB崩溃恢复.md.html</a>
</li>
<li>
<a href="/文章/MySQL · 引擎特性 · 临时表那些事儿.md.html">MySQL · 引擎特性 · 临时表那些事儿.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 半同步复制.md.html">MySQL 主从复制 半同步复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制 基于GTID复制.md.html">MySQL 主从复制 基于GTID复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 主从复制.md.html">MySQL 主从复制.md.html</a>
</li>
<li>
<a href="/文章/MySQL 事务日志(redo log和undo log).md.html">MySQL 事务日志(redo log和undo log).md.html</a>
</li>
<li>
<a href="/文章/MySQL 亿级别数据迁移实战代码分享.md.html">MySQL 亿级别数据迁移实战代码分享.md.html</a>
</li>
<li>
<a href="/文章/MySQL 从一条数据说起-InnoDB行存储数据结构.md.html">MySQL 从一条数据说起-InnoDB行存储数据结构.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:事务和锁的面纱.md.html">MySQL 地基基础:事务和锁的面纱.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据字典.md.html">MySQL 地基基础:数据字典.md.html</a>
</li>
<li>
<a href="/文章/MySQL 地基基础:数据库字符集.md.html">MySQL 地基基础:数据库字符集.md.html</a>
</li>
<li>
<a href="/文章/MySQL 性能优化:碎片整理.md.html">MySQL 性能优化:碎片整理.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html">MySQL 故障诊断:一个 ALTER TALBE 执行了很久,你慌不慌?.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:如何在日志中轻松定位大事务.md.html">MySQL 故障诊断:如何在日志中轻松定位大事务.md.html</a>
</li>
<li>
<a href="/文章/MySQL 故障诊断:教你快速定位加锁的 SQL.md.html">MySQL 故障诊断:教你快速定位加锁的 SQL.md.html</a>
</li>
<li>
<a href="/文章/MySQL 日志详解.md.html">MySQL 日志详解.md.html</a>
</li>
<li>
<a class="current-tab" href="/文章/MySQL 的半同步是什么?.md.html">MySQL 的半同步是什么?.md.html</a>
</li>
<li>
<a href="/文章/MySQL中的事务和MVCC.md.html">MySQL中的事务和MVCC.md.html</a>
</li>
<li>
<a href="/文章/MySQL事务_事务隔离级别详解.md.html">MySQL事务_事务隔离级别详解.md.html</a>
</li>
<li>
<a href="/文章/MySQL优化优化 select count().md.html">MySQL优化优化 select count().md.html</a>
</li>
<li>
<a href="/文章/MySQL共享锁、排他锁、悲观锁、乐观锁.md.html">MySQL共享锁、排他锁、悲观锁、乐观锁.md.html</a>
</li>
<li>
<a href="/文章/MySQL的MVCC多版本并发控制.md.html">MySQL的MVCC多版本并发控制.md.html</a>
</li>
<li>
<a href="/文章/QingStor 对象存储架构设计及最佳实践.md.html">QingStor 对象存储架构设计及最佳实践.md.html</a>
</li>
<li>
<a href="/文章/RocketMQ 面试题集锦.md.html">RocketMQ 面试题集锦.md.html</a>
</li>
<li>
<a href="/文章/SnowFlake 雪花算法生成分布式 ID.md.html">SnowFlake 雪花算法生成分布式 ID.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html">Spring Boot 2.x 结合 k8s 实现分布式微服务架构.md.html</a>
</li>
<li>
<a href="/文章/Spring Boot 教程:如何开发一个 starter.md.html">Spring Boot 教程:如何开发一个 starter.md.html</a>
</li>
<li>
<a href="/文章/Spring MVC 原理.md.html">Spring MVC 原理.md.html</a>
</li>
<li>
<a href="/文章/Spring MyBatis和Spring整合的奥秘.md.html">Spring MyBatis和Spring整合的奥秘.md.html</a>
</li>
<li>
<a href="/文章/Spring 帮助你更好的理解Spring循环依赖.md.html">Spring 帮助你更好的理解Spring循环依赖.md.html</a>
</li>
<li>
<a href="/文章/Spring 循环依赖及解决方式.md.html">Spring 循环依赖及解决方式.md.html</a>
</li>
<li>
<a href="/文章/Spring中眼花缭乱的BeanDefinition.md.html">Spring中眼花缭乱的BeanDefinition.md.html</a>
</li>
<li>
<a href="/文章/Vert.x 基础入门.md.html">Vert.x 基础入门.md.html</a>
</li>
<li>
<a href="/文章/eBay 的 Elasticsearch 性能调优实践.md.html">eBay 的 Elasticsearch 性能调优实践.md.html</a>
</li>
<li>
<a href="/文章/不可不说的Java“锁”事.md.html">不可不说的Java“锁”事.md.html</a>
</li>
<li>
<a href="/文章/互联网并发限流实战.md.html">互联网并发限流实战.md.html</a>
</li>
<li>
<a href="/文章/从ReentrantLock的实现看AQS的原理及应用.md.html">从ReentrantLock的实现看AQS的原理及应用.md.html</a>
</li>
<li>
<a href="/文章/从SpringCloud开始聊微服务架构.md.html">从SpringCloud开始聊微服务架构.md.html</a>
</li>
<li>
<a href="/文章/全面了解 JDK 线程池实现原理.md.html">全面了解 JDK 线程池实现原理.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性理论与算法.md.html">分布式一致性理论与算法.md.html</a>
</li>
<li>
<a href="/文章/分布式一致性算法 Raft.md.html">分布式一致性算法 Raft.md.html</a>
</li>
<li>
<a href="/文章/分布式唯一 ID 解析.md.html">分布式唯一 ID 解析.md.html</a>
</li>
<li>
<a href="/文章/分布式链路追踪:集群管理设计.md.html">分布式链路追踪:集群管理设计.md.html</a>
</li>
<li>
<a href="/文章/动态代理种类及原理,你知道多少?.md.html">动态代理种类及原理,你知道多少?.md.html</a>
</li>
<li>
<a href="/文章/响应式架构与 RxJava 在有赞零售的实践.md.html">响应式架构与 RxJava 在有赞零售的实践.md.html</a>
</li>
<li>
<a href="/文章/大数据算法——布隆过滤器.md.html">大数据算法——布隆过滤器.md.html</a>
</li>
<li>
<a href="/文章/如何优雅地记录操作日志?.md.html">如何优雅地记录操作日志?.md.html</a>
</li>
<li>
<a href="/文章/如何设计一个亿级消息量的 IM 系统.md.html">如何设计一个亿级消息量的 IM 系统.md.html</a>
</li>
<li>
<a href="/文章/异步网络模型.md.html">异步网络模型.md.html</a>
</li>
<li>
<a href="/文章/当我们在讨论CQRS时我们在讨论些神马.md.html">当我们在讨论CQRS时我们在讨论些神马.md.html</a>
</li>
<li>
<a href="/文章/彻底理解 MySQL 的索引机制.md.html">彻底理解 MySQL 的索引机制.md.html</a>
</li>
<li>
<a href="/文章/最全的 116 道 Redis 面试题解答.md.html">最全的 116 道 Redis 面试题解答.md.html</a>
</li>
<li>
<a href="/文章/有赞权限系统(SAM).md.html">有赞权限系统(SAM).md.html</a>
</li>
<li>
<a href="/文章/有赞零售中台建设方法的探索与实践.md.html">有赞零售中台建设方法的探索与实践.md.html</a>
</li>
<li>
<a href="/文章/服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html">服务注册与发现原理剖析Eureka、Zookeeper、Nacos.md.html</a>
</li>
<li>
<a href="/文章/深入浅出Cache.md.html">深入浅出Cache.md.html</a>
</li>
<li>
<a href="/文章/深入理解 MySQL 底层实现.md.html">深入理解 MySQL 底层实现.md.html</a>
</li>
<li>
<a href="/文章/漫画讲解 git rebase VS git merge.md.html">漫画讲解 git rebase VS git merge.md.html</a>
</li>
<li>
<a href="/文章/生成浏览器唯一稳定 ID 的探索.md.html">生成浏览器唯一稳定 ID 的探索.md.html</a>
</li>
<li>
<a href="/文章/缓存 如何保证缓存与数据库的双写一致性?.md.html">缓存 如何保证缓存与数据库的双写一致性?.md.html</a>
</li>
<li>
<a href="/文章/网易严选怎么做全链路监控的?.md.html">网易严选怎么做全链路监控的?.md.html</a>
</li>
<li>
<a href="/文章/美团万亿级 KV 存储架构与实践.md.html">美团万亿级 KV 存储架构与实践.md.html</a>
</li>
<li>
<a href="/文章/美团点评Kubernetes集群管理实践.md.html">美团点评Kubernetes集群管理实践.md.html</a>
</li>
<li>
<a href="/文章/美团百亿规模API网关服务Shepherd的设计与实现.md.html">美团百亿规模API网关服务Shepherd的设计与实现.md.html</a>
</li>
<li>
<a href="/文章/解读《阿里巴巴 Java 开发手册》背后的思考.md.html">解读《阿里巴巴 Java 开发手册》背后的思考.md.html</a>
</li>
<li>
<a href="/文章/认识 MySQL 和 Redis 的数据一致性问题.md.html">认识 MySQL 和 Redis 的数据一致性问题.md.html</a>
</li>
<li>
<a href="/文章/进阶Dockerfile 高阶使用指南及镜像优化.md.html">进阶Dockerfile 高阶使用指南及镜像优化.md.html</a>
</li>
<li>
<a href="/文章/铁总在用的高性能分布式缓存计算框架 Geode.md.html">铁总在用的高性能分布式缓存计算框架 Geode.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html</a>
</li>
<li>
<a href="/文章/阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html">阿里云PolarDB及其共享存储PolarFS技术实现分析.md.html</a>
</li>
<li>
<a href="/文章/面试最常被问的 Java 后端题.md.html">面试最常被问的 Java 后端题.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计在互联网业务开发中的实践.md.html">领域驱动设计在互联网业务开发中的实践.md.html</a>
</li>
<li>
<a href="/文章/领域驱动设计的菱形对称架构.md.html">领域驱动设计的菱形对称架构.md.html</a>
</li>
<li>
<a href="/文章/高效构建 Docker 镜像的最佳实践.md.html">高效构建 Docker 镜像的最佳实践.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
let sidebar = document.querySelector('.book-sidebar')
let content = document.querySelector('.off-canvas-content')
if (sidebar_toggle.classList.contains('extend')) { // show
sidebar_toggle.classList.remove('extend')
sidebar.classList.remove('hide')
content.classList.remove('extend')
} else { // hide
sidebar_toggle.classList.add('extend')
sidebar.classList.add('hide')
content.classList.add('extend')
}
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.add('show')
overlay.classList.add('show')
}
function hide_canvas() {
let sidebar = document.querySelector('.book-sidebar')
let overlay = document.querySelector('.off-canvas-overlay')
sidebar.classList.remove('show')
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
<div class="column col-12 col-lg-12">
<div class="book-navbar">
<!-- For Responsive Layout -->
<header class="navbar">
<section class="navbar-section">
<a onclick="open_sidebar()">
<i class="icon icon-menu"></i>
</a>
</section>
</header>
</div>
<div class="book-content" style="max-width: 960px; margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;">
<div class="book-post">
<p id="tip" align="center"></p>
<div><h1>MySQL 的半同步是什么?</h1>
<pre><code>前言
</code></pre>
<p>年后在进行腾讯二面的时候写完算法的后问的第一个问题就是MySQL的半同步是什么我当时直接懵了我以为是问的MySQL的两阶段提交的问题呢结果确认了一下后不是两阶段提交然后面试官看我连问的是啥都不知道就直接跳过这个问题直接聊下一个问题了。所以这次总结一下这部分的知识内容文字内容比较多可能会有些枯燥但对于这方面感兴趣的人来说还是比较有意思的。</p>
<h3>MySQL的主从复制</h3>
<p>我们的一般在大规模的项目上都是使用MySQL的复制功能来创建MySQL的主从集群的。主要是可以通过为服务器配置一个或多个备库的方式来进行数据同步。复制的功能不仅有利于构建<strong>高性能应用,同时也是高可用、可扩展性、灾难恢复、备份以及数据仓库等工作的基础</strong></p>
<p>说的通俗一点通过MySQL的主从复制来实现读写分离相比单点数据库又读又写来说提升了业务系统性能优化了用户体验。另外通过主从复制实现了数据库的高可用当主节点MySQL挂了的时候可以用从库来顶上。</p>
<h4>MySQL支持的复制方式</h4>
<p>MySQL支持三种复制方式</p>
<ul>
<li><strong>基于语句的复制也称为逻辑复制主要是指在主数据库上执行的SQL语句在从数据库上会重复执行一遍</strong>。MySQL默认采用的就是这种复制效率比较高。但是也是有一定的问题的如果SQL中使用uuid()、rand()等函数,那么复制到从库的数据就会有偏差。</li>
<li><strong>基于行的复制,指将更新处理后的数据复制到从数据库,而不是执行一边语句</strong>。从MySQL5.1的版本才被支持。</li>
<li><strong>混合复制默认采用语句复制当发现语句不能进行精准复制数据时例如语句中含有uuid()、rand()等函数),采用基于行的复制</strong></li>
</ul>
<h4>主从复制原理</h4>
<p>MySQL的复制原理概述上来讲大体可以分为这三步</p>
<ol>
<li>在主库上把数据更改记录到二进制日志Binary Log中。</li>
<li>从库将主库上的日志复制到自己的中继日志Relay Log中。</li>
<li>备库读取中继日志中的事件,将其重放到备库数据之上。</li>
</ol>
<p>主要过程如下图:
<img src="assets/20210321233214268.png" alt="MySQL复制过程" />
下面来详细说一下复制的这三步:</p>
<p><strong>第一步是在主库上记录二进制日志首先主库要开启binlog日志记录功能并授权Slave从库可以访问的权限</strong>。这里需要注意的一点就是binlog的日志里的顺序是按照事务提交的顺序来记录的而非每条语句的执行顺序。</p>
<p><strong>第二步从库将binLog复制到其本地的RelayLog中。首先从库会启动一个工作线程称为I/O线程I/O线程跟主库建立一个普通的客户端连接然后主库上启动一个特殊的二进制转储binlog dump线程此转储线程会读取binlog中的事件</strong>。当追赶上主库后,会进行休眠,直到主库通知有新的更新语句时才继续被唤醒。
这样通过从库上的I/O线程和主库上的binlog dump线程就将binlog数据传输到从库上的relaylog中了。</p>
<p><strong>第三步从库中启动一个SQL线程从relaylog中读取事件并在备库中执行从而实现备库数据的更新。</strong></p>
<p>这种复制架构实现了获取事件和重放事件的解耦运行I/O线程能够独立于SQL线程之外工作。但是这种架构也限制复制的过程最重要的一点是在主库上并发运行的查询在备库中只能串行化执行因为只有一个SQL线程来重放中继日志中的事件。</p>
<blockquote>
<p>说到这个主从复制的串行化执行的问题我就想到了一个之前在工作中遇到的一个问题就是有这么一个业务场景我们有一个操作是初始化一批数据数据是从一个外部系统的接口中获取的然后我是通过线程池里的多个线程并行从外部系统的接口中获取数据每个线程获取到数据后直接插入到数据库中。然后在数据全部入库完成后然后去执行批量查询将刚插入到数据库中的数据查询出来放到ElasticSearch中。结果每次放入到ES中的数据总是不完整后来研究了半天都不行最终是让查询也走的主库才解决的问题。当时不知道是MySQL主从复制的串行化从而导致的这个问题。</p>
</blockquote>
<h4>MySQL主从复制模式</h4>
<p>MySQL的主从复制其实是支持<strong>异步复制</strong><strong>半同步复制</strong><strong>GTID复制</strong>等多种复制模式的。</p>
<h5>异步模式</h5>
<p>MySQL的默认复制模式就是异步模式主要是<strong>指MySQL的主服务器上的I/O线程将数据写到binlong中就直接返回给客户端数据更新成功不考虑数据是否传输到从服务器以及是否写入到relaylog中</strong>。在这种模式下复制数据其实是有风险的一旦数据只写到了主库的binlog中还没来得急同步到从库时就会造成数据的丢失。</p>
<p>但是这种模式确也是效率最高的,因为变更数据的功能都只是在主库中完成就可以了,从库复制数据不会影响到主库的写数据操作。
<img src="assets/20210322232750209.png" alt="异步复制" />
上面我也说了,这种异步复制模式虽然效率高,但是数据丢失的风险很大,所以就有了后面要介绍的半同步复制模式。</p>
<h5>半同步模式</h5>
<p>MySQL从<strong>5.5</strong>版本开始通过以插件的形式开始支持半同步的主从复制模式。什么是半同步主从复制模式呢?
这里通过对比的方式来说明一下:</p>
<ul>
<li><strong>异步复制模式</strong>上面我们已经介绍了异步复制模式主库在执行完客户端提交的事务后只要将执行逻辑写入到binlog后就立即返回给客户端并不关心从库是否执行成功这样就会有一个隐患就是在主库执行的binlog还没同步到从库时主库挂了这个时候从库就就会被强行提升为主库这个时候就有可能造成数据丢失。</li>
<li><strong>同步复制模式</strong>:当主库执行完客户端提交的事务后,需要等到所有从库也都执行完这一事务后,才返回给客户端执行成功。因为要等到所有从库都执行完,执行过程中会被阻塞,等待返回结果,所以性能上会有很严重的影响。</li>
<li><strong>半同步复制模式</strong>半同步复制模式可以说是介于异步和同步之间的一种复制模式主库在执行完客户端提交的事务后要等待至少一个从库接收到binlog并将数据写入到relay log中才返回给客户端成功结果。半同步复制模式比异步模式提高了数据的可用性但是也产生了一定的性能延迟最少要一个TCP/IP连接的往返时间。</li>
</ul>
<p>半同步复制模式,可以很明确的知道,在一个事务提交成功之后,此事务至少会存在于两个地方一个是主库一个是从库中的某一个。<strong>主要原理是在master的dump线程去通知从库时增加了一个ACK机制也就是会确认从库是否收到事务的标志码master的dump线程不但要发送binlog到从库还有负责接收slave的ACK。当出现异常时Slave没有ACK事务那么将自动降级为异步复制直到异常修复后再自动变为半同步复制</strong></p>
<p>MySQL半同步复制的流程如下</p>
<p><img src="assets/20210405204127556.png" alt="MySQL 半同步复制模式" /></p>
<h6>半同步复制的隐患</h6>
<p>半同步复制模式也存在一定的数据风险当事务在主库提交完后等待从库ACK的过程中如果Master宕机了这个时候就会有两种情况的问题。</p>
<ul>
<li><strong>事务还没发送到Slave上</strong>若事务还没发送Slave上客户端在收到失败结果后会重新提交事务因为重新提交的事务是在新的Master上执行的所以会执行成功后面若是之前的Master恢复后会以Slave的身份加入到集群中这个时候之前的事务就会被执行两次第一次是之前此台机器作为Master的时候执行的第二次是做为Slave后从主库中同步过来的。</li>
<li><strong>事务已经同步到Slave上</strong>因为事务已经同步到Slave了所以当客户端收到失败结果后再次提交事务你那么此事务就会再当前Slave机器上执行两次。</li>
</ul>
<p>为了解决上面的隐患MySQL从5.7版本开始,增加了一种新的半同步方式。新的半同步方式的执行过程是将“<strong>Storage Commit</strong>”这一步移动到了“<strong>Write Slave dump</strong>”后面。这样保证了<strong>只有Slave的事务ACK后才提交主库事务</strong>。MySQL 5.7.2版本新增了一个参数来进行配置:<code>rpl_semi_sync_master_wait_point</code>,此参数有两个值可配置:</p>
<ul>
<li><strong>AFTER_SYNC</strong>参数值为AFTER_SYNC时代表采用的是新的半同步复制方式。</li>
<li><strong>AFTER_COMMIT</strong>:代表采用的是之前的旧方式的半同步复制模式。</li>
</ul>
<p><img src="assets/20210405211616622.png" alt="新的半同步复制" />
MySQL从5.7.2版本开始,默认的半同步复制方式就是<code>AFTER_SYNC</code>方式了,但是方案不是万能的,因为<code>AFTER_SYNC</code>方式是在事务同步到Slave后才提交主库的事务的若是当主库等待Slave同步成功的过程中Master挂了这个Master事务提交就失败了客户端也收到了事务执行失败的结果了但是Slave上已经将binLog的内容写到Relay Log里了这个时候Slave数据就会多了但是多了数据一般问题不算严重多了总比少了好。MySQL在没办法解决分布式数据一致性问题的情况下它能保证的是不丢数据多了数据总比丢数据要好。</p>
<p>这里说几个的半同步复制模式的参数:</p>
<pre><code class="language-sql">mysql&gt; show variables like '%Rpl%';
+-------------------------------------------+------------+
| Variable_name | Value |
+-------------------------------------------+------------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_for_slave_count | 1 |
| rpl_semi_sync_master_wait_no_slave | ON |
| rpl_semi_sync_master_wait_point | AFTER_SYNC |
| rpl_stop_slave_timeout | 31536000 |
+-------------------------------------------+------------+
-- 半同步复制模式开关
rpl_semi_sync_master_enabled
-- 半同步复制,超时时间,单位毫秒,当超过此时间后,自动切换为异步复制模式
rpl_semi_sync_master_timeout
-- MySQL 5.7.3引入的该变量设置主需要等待多少个slave应答才能返回给客户端默认为1。
rpl_semi_sync_master_wait_for_slave_count
-- 此值代表当前集群中的slave数量是否还能够满足当前配置的半同步复制模式默认为ON当不满足半同步复制模式后全部Slave切换到异步复制此值也会变为OFF
rpl_semi_sync_master_wait_no_slave
-- 代表半同步复制提交事务的方式5.7.2之后默认为AFTER_SYNC
rpl_semi_sync_master_wait_point
</code></pre>
<h5>GTID模式</h5>
<p>MySQL从5.6版本开始推出了GTID复制模式GTID即全局事务ID (global transaction identifier)的简称GTID是由UUID+TransactionId组成的UUID是单个MySQL实例的唯一标识在第一次启动MySQL实例时会自动生成一个server_uuid, 并且默认写入到数据目录下的auto.cnf(mysql/data/auto.cnf)文件里。TransactionId是该MySQL上执行事务的数量随着事务数量增加而递增。这样保证了<strong>GTID在一组复制中全局唯一</strong></p>
<p>这样通过GTID可以清晰的看到当前事务是从哪个实例上提交的提交的第多少个事务。</p>
<p>来看一个GTID的具体形式</p>
<pre><code class="language-sql">mysql&gt; show master status;
+-----------+----------+--------------+------------------+-------------------------------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------+----------+--------------+------------------+-------------------------------------------+
| on.000003 | 187 | | | 76147e28-8086-4f8c-9f98-1cf33d92978d:1-322|
+-----------+----------+--------------+------------------+-------------------------------------------+
1 row in set (0.00 sec)
</code></pre>
<p>GTID76147e28-8086-4f8c-9f98-1cf33d92978d:1-322
UUID76147e28-8086-4f8c-9f98-1cf33d92978d
TransactionId:1-322</p>
<h4>GTID的工作原理</h4>
<p>由于GTID在一组主从复制集群中的唯一性从而保证了每个GTID的事务只在一个MySQL上执行一次。
那么是怎么实现这种机制的呢GTID的原理又是什么样的呢</p>
<p>当从服务器连接主服务器时把自己执行过的GTID<strong>Executed_Gtid_Set: 即已经执行的事务编码</strong>以及获取到GTID<strong>Retrieved_Gtid_Set: 即从库已经接收到主库的事务编号</strong>都传给主服务器。主服务器会从服务器缺少的GTID以及对应的transactionID都发送给从服务器让从服务器补全数据。当主服务器宕机时会找出同步数据最成功的那台conf服务器直接将它提升为主服务器。若是强制要求某一台不是同步最成功的一台从服务器为主会先通过change命令到最成功的那台服务器将GTID进行补全然后再把强制要求的那台机器提升为主。</p>
<p>主要数据同步机制可以分为这几步:</p>
<ul>
<li>master更新数据时在事务前生产GTID一同记录到binlog中。</li>
<li>slave端的i/o线程将变更的binlog写入到relay log中。</li>
<li>sql线程从relay log中获取GTID然后对比Slave端的binlog是否有记录。</li>
<li>如果有记录说明该GTID的事务已经执行slave会忽略该GTID。</li>
<li>如果没有记录Slave会从relay log中执行该GTID事务并记录到binlog。</li>
<li>在解析过程中,判断是否有主键,如果没有主键就使用二级索引,再没有二级索引就扫描全表。</li>
</ul>
<p>初始结构如下图
<img src="assets/20210418151707365.png" alt="GTID" />
当Master出现宕机后就会演变成下图。
<img src="assets/2021041815183969.png" alt="GTID复制" />
通过上图我们可以看出来当Master挂掉后Slave-1执行完了Master的事务Slave-2延时一些所以没有执行完Master的事务这个时候提升Slave-1为主Slave-2连接了新主Slave-1将最新的GTID传给新主然后Slave-1就从这个GTID的下一个GTID开始发送事务给Slave-2。<strong>这种自我寻找复制位置的模式减少事务丢失的可能性以及故障恢复的时间。</strong></p>
<h4>GTID的优劣势</h4>
<p>通过上面的分析我们可以得出GTID的优势是</p>
<ul>
<li>每一个事务对应一个执行ID一个GTID在一个服务器上只会执行一次;</li>
<li>GTID是用来代替传统复制的方法GTID复制与普通复制模式的最大不同就是不需要指定二进制文件名和位置;</li>
<li>减少手工干预和降低服务故障时间,当主机挂了之后通过软件从众多的备机中提升一台备机为主机;</li>
</ul>
<p>GTID的缺点也很明显</p>
<ul>
<li>首先不支持非事务的存储引擎;</li>
<li>不支持create table ... select 语句复制(主库直接报错);(原理: 会生成两个sql, 一个是DDL创建表SQL, 一个是insert into 插入数据的sql; 由于DDL会导致自动提交, 所以这个sql至少需要两个GTID, 但是GTID模式下, 只能给这个sql生成一个GTID)</li>
<li>不允许一个SQL同时更新一个事务引擎表和非事务引擎表;</li>
<li>在一个MySQL复制群组中要求全部开启GTID或关闭GTID。</li>
<li>开启GTID需要重启 (mysql5.7除外);</li>
<li>开启GTID后就不再使用原来的传统复制方式不像半同步复制半同步复制失败后可以降级到异步复制;</li>
<li>对于create temporary table 和 drop temporary table语句不支持;</li>
<li>不支持sql_slave_skip_counter;</li>
</ul>
<p>其实GTID的这部分内容挺多的如果有想深入研究的可以去看看<a href="https://www.cnblogs.com/kevingrace/p/5569753.html">这篇文章</a>
最后说几个开启GTID的必备条件</p>
<ul>
<li>MySQL 5.6 版本在my.cnf文件中添加:</li>
</ul>
<pre><code class="language-sql">gtid_mode=on (必选) #开启gtid功能
log_bin=log-bin=mysql-bin (必选) #开启binlog二进制日志功能
log-slave-updates=1 (必选) #也可以将1写为on
enforce-gtid-consistency=1 (必选) #也可以将1写为on
</code></pre>
<ul>
<li>MySQL 5.7或更高版本在my.cnf文件中添加:</li>
</ul>
<pre><code class="language-sql">gtid_mode=on (必选)
enforce-gtid-consistency=1 (必选)
log_bin=mysql-bin (可选) #高可用切换,最好开启该功能
log-slave-updates=1 (可选) #高可用切换,最好打开该功能
</code></pre>
<blockquote>
<p>参考:
《高性能MySQL》
<a href="https://www.cnblogs.com/kevingrace/p/5569753.html">MySQL 基于GTID复制模式</a></p>
</blockquote>
</div>
</div>
<div>
<div style="float: left">
<a href="/文章/MySQL 日志详解.md.html">上一页</a>
</div>
<div style="float: right">
<a href="/文章/MySQL中的事务和MVCC.md.html">下一页</a>
</div>
</div>
</div>
</div>
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
<script defer src="https://static.cloudflareinsights.com/beacon.min.js/v652eace1692a40cfa3763df669d7439c1639079717194" integrity="sha512-Gi7xpJR8tSkrpF7aordPZQlW2DLtzUlZcumS8dMQjwDHEnw9I7ZLyiOj/6tZStRBGtGgN6ceN6cMH8z7etPGlw==" data-cf-beacon='{"rayId":"709980047ec08b66","version":"2021.12.0","r":1,"token":"1f5d475227ce4f0089a7cff1ab17c0f5","si":100}' crossorigin="anonymous"></script>
</body>
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NPSEEVD756"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
var path = window.location.pathname
var cookie = getCookie("lastPath");
console.log(path)
if (path.replace("/", "") === "") {
if (cookie.replace("/", "") !== "") {
console.log(cookie)
document.getElementById("tip").innerHTML = "<a href='" + cookie + "'>跳转到上次进度</a>"
}
} else {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
d.setTime(d.getTime() + (180 * 24 * 60 * 60 * 1000));
var expires = "expires=" + d.toGMTString();
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i].trim();
if (c.indexOf(name) === 0) return c.substring(name.length, c.length);
}
return "";
}
</script>
</html>