服务器部署与 MySQL 运维学习笔记:从 Ansible 到高可用与性能优化

**摘要:**本文整理了一套面向生产环境的部署与运维学习笔记,涵盖项目整体技术栈、基于 Ansible 的多服务器统一管理、Nginx 配置优化与七种负载均衡算法、Keepalived 基于 VRRP 的高可用方案,以及 MySQL 的体系结构、存储引擎、逻辑与物理备份、主从复制和性能分析思路。文章重点说明各组件在解决单点故障、性能瓶颈和数据可靠性时的实际作用,并给出可直接参考的配置示例与面试总结。

1. 项目框架

前端:Vue 3,Node.js 打包,Nginx。

后端:Spring、Spring Boot、MyBatis-Plus。

中间件:MySQL、Redis、Milvus、ETCD、MinIO。

监控:Prometheus、Grafana。

AI Agent:Dify。

仓库:Harbor。

2. 多服务器管理

在本次项目中,为了让系统运行更加稳定,我采用了高可用方案来解决单点问题。

什么是高可用?通俗来说就是「人多力量大」,用来解决性能瓶颈、减轻负载压力。稍微学术一点的说法是:性能高、稳定性好,能够实现热备,当一个服务器崩溃时,能自动跳转到其他服务器,保障业务正常运行。

多服务器同时会带来两个比较难解决的问题,分别是统一管理和服务器之间的角色调配。Ansible 就是用来解决第一个问题的。

3. Ansible

什么是 Ansible?它是一个通过 SSH 进行服务器连接的简单工具。最大优势是简单,缺点是因为使用 SSH 进行连接,导致数据包加密,从而带来一定的性能损失。又因为使用 SSH 进行连接,为了避免来回输入密码,需要配置 SSH 免密登录。

Ansible 通过使用模块来统一管理服务器,模块数量大约有 8300 个左右,其中的 shell 模块被戏称为万能模块。

4. Ansible 怎么用

4.1 Inventory 主机清单

主机清单用来存储控制对象的 IP,一般格式定义为:

conf 复制代码
[nginx.service]
192.168.88.102
[host.server]
192.168.88.100
[all:children]
nginx.service
host.server

用方括号【】来定义下方 IP 群的名称,方括号下面紧跟着服务器 IP。第三个 children 代表「孩子」,意思是这个服务的 IP 是下面跟着的服务 IP 的并集。

4.2 Module 模块

bash 复制代码
# 常见的模块
# ansible-doc <module_name>
shell 万能模块
hostname 设置主机名
# 注意 file 里存在参数 path 路径,当 state 为 link 的时候,链接源地址也是必填的
# 一般也会填写 owner、group、mode
file 创建文件
service 服务
# 注意 当模块后面要加参数时,参数加在 ansible 的 a 参数后
# a 是 args 的意思
注意:shell 模块和其他模块有一个最大的区别就是幂等性
幂等性:当这个东西已经执行过,没有发生变化时,再执行一遍得到相同的结果,就不再执行

4.3 Playbook 剧本

Playbook 解决了 Ansible 每一次都需要手动输入命令、无法保证执行顺序、无法做条件判断和错误处理的问题。

Playbook 有点像 Docker Compose,一次编写、多次执行,采用 YAML 语法,示例如下:

yaml 复制代码
---
- name: 这个 YAML 是用来解决什么的
  hosts: 控制对象(目标主机)
  remote_user: root
  tasks:
    - name: 执行这个模块的作用是什么,做一个简单的说明
      模块名:
      ......
# 当需要打一个标签的话用 tags
当只有上面文件发生了改变,这个触发器才会执行
notify: # 这个下面跟着一些触发器的名字,和模块名对齐
a
y
触发器 handlers
handlers:
name: a
模块名
name: y
模块名
注意:handlers 和 tasks(任务清单) 对齐

执行命令:

shell 复制代码
ansible-playbook 这个 playbook 的绝对路径或者相对路径

5. Nginx

Nginx 的作用:

  • 作为 Web 应用:用来存放前端静态资源。
  • 反向代理:客户端不需要知道自己访问的是哪一个服务器 IP,只需要访问 Nginx,就能通过 Nginx 访问后端服务器。
  • 负载均衡:通过负载均衡策略把请求分发给服务器。

6. Nginx 优化

三种方法:动静分离、开启缓存、开启 Gzip 压缩。

  • 动静分离:将静态资源与动态请求分离到不同的服务或目录,通过 Nginx 精准路由,避免动态请求阻塞静态资源的高效传输,同时降低后端压力。
  • 开启缓存:缓存存储在内存中,访问速度快。通过开启缓存就能大大加速用户访问速度。
  • 开启 Gzip 压缩:是一种用 CPU 换带宽的方法,通过用 CPU 压缩 Web 资源,减少带宽压力,传输后由客户端解压。

7. Nginx 配置

Nginx 主配置文件的各个块说明:

  • main:全局配置,例如运行用户、Worker 进程数、PID、错误日志。
  • events:连接处理模型,例如 worker_connections、epoll、multi_accept。
  • http:所有 HTTP、HTTPS 服务的公共配置,例如日志、压缩、TLS 默认值、include。
  • server:一个虚拟主机,定义监听端口、域名、证书、根目录。
  • location:按 URI 路由请求,负责静态文件、反向代理、重写和访问控制。
  • upstream:定义后端服务器池,实现负载均衡和故障切换。
  • stream:四层代理,处理 TCP、UDP,例如 MySQL、Redis、DNS。
  • map、geo、types:辅助变量、地理规则和 MIME 映射。

主配置文件是 nginx.conf,通过这个主配置文件可以导入其他路径下的配置文件,例如 conf.d。

8. 负载均衡算法

Nginx 负载均衡常见的 7 种算法如下。

8.1 轮询,Round Robin

作用:请求按顺序依次分发到后端节点,是默认算法。

配置:

nginx 复制代码
upstream backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

8.2 加权轮询,Weighted Round Robin

作用:按权重分配流量,权重越高收到的请求越多。

适用:服务器配置不一致,或需要灰度发布。

配置:

nginx 复制代码
upstream backend {
    server 10.0.0.11:8080 weight=5;
    server 10.0.0.12:8080 weight=2;
    server 10.0.0.13:8080 weight=1;
}

8.3 最少连接,Least Connections

作用:优先把请求发给当前活跃连接数最少的节点。

适用:请求处理时间差异较大的场景,例如接口、WebSocket。

配置:

nginx 复制代码
upstream backend {
    least_conn;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

说明:least_conn 可以结合 weight 使用,权重会参与最少连接的计算,可理解为加权最少连接。

8.4 IP 哈希,IP Hash

作用:根据客户端 IP 计算哈希,同一客户端通常固定访问同一后端。

适用:需要会话保持,但应用暂不支持共享 Session。

配置:

nginx 复制代码
upstream backend {
    ip_hash;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

注意:Nginx 主要基于客户端 IP 的前三段进行哈希。大量用户经过同一 NAT 出口时,容易造成负载倾斜;后端节点增减时,部分用户可能被重新映射。

8.5 哈希,Generic Hash

作用:根据指定变量计算哈希,例如 URL、请求头、参数或客户端 IP。

适用:缓存命中、租户绑定、按 URL 分流。

配置:

nginx 复制代码
upstream backend {
    hash $request_uri consistent;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

说明:consistent 表示一致性哈希。节点增减时,它比普通取模哈希更少改变原有映射关系。常见用法包括:

nginx 复制代码
hash $request_uri consistent;
hash $arg_user_id consistent;
hash $http_x_tenant_id consistent;

8.6 随机,Random

作用:从后端节点中随机选择服务器,也可以随机选两个再按最少连接决策。

适用:大规模节点池、普通无状态服务。

配置:

nginx 复制代码
upstream backend {
    random;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

随机选两个,再选择连接数较少的节点:

nginx 复制代码
upstream backend {
    random two least_conn;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

8.7 最少响应时间,Least Time

作用:优先选择平均响应时间更短、连接数更少的后端节点。

语法:

nginx 复制代码
least_time header;
least_time last_byte;

配置:

nginx 复制代码
upstream backend {
    least_time header;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

关键限制:least_time 属于 NGINX Plus 能力,开源版 Nginx 通常不支持,不要直接把该配置用于开源版。

8.8 选择建议

  • 普通无状态服务:轮询或加权轮询。
  • 后端机器配置不同:加权轮询。
  • 请求耗时差异大:least_conn。
  • 需要 IP 会话保持:ip_hash。
  • 需要 URL 或租户稳定映射:hash consistent。
  • 大规模节点池:random two least_conn。
  • NGINX Plus 且重视响应时间:least_time。

容易混淆的算法:

  • fair:按响应时间分发,通常依赖第三方模块,不是原生算法。
  • url_hash:Nginx 现代版本通常直接使用 hash $request_uri consistent,不建议依赖旧式 url_hash 模块。
  • sticky:更精确的会话保持方案,通常依赖 NGINX Plus 或第三方模块,不只依赖 IP。

重要注意事项:

  1. 同一个 upstream 通常只配置一种负载均衡算法,不要同时配置 ip_hash、least_conn、hash、random。
  2. ip_hash、hash、least_time、random 对版本和产品类型有不同限制,部署前执行 nginx -V 确认。
  3. 修改配置后必须先检查,再平滑重载:
shell 复制代码
nginx -t
systemctl reload nginx
  1. 负载均衡算法不能替代健康检查。开源版健康检查能力有限,生产环境通常还需要 Keepalived、HAProxy、云负载均衡或 NGINX Plus 配合。

9. Nginx 安全扩展

9.1 Nginx 禁止目录列表

nginx 复制代码
server {
    location / {
        autoindex off;  # 禁止目录列表
    }
}

防止目录列表,本质上是防止信息泄露。目录列表本身不一定直接导致入侵,但它会把服务器上的文件和目录结构主动告诉攻击者,显著降低攻击成本,并扩大后续攻击面。

正常访问目录时,Nginx 会先寻找 index.html、index.php 等首页文件。如果找不到,并且开启了 autoindex on,Nginx 就会生成一个类似文件管理器的 HTML 页面,显示目录下的文件名、大小、修改时间,访问者可以逐层浏览。

9.2 IP 访问控制

nginx 复制代码
location /admin/ {
    deny 192.168.1.1;          # 禁止特定 IP
    allow 192.168.88.0/24;     # 允许内网段
    deny all;                  # 禁止其他所有
    proxy_pass http://backend:9000/admin/;
}

设置 IP 访问控制,主要是为了把请求限制在可信来源范围内,减少暴露面和攻击入口。它适合保护后台、监控、运维接口和内部服务,但不能替代用户认证和权限控制。

10. Keepalived

10.1 先纠正一个关键概念

Keepalived 和前面讲的 Nginx Keepalive 不是同一个东西。

  • Nginx Keepalive:复用 HTTP、TCP 连接,主要解决性能和连接开销问题。
  • Keepalived:基于 VRRP 实现 VIP 漂移、故障检测和自动切换,主要解决高可用问题。

Keepalived 通常不负责负载均衡。负载均衡可以由 Nginx、LVS、HAProxy 完成,Keepalived 负责让多个负载均衡节点之间实现主备切换。

10.2 核心内容

Keepalived 是一个基于 VRRP 的开源软件:

  • VRRP:Virtual Router Redundancy Protocol,虚拟路由冗余协议。
  • 最初用于为 LVS 提供高可用,后来扩展为通用 VIP 漂移和高可用方案。
  • 常见于 Web、数据库、Nginx、LVS 等场景。
  • 核心目标是避免单点故障,让服务对外访问的 VIP 始终可用。

主备、主主、主从需要正确理解:

  • 主备:一个节点持有 VIP,另一个节点待命。
  • 双主:通常配置多个 VRRP 实例和多个 VIP,两台服务器互为主备,不是两个节点同时持有同一个 VIP。
  • 主从:Keepalived 可以切换 VIP,但不等于会同步数据库或业务数据。MySQL 主从还需要数据库复制机制。

10.3 心跳机制

Keepalived 的心跳通常指 VRRP Advertisement 报文。工作过程:

  1. MASTER 节点定期发送 VRRP 心跳。
  2. BACKUP 节点持续监听心跳。
  3. 如果 BACKUP 在超时时间内没有收到心跳,就认为 MASTER 故障。
  4. BACKUP 提升为 MASTER,并将 VIP 添加到自己的网卡。
  5. 节点发送免费 ARP,通知交换机和其他主机更新 ARP 表。
  6. 流量随后切换到新的 MASTER。

常见配置:

nginx 复制代码
advert_int 1;

表示 MASTER 大约每 1 秒发送一次 VRRP 报文。

故障判定时间通常约为:

text 复制代码
3 × advert_int + 优先级偏差时间

因此 advert_int 1 时,通常大约 3 秒左右检测到故障,不是瞬时切换。心跳间隔越短,发现越快,但网络和 CPU 开销越大。

Keepalived 心跳主要检测节点和网络是否存活,不一定检测 Nginx 进程是否正常。如果 Nginx 已经崩溃,但 Keepalived 和网络仍然正常,VIP 通常不会自动切换,除非配置了服务健康检查。

10.4 优先级机制

VRRP 使用优先级决定谁成为 MASTER:

nginx 复制代码
priority 100;

规则:

  • 优先级数值越大,越容易成为 MASTER。
  • 主节点通常配置 priority 100,备节点通常配置 priority 90。
  • 优先级相同时,通常 IP 更大的节点胜出。
  • 节点的初始 state 不是最终状态,真正决定状态的是优先级、抢占策略和当前通信情况。
  • 优先级下降后,是否立即切换还取决于抢占策略。

Keepalived 真正实用的部分,是把优先级和健康检查联动。

nginx 复制代码
vrrp_script chk_nginx {
    script "/usr/bin/curl -fsS --max-time 1 http://127.0.0.1/health >/dev/null || exit 1"
    interval 2
    timeout 1
    fall 3
    rise 2
    weight -40
}

参数含义:

  • script:健康检查命令,返回 0 表示成功,非 0 表示失败。
  • interval 2:每 2 秒检查一次。
  • timeout 1:检查命令最长执行 1 秒。
  • fall 3:连续失败 3 次才认为异常,避免偶发失败导致切换。
  • rise 2:连续成功 2 次才认为恢复,避免状态反复抖动。
  • weight -40:检查失败时,当前节点优先级减少 40。

例如:主节点原始优先级 100,健康检查失败后变为 100 - 40 = 60,备节点优先级 90。在默认抢占策略下,备节点可以接管 VIP。

10.5 自动切换机制

自动切换并不是 Keepalived 自己猜测服务是否正常,而是由下面几个条件共同触发:

  1. VRRP 心跳中断。
  2. 网卡或网络链路故障。
  3. 健康检查脚本失败。
  4. 节点优先级下降到低于其他节点。
  5. 节点主动释放 VIP,或者 Keepalived 进程退出。

切换过程大致如下:

text 复制代码
MASTER 心跳消失
        ↓
BACKUP 等待故障判定时间
        ↓
BACKUP 成为 MASTER
        ↓
添加 VIP 到网卡
        ↓
发送免费 ARP
        ↓
交换机、网关和客户端更新 ARP
        ↓
流量切换到新 MASTER

常用配置示例。

主节点:

nginx 复制代码
global_defs {
    router_id nginx01
}
vrrp_script chk_nginx {
    script "/usr/bin/curl -fsS --max-time 1 http://127.0.0.1/health >/dev/null || exit 1"
    interval 2
    timeout 1
    fall 3
    rise 2
    weight -40
}
vrrp_instance VI_1 {
    state MASTER
    interface eth0
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 12345678
    }
    virtual_ipaddress {
        192.168.1.100/24 dev eth0
    }
    track_script {
        chk_nginx
    }
}

备节点:

nginx 复制代码
global_defs {
    router_id nginx02
}
vrrp_script chk_nginx {
    script "/usr/bin/curl -fsS --max-time 1 http://127.0.0.1/health >/dev/null || exit 1"
    interval 2
    timeout 1
    fall 3
    rise 2
    weight -40
}
vrrp_instance VI_1 {
    state BACKUP
    interface eth0
    virtual_router_id 51
    priority 90
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 12345678
    }
    virtual_ipaddress {
        192.168.1.100/24 dev eth0
    }
    track_script {
        chk_nginx
    }
}

关键配置必须一致:

  • virtual_router_id 必须相同。
  • virtual_ipaddress 必须相同。
  • advert_int 建议相同。
  • interface 必须指向正确的通信网卡。
  • 同一二层网络中,不同 VRRP 实例不能使用重复的 VRID。

10.6 抢占机制

默认情况下,原 MASTER 恢复后,如果优先级更高,可能重新抢回 VIP。

如果希望故障恢复后不切回,可以配置:

nginx 复制代码
nopreempt

但 nopreempt 会影响故障恢复后的流量流向,需要结合业务设计。

常见策略:

  • 默认抢占:主节点恢复后重新接管 VIP。
  • 禁止抢占:备用节点接管后不再切回,减少切换次数。
  • 延迟抢占:等待业务完全启动后再接管,避免服务恢复不完整。

10.7 主要优点

  • 实现 VIP 自动漂移,减少单点故障。
  • 心跳和切换机制成熟,配置相对简单。
  • 支持主备、多实例和复杂故障检测。
  • 可以通过脚本检测 Nginx、MySQL、Redis 等业务服务。
  • 支持网卡、接口、路由和自定义健康检查。
  • 不依赖 DNS 切换,客户端仍然访问同一个 VIP。

10.8 主要缺点

  1. 只能解决节点级高可用,不负责数据同步。例如 MySQL 主从切换后,还要处理复制关系、事务一致性和数据回放。
  2. 健康检查配置不当会导致 VIP 不切换。如果只依赖 VRRP 心跳,服务进程崩溃但节点仍在线时,Keepalived 不会自动发现。
  3. 可能出现脑裂。心跳网络中断后,两个节点可能都认为对方故障,同时持有同一个 VIP,导致 ARP 冲突和流量异常。
  4. 云环境支持有限。很多公有云默认不允许 VRRP 组播、伪造 IP 或免费 ARP。云环境通常更适合使用云负载均衡、浮动 IP 或云厂商提供的 HA 方案。
  5. VIP 切换需要 ARP 收敛。即使 VIP 已经漂移,交换机、网关和客户端仍可能需要一段时间更新 ARP,切换期间会短暂丢包。
  6. 配置错误可能中断整个业务。例如 VRID 冲突、网卡选错、健康检查误判、防火墙拦截 VRRP,都会导致异常切换。

10.9 防止脑裂和误切换

  • 使用独立的心跳链路,避免业务网络拥塞影响 VRRP。
  • 可以使用 VRRP Unicast,减少组播依赖。
  • 只允许可信节点之间交换 VRRP 报文。
  • 健康检查增加 fall 和 rise,避免网络抖动触发切换。
  • 监控 Keepalived 状态、VIP 所在节点和 ARP 变化。
  • 使用 notify_master、notify_backup、notify_fault 记录切换日志。
  • 数据库等有状态服务必须额外设计复制、仲裁和故障恢复流程。
  • 不要用 Keepalived 代替应用认证、数据库高可用和真正的负载均衡。

10.10 面试总结

Keepalived 通过 VRRP 心跳判断节点是否存活,通过优先级决定谁持有 VIP,通过健康检查脚本判断业务是否正常,通过 VIP 漂移和免费 ARP 完成自动故障切换。

需要重点记住:

  • 心跳正常,只代表节点和网络正常,不代表 Nginx 或数据库正常。
  • 优先级决定谁更适合成为 MASTER,不等于服务健康状态。
  • 自动切换必须结合 VRRP 心跳、健康检查、优先级和抢占策略。
  • Keepalived 不负责数据同步,也不能完全防止脑裂。
  • Nginx Keepalive 是连接复用,Keepalived 是 VIP 高可用,两者是完全不同的机制。

11. MySQL 学习笔记:体系结构、数据引擎、慢查询日志与性能分析

11.1 MySQL 体系结构

11.1.1 客户端(连接者)

常见客户端:

  • mysql 命令行客户端
  • JDBC、ODBC
  • Python、Go、Java、PHP 等语言驱动
  • Navicat、DBeaver 等图形化管理工具
  • 应用程序连接池

客户端连接 MySQL 的方式:

  • TCP/IP,例如 host:3306
  • Linux 本地 Unix Socket,例如 /var/lib/mysql/mysql.sock
  • Windows 命名管道和共享内存

客户端主要负责:建立连接、发送认证信息、发送 SQL、接收结果集,以及管理连接池、超时和重连。

11.1.2 连接层

连接层负责接收客户端连接并进行基础认证。主要工作:管理客户端连接、用户认证、读取连接参数、分配线程、管理连接超时、支持 SSL/TLS、记录连接状态。

传统 MySQL 通常采用一个连接对应一个线程的模型。查看连接相关参数:

sql 复制代码
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'wait_timeout';
SHOW VARIABLES LIKE 'interactive_timeout';
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';

注意:认证通常发生在连接建立阶段;权限校验和 SQL 执行属于服务层流程;连接数不是越大越好,连接过多会增加线程切换和内存消耗。

11.1.3 服务层

服务层是 MySQL 处理 SQL 的核心区域。

1. SQL 接口

接收客户端 SQL,支持 DML、DDL、存储过程、视图、触发器、函数。

2. 解析器

进行词法分析和语法分析,生成语法树。常见错误:

text 复制代码
You have an error in your SQL syntax

这通常说明 SQL 在解析阶段已经失败。

3. 预处理器

检查表、列、权限等对象是否存在,并进行语义校验。

4. 查询优化器

决定 SQL 的执行方式,例如:使用哪个索引、多表连接顺序、是否使用临时表、是否使用文件排序、是否进行索引条件下推、是否使用覆盖索引。

5. 执行器

按照优化器生成的执行计划调用存储引擎接口,最终返回结果。

6. 日志和事务管理

服务层还涉及 Binary Log、用户权限、连接管理、SQL 解析、执行计划。

需要注意:MySQL 5.7 之前有查询缓存,MySQL 8.0 已经移除查询缓存;InnoDB 的 Redo Log 和 Undo Log 属于存储引擎层面;Binary Log 属于 MySQL Server 层。

一条 SQL 的大致执行流程:

text 复制代码
客户端
  ↓
连接层
  ↓
SQL 接口
  ↓
解析器
  ↓
预处理器
  ↓
查询优化器
  ↓
执行器
  ↓
存储引擎接口
  ↓
Buffer Pool
  ↓
磁盘文件
11.1.4 引擎层(重要)

引擎层是 MySQL 的插件式存储引擎层。MySQL 通过 Handler API 解耦 Server 层和存储引擎。不同存储引擎决定:是否支持事务、锁粒度、索引实现、崩溃恢复能力、外键支持、并发性能、数据文件格式。

常见存储引擎:

引擎 事务 典型用途
InnoDB 支持 行锁、MVCC OLTP、默认引擎
MyISAM 不支持 表锁 读多写少、旧系统
MEMORY 不支持 表锁 临时数据、内存表
ARCHIVE 不支持 行级插入 归档、压缩数据
CSV 不支持 表锁 数据交换

查看存储引擎:

sql 复制代码
SHOW ENGINES;
SHOW TABLE STATUS;
SHOW CREATE TABLE t;
11.1.5 存储层(物理层)

存储层负责将数据真正落到文件系统、磁盘、SSD 或网络存储。包括:数据页、索引页、Undo Log、Redo Log、表空间、Binary Log、临时文件、文件系统和块设备、Buffer Pool 与磁盘之间的数据交换。

InnoDB 通常采用页作为基本读取单位。查看页大小:

sql 复制代码
SHOW VARIABLES LIKE 'innodb_page_size';

默认通常为 16KB。

11.1.6 总结

MySQL 整体可以概括为:客户端、连接层、服务层、引擎层、存储层。Server 层负责 SQL 解析、优化和执行,存储引擎负责数据读写、索引、事务和锁。InnoDB 是现代 MySQL 的默认和核心存储引擎。

11.1.7 面试题
1. 一条 SQL 在 MySQL 中如何执行?

答:客户端连接,连接层认证,服务层解析和优化,执行器调用存储引擎,存储引擎通过 Buffer Pool 和磁盘完成读写,最后返回结果。

2. MySQL 8.0 为什么没有查询缓存?

答:查询缓存容易因任何写操作失效,命中率不稳定,并发维护成本高,收益有限,因此 MySQL 8.0 移除。

3. 为什么 Engine 层很重要?

答:它决定事务、锁、索引、崩溃恢复和并发能力,是 SQL 执行和数据存储之间的关键层。

4. Binary Log 和 Redo Log 有什么区别?

答:Binary Log 属于 Server 层,主要记录逻辑变更,用于复制和恢复;Redo Log 属于 InnoDB,记录物理页修改,用于崩溃恢复和持久化。

11.2 数据引擎

11.2.1 存储引擎层

MySQL 支持插件式存储引擎,Server 层通过统一接口调用存储引擎。

查看默认引擎:

sql 复制代码
SHOW VARIABLES LIKE 'default_storage_engine';

查看表的引擎:

sql 复制代码
SHOW TABLE STATUS LIKE 'orders'\G
SHOW CREATE TABLE orders\G

查看所有表引擎:

sql 复制代码
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'test';

修改表引擎:

sql 复制代码
ALTER TABLE orders ENGINE = InnoDB;

重要原则:默认优先使用 InnoDB;不要为了单纯提升读性能随意改用 MyISAM;存储引擎可以按表选择;一个事务通常只适合由支持事务的引擎处理。

11.2.2 数据文件存储

MySQL 默认数据目录通常是:

text 复制代码
/var/lib/mysql

InnoDB 常见文件:

text 复制代码
ibdata1
#ib_16384_0.dblwr
#innodb_redo/
undo_001
undo_002
mysql.ibd
test/
orders.ibd
binlog.000001
binlog.index
mysql-error.log
mysql-slow.log

解释说明:

  • ibdata1:系统表空间,可能包含数据字典、Undo 和 Change Buffer 信息。
  • *.ibd:开启 innodb_file_per_table 后,每个 InnoDB 表通常有独立表空间。
  • #innodb_redo/:MySQL 8.0.30 之后的 Redo Log 目录。
  • undo_001、undo_002:Undo 表空间。
  • *.dblwr:Doublewrite 文件,用于崩溃恢复。
  • mysql.ibd:MySQL 8.0 数据字典相关表空间。
  • binlog.*:Binary Log。

MyISAM 常见文件:

text 复制代码
orders.MYD
orders.MYI

说明:.MYD 是 MyISAM 数据文件,.MYI 是 MyISAM 索引文件。MySQL 8.0 不再使用旧的 .frm 文件管理表结构。

查看文件位置:

sql 复制代码
SHOW VARIABLES LIKE 'datadir';
SHOW VARIABLES LIKE 'innodb_data_file_path';
SHOW VARIABLES LIKE 'innodb_file_per_table';
SHOW VARIABLES LIKE 'log_bin';

InnoDB 文件设计要点:

  • 数据按页管理,默认 16KB。
  • Redo Log 使用 WAL,先写日志再异步刷数据页。
  • Undo Log 支持回滚和 MVCC。
  • Doublewrite 降低页写入过程中的崩溃风险。
  • Buffer Pool 是 InnoDB 最重要的内存区域。
  • 主键索引通常是聚簇索引。
  • 二级索引叶子节点通常保存主键值。
11.2.3 MyISAM 引擎

主要特点:不支持事务、不支持回滚、使用表级锁、不支持崩溃安全恢复、不支持外键约束,但支持全文索引、空间索引、表压缩,数据和索引通常分开存储。

优点:结构简单;某些只读场景下读取速度快;全文索引在旧版本中经常使用;表级维护比较简单。

缺点:写操作会锁整张表;并发写入能力差;不具备事务能力;崩溃后可能需要进行修复;不适合现代高并发 OLTP 业务;外键语法可能被解析但不生效。

常见维护命令:

sql 复制代码
CHECK TABLE orders;
REPAIR TABLE orders;
OPTIMIZE TABLE orders;

MyISAM 更适合:历史归档、只读数据、低并发读取、旧系统兼容。不建议用于订单、支付、库存、高并发写入,以及对事务和数据一致性要求高的业务。

11.3 慢查询日志与索引优化

11.3.1 慢查询日志

慢查询日志用于记录执行时间超过阈值的 SQL,是定位历史慢 SQL 的重要手段。核心参数:

sql 复制代码
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SHOW VARIABLES LIKE 'slow_query_log_file';

临时开启慢查询日志:

sql 复制代码
SET GLOBAL slow_query_log = ON;
SET GLOBAL long_query_time = 0.1;
SET GLOBAL log_queries_not_using_indexes = ON;

永久配置建议写入 my.cnf:

ini 复制代码
[mysqld]
slow_query_log = ON
long_query_time = 0.1
slow_query_log_file = /var/lib/mysql/mysql-slow.log

分析慢日志时,不能只看单次 SQL,还要关注:执行频率、总耗时、平均耗时、最大耗时、锁等待、扫描行数与返回行数的比例。

11.3.2 慢查询常见原因
  • 没有索引
  • 索引失效
  • 索引选择性低
  • 联合索引顺序错误
  • 返回数据量过大
  • 深度分页
  • 大表 JOIN
  • 隐式类型转换
  • 函数导致索引失效
  • 排序和分组使用临时表
  • 锁等待
  • 磁盘 IO 瓶颈
  • Buffer Pool 命中率低
11.3.3 添加索引
0. 查看索引
sql 复制代码
SHOW INDEX FROM orders;

查看索引信息:

sql 复制代码
SELECT *
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'test'
  AND TABLE_NAME = 'orders';
1. 唯一索引 unique
sql 复制代码
ALTER TABLE orders
ADD UNIQUE KEY uk_order_no (order_no);

特点:索引值必须唯一;可以保证业务唯一性;可能影响插入和更新性能;唯一索引通常也是查询索引。

2. 普通索引 key/index
sql 复制代码
ALTER TABLE orders
ADD KEY idx_user_id (user_id);

特点:不要求唯一,常用于等值查询和范围查询。KEY 和 INDEX 在 MySQL 中通常等价。

3. 联合索引
sql 复制代码
ALTER TABLE orders
ADD KEY idx_user_status_created
(user_id, status, created_at);

适合查询:

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 100
  AND status = 'PAID'
ORDER BY created_at DESC;

最左前缀原则。联合索引:

text 复制代码
(user_id, status, created_at)

可以使用索引的情况:

text 复制代码
WHERE user_id = ?
WHERE user_id = ? AND status = ?
WHERE user_id = ? AND status = ? AND created_at = ?
WHERE user_id = ? AND status = ? AND created_at > ?

不能完整利用索引的情况:

text 复制代码
WHERE status = ?
WHERE status = ? AND created_at = ?
WHERE user_id = ? AND created_at = ?

说明:查询条件缺少最左列,通常不能有效使用该联合索引;范围条件之后的索引列通常无法继续用于排序和过滤;WHERE status = ? AND user_id = ? 由于都是等值条件,优化器通常可以匹配联合索引;联合索引字段顺序要结合查询模式设计。

11.3.4 案例验证:慢查询

创建测试表:

sql 复制代码
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    order_no VARCHAR(32) NOT NULL,
    status VARCHAR(20) NOT NULL,
    amount DECIMAL(10,2) NOT NULL,
    created_at DATETIME NOT NULL,
    KEY idx_order_no (order_no)
) ENGINE = InnoDB;

制造测试数据:

sql 复制代码
INSERT INTO orders(user_id, order_no, status, amount, created_at)
SELECT
    FLOOR(RAND() * 100000),
    CONCAT('NO', LPAD(seq, 12, '0')),
    IF(RAND() > 0.5, 'PAID', 'WAIT'),
    ROUND(RAND() * 1000, 2),
    NOW() - INTERVAL FLOOR(RAND() * 365) DAY
FROM (
    SELECT @seq := @seq + 1 AS seq
    FROM information_schema.COLUMNS a,
         information_schema.COLUMNS b,
         (SELECT @seq := 0) init
    LIMIT 100000
) t;

慢查询前分析:

sql 复制代码
EXPLAIN
SELECT id, user_id, status, amount
FROM orders
WHERE user_id = 500
  AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

可能看到:

text 复制代码
type=ALL
key=NULL
rows=100000
Extra=Using where; Using filesort

添加联合索引:

sql 复制代码
ALTER TABLE orders
ADD KEY idx_user_status_created
(user_id, status, created_at);

重新分析:

sql 复制代码
EXPLAIN
SELECT id, user_id, status, amount
FROM orders
WHERE user_id = 500
  AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

可能看到:

text 复制代码
type=ref
key=idx_user_status_created
rows=10
Extra=Using index condition

验证慢查询:

sql 复制代码
SET GLOBAL long_query_time = 0.1;
SET GLOBAL slow_query_log = ON;
SELECT id, user_id, status, amount
FROM orders
WHERE user_id = 500
  AND status = 'PAID'
ORDER BY created_at DESC
LIMIT 20;

对比:

指标 添加索引前 添加索引后
访问类型 ALL ref
使用索引 idx_user_status_created
扫描行数 很大 明显减少
排序 Using filesort 可能消失
响应时间 明显降低
慢日志 通常消失
11.3.5 面试题
1. EXPLAIN 的 type 有哪些?

答:常见有 system、const、eq_ref、ref、range、index、ALL。通常越靠前越好。

2. Using index 和 Using index condition 有什么区别?

答:Using index 表示覆盖索引,不需要回表;Using index condition 表示索引条件下推,仍然可能回表。

3. 为什么有索引还是全表扫描?

答:可能是函数运算、隐式类型转换、前置模糊匹配、低选择性、联合索引不符合最左前缀,或者优化器判断全表扫描成本更低。

4. 联合索引为什么要注意顺序?

答:B+Tree 按照联合索引列顺序组织,必须从最左列开始匹配。等值条件字段优先,范围字段通常放在后面。

5. 索引越多越好吗?

答:不是。索引会增加磁盘占用,并降低 INSERT、UPDATE、DELETE 的性能,还会增加优化器选择成本。

6. 为什么 EXPLAIN 的 rows 不准确?

答:rows 是基于统计信息和索引基数估算的结果,不一定是真实扫描行数。需要真实数据时可使用 EXPLAIN ANALYZE。

11.4 性能分析

11.4.1 SHOW PROCESSLIST

作用:查看当前 MySQL 连接和线程状态。

命令:

sql 复制代码
SHOW PROCESSLIST;
SHOW FULL PROCESSLIST;

查询非 Sleep 会话:

sql 复制代码
SELECT *
FROM information_schema.PROCESSLIST
WHERE COMMAND <> 'Sleep'
ORDER BY TIME DESC;

常见列:

含义
Id 连接 ID
User 当前用户
Host 客户端地址
db 当前数据库
Command 当前命令
Time 当前状态持续时间
State 当前执行状态
Info 正在执行的 SQL

需要重点关注:Time 很大的查询、State=Waiting for table metadata lock、State=Waiting for lock、State=Sending data、State=Copying to tmp table、大量 Sleep 连接、大量 Query 线程。

终止 SQL:

sql 复制代码
KILL QUERY 123;

终止连接:

sql 复制代码
KILL CONNECTION 123;

注意:KILL QUERY 只终止当前 SQL;KILL CONNECTION 会断开整个连接;执行 KILL 需要相应权限;终止大型事务时要注意回滚耗时;生产环境不要盲目批量 KILL。

MySQL 8.0 还可以查看:

sql 复制代码
SELECT *
FROM performance_schema.threads;
SELECT *
FROM sys.processlist;
11.4.2 SHOW PROFILES

启用当前会话性能分析:

sql 复制代码
SET profiling = 1;

执行 SQL:

sql 复制代码
SELECT *
FROM orders
WHERE user_id = 500
  AND status = 'PAID';

查看 SQL 耗时:

sql 复制代码
SHOW PROFILES;

查看指定 SQL 的详细阶段:

sql 复制代码
SHOW PROFILE CPU, BLOCK IO FOR QUERY 1;

常见阶段:starting、checking permissions、Opening tables、init、System lock、optimizing、statistics、preparing、executing、Sending data、end、query end、closing tables、freeing items、cleaning up。

重要说明:SHOW PROFILES 是会话级工具;默认只保留少量最近查询;它属于旧版性能分析方式;MySQL 5.7 起已标记为不推荐;MySQL 8.0 更适合使用 Performance Schema、Sys Schema 和 EXPLAIN ANALYZE。

关闭:

sql 复制代码
SET profiling = 0;

推荐替代方式:

sql 复制代码
SELECT *
FROM sys.statement_analysis
ORDER BY total_latency DESC
LIMIT 10;

按 SQL 指纹聚合:

sql 复制代码
SELECT
    DIGEST_TEXT,
    COUNT_STAR,
    SUM_TIMER_WAIT,
    AVG_TIMER_WAIT,
    SUM_ROWS_EXAMINED,
    SUM_ROWS_SENT
FROM performance_schema.events_statements_summary_by_digest
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;

MySQL 8.0.18 之后:

sql 复制代码
EXPLAIN ANALYZE
SELECT *
FROM orders
WHERE user_id = 500
  AND status = 'PAID';
11.4.3 小结

完整排查链路:

text 复制代码
SHOW PROCESSLIST
发现当前异常连接和阻塞
        ↓
慢查询日志
发现历史高频、高耗时 SQL
        ↓
pt-query-digest
按 SQL 指纹聚合排序
        ↓
EXPLAIN
分析执行计划
        ↓
EXPLAIN ANALYZE
获取真实执行数据
        ↓
添加或调整索引
        ↓
再次对比慢日志和执行计划

核心结论:

  • SHOW PROCESSLIST 适合看当前正在发生什么。
  • 慢查询日志适合看历史上哪些 SQL 慢。
  • EXPLAIN 适合看 SQL 为什么慢。
  • EXPLAIN ANALYZE 适合看真实执行开销。
  • SHOW PROFILES 可以分析阶段耗时,但属于旧方案。
  • 索引是慢查询优化的重点,但不是唯一手段。
  • 优化后必须验证读性能、写性能、锁等待和磁盘 IO。
  • 生产环境优化索引前必须评估表大小、在线 DDL 风险和执行窗口。

12. MySQL 主从复制与 GTID

12.1 备份基础

MySQL 备份需要从多个维度理解:

  • 备份对象:逻辑数据或物理文件。
  • 备份范围:全量、增量、差异。
  • 备份方式:热备、温备、冷备。
  • 数据一致性:崩溃一致性或应用一致性。
  • 恢复目标:RPO 和 RTO。
  • 恢复粒度:实例、数据库、表或数据行。

RPO 表示允许丢失多少数据,RTO 表示允许业务中断多长时间。

12.2 逻辑备份

12.2.1 核心原理

逻辑备份不直接复制 MySQL 数据文件,而是通过 SQL 查询读取数据库结构和数据,再转换成 SQL、CSV、JSON 等格式。

常见工具:mysqldump、mydumper、MySQL Shell util.dumpInstance、SELECT INTO OUTFILE。

常见备份内容:建库和建表语句、索引和约束、数据记录、存储过程、函数、触发器和事件、GTID 或 Binlog 位点信息。

备份流程:SQL 查询,读取表结构和数据,转换成 SQL 或文本文件,压缩、加密、上传。恢复时,MySQL 需要重新执行 SQL、插入数据、重建索引并更新统计信息。

mysqldump 示例:

bash 复制代码
mysqldump -uroot -p \
  --single-transaction \
  --routines \
  --events \
  --triggers \
  --source-data=2 \
  --set-gtid-purged=ON \
  mydb > mydb.sql

恢复数据:

bash 复制代码
mysql -uroot -p mydb < mydb.sql

--single-transaction 适用于 InnoDB,通过一致性快照减少锁表时间。

注意事项:备份期间尽量不要执行 DDL;MyISAM 等非事务表无法获得相同的一致性保证;大事务可能影响一致性快照和复制位点;--set-gtid-purged 要根据恢复目标谨慎选择;备份文件必须配合 Binlog 才能实现完整的时间点恢复。

12.2.2 优点
  • 可读性强,方便检查和修改。
  • 跨版本、跨平台能力较好。
  • 支持单库、单表和部分数据备份。
  • 适合数据迁移和版本升级。
  • 适合开发和测试环境初始化。
  • 备份文件便于压缩、加密和传输。
  • 恢复单表比较方便。
12.2.3 缺点
  • 大库备份速度慢。
  • 大库恢复速度更慢。
  • 恢复时需要重新执行 SQL 和重建索引。
  • 恢复过程消耗大量 CPU、内存和磁盘 IO。
  • 对生产库存在查询压力。
  • 大对象和二进制数据效率较低。
  • 无法直接备份 InnoDB 数据页和 Redo Log。
  • 不能替代时间点恢复。
12.2.4 适用场景

中小型数据库、单表恢复、数据迁移、跨版本升级、开发和测试环境初始化、表结构和小范围数据归档,以及作为物理备份的补充。

12.3 物理备份

12.3.1 核心原理

物理备份直接复制数据库的数据文件、表空间、日志和元数据,关注页、文件和 LSN。

常见方式:停库后复制数据目录;LVM、ZFS 或云盘快照;Percona XtraBackup;MySQL Enterprise Backup。

冷备份示例:

bash 复制代码
systemctl stop mysqld
tar -czf mysql-data.tar.gz /var/lib/mysql
systemctl start mysqld

直接复制运行中的 MySQL 数据目录通常不安全,因为数据页、Redo Log 和 Undo Log 可能处于不一致状态。生产环境进行物理热备,通常使用 XtraBackup。

12.3.2 XtraBackup 原理

XtraBackup 是 Percona 提供的 MySQL 物理热备工具,主要用于 InnoDB。

备份流程:启动备份,复制 InnoDB 表空间和数据页,持续复制 Redo Log,记录备份期间的 LSN,获得一致性物理副本。

恢复前必须执行 prepare:prepare 阶段应用 Redo Log,回滚未提交事务,让数据文件和日志达到一致状态。因此,XtraBackup 不是简单复制文件,而是通过数据文件、Redo Log 和 LSN 保证备份最终具备一致性。

全量备份:

bash 复制代码
xtrabackup \
  --defaults-file=/etc/my.cnf \
  --user=backup \
  --password='密码' \
  --backup \
  --target-dir=/backup/mysql/full

准备备份:

bash 复制代码
xtrabackup --prepare --target-dir=/backup/mysql/full

恢复备份:

bash 复制代码
systemctl stop mysqld
xtrabackup --defaults-file=/etc/my.cnf 
--copy-back
--target-dir=/backup/mysql/full
chown -R mysql:mysql /var/lib/mysql
systemctl start mysqld

增量备份:

bash 复制代码
xtrabackup --backup \
  --target-dir=/backup/mysql/inc1 \
  --incremental-basedir=/backup/mysql/full

准备增量备份链:

bash 复制代码
xtrabackup --prepare --apply-log-only \
  --target-dir=/backup/mysql/full
xtrabackup --prepare --apply-log-only 
--target-dir=/backup/mysql/full
--incremental-dir=/backup/mysql/inc1
xtrabackup --prepare --target-dir=/backup/mysql/full

关键注意事项:XtraBackup 版本必须与 MySQL 主版本兼容;MySQL 8.0 通常使用 XtraBackup 8.0;MySQL 8.4 需要使用兼容 8.4 的版本;MyISAM 等非事务表可能仍然需要锁表;恢复时 MySQL 通常必须停止;数据目录通常必须为空;prepare 完成后才能执行 copy-back;增量备份必须按顺序 prepare;要保存 GTID、xtrabackup_info 和 Binlog 信息;物理备份通常还需要配合 Binlog 才能做时间点恢复。

12.3.3 优点
  • 大数据库备份速度快。
  • 恢复速度快。
  • 不执行 SQL。
  • 不需要重新插入数据。
  • 不必重建全部索引。
  • 支持 InnoDB 热备。
  • 支持增量备份。
  • 支持压缩和加密。
  • 支持流式备份。
  • 适合 TB 级数据库和低 RTO 场景。
12.3.4 缺点
  • 与 MySQL 版本、存储引擎和平台强相关。
  • 备份文件可读性差。
  • 跨大版本恢复困难。
  • 单表恢复不如逻辑备份方便。
  • prepare 和恢复流程复杂。
  • 备份文件通常较大。
  • 对表空间、日志和 LSN 理解要求高。
  • 混合存储引擎备份需要额外处理。
  • 工具版本不匹配可能导致失败。
12.3.5 适用场景

TB 级 MySQL 数据库、全实例灾备、快速恢复生产实例、搭建主从复制新节点、MySQL 大版本内迁移、低 RTO 的核心业务,以及配合 Binlog 做时间点恢复。

12.4 逻辑备份和物理备份对比

对比项 逻辑备份 物理备份
备份对象 SQL、数据记录 数据页、表空间、日志
常见工具 mysqldump、mydumper XtraBackup、文件快照
是否可读
备份速度 中小库可以,大库慢
恢复速度
跨版本能力 较强 较弱
跨平台能力 较强 较弱
单表恢复 方便 相对复杂
增量能力 依赖工具 XtraBackup 支持
生产影响 查询压力 关注 IO、LSN 和锁
适合数据量 中小规模 中大型和超大库
典型用途 迁移、单表恢复 灾备、全实例恢复、主从搭建

生产环境常见组合:物理备份负责全实例快速恢复;逻辑备份负责表结构、单表和小范围恢复;Binlog 负责时间点恢复;主从复制负责读扩展和可用性。

12.5 主从复制核心原理

MySQL 8.0.26 之后,官方建议使用 Source 和 Replica 术语。复制本质上就是 Binlog 的传输和重放:

text 复制代码
Source 写入 Binlog
  ↓
Replica IO Thread 拉取 Binlog
  ↓
写入 Replica Relay Log
  ↓
Replica SQL Thread 重放事务
  ↓
更新 Replica 数据

核心组件:Source Binlog Dump Thread、Replica IO Thread、Relay Log、Replica SQL Thread、Parallel Worker、GTID、Binlog Position。

12.6 Binlog 格式

12.6.1 STATEMENT

记录 SQL 语句。优点:日志体积较小,可读性较好。缺点:NOW()、RAND()、UUID() 等可能产生不一致,用户变量和部分上下文可能影响结果,对执行环境依赖较大。

12.6.2 ROW

记录每一行数据修改前后的内容。优点:复制结果更加确定,不依赖 SQL 执行上下文,适合数据一致性要求较高的场景。缺点:日志量通常更大,批量更新会产生大量 Row Event,可读性较差。

12.6.3 MIXED

MySQL 根据语句是否安全,自动选择 STATEMENT 或 ROW。

生产建议:

ini 复制代码
binlog_format = ROW
binlog_row_image = FULL

12.7 GTID 和 Binlog Position

12.7.1 Binlog Position

使用 Binlog 文件名和偏移量确定复制起点:

sql 复制代码
SHOW BINARY LOG STATUS;

旧版本命令:

sql 复制代码
SHOW MASTER STATUS;

特点:原理简单;需要准确记录 File 和 Position;拓扑变化时维护复杂;人工切换容易出错。

12.7.2 GTID

GTID 格式通常类似:

text 复制代码
3f2f7c11-aaaa-11ef-b111-00163e123456:1-1000

特点:每个事务有全局唯一编号;不需要手工维护 File 和 Position;主从切换更加方便;Replica 会自动跳过已经执行的事务;适合自动故障切换和复杂拓扑;需要控制事务和 DDL 使用;gtid_executed 和 gtid_purged 管理很关键。

推荐配置:

ini 复制代码
gtid_mode = ON
enforce_gtid_consistency = ON

12.8 复制模式

12.8.1 异步复制

默认模式。Source 提交事务,写入 Binlog,返回客户端成功,Replica 之后异步拉取和重放。

特点:性能最好,结构简单,Source 不等待 Replica 确认,Source 故障时可能丢失尚未传播的数据,RPO 通常大于 0。

12.8.2 半同步复制

Source 等待至少一个 Replica 确认收到 Binlog,再返回提交成功。

特点:降低数据丢失概率,但不等于零丢失;Replica 确认收到日志不代表已经应用;超时后通常降级为异步复制;会增加事务延迟。

核心区别:异步复制是 Source 不等待 Replica;半同步复制是 Source 等待 Replica 收到日志;同步复制是多数节点确认后才提交。

12.8.3 组复制

MySQL Group Replication 基于共识协议实现。特点:多节点组成复制组,支持单主模式和多主模式,支持自动选主和成员管理,支持冲突检测,对网络、延迟和事务大小较敏感,通常配合 MySQL Router 使用。

12.9 主从复制建立流程

text 复制代码
Source 开启 Binlog 和 GTID
  ↓
创建复制账号
  ↓
使用物理备份或逻辑备份初始化 Replica
  ↓
Replica 恢复数据并保存 GTID
  ↓
配置复制源
  ↓
启动 IO Thread 和 SQL Thread
  ↓
Replica 持续拉取并重放事务

Source 推荐配置:

ini 复制代码
[mysqld]
server_id = 1
log_bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
gtid_mode = ON
enforce_gtid_consistency = ON
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1

Replica 推荐配置:

ini 复制代码
[mysqld]
server_id = 2
relay_log = relay-bin
gtid_mode = ON
enforce_gtid_consistency = ON
read_only = ON
super_read_only = ON
relay_log_recovery = ON

创建复制用户:

sql 复制代码
CREATE USER 'repl'@'%' IDENTIFIED BY 'StrongPassword';
GRANT REPLICATION SLAVE ON . TO 'repl'@'%';
FLUSH PRIVILEGES;

配置复制:

sql 复制代码
CHANGE REPLICATION SOURCE TO
  SOURCE_HOST = '10.0.0.10',
  SOURCE_PORT = 3306,
  SOURCE_USER = 'repl',
  SOURCE_PASSWORD = 'StrongPassword',
  SOURCE_AUTO_POSITION = 1,
  GET_SOURCE_PUBLIC_KEY = 1;
START REPLICA;

查看复制状态:

sql 复制代码
SHOW REPLICA STATUS\G

重点关注:Replica_IO_Running、Replica_SQL_Running、Seconds_Behind_Source、Retrieved_Gtid_Set、Executed_Gtid_Set、Last_IO_Error、Last_SQL_Error、Relay_Log_Pos。

并行应用状态:

sql 复制代码
SELECT *
FROM performance_schema.replication_applier_status_by_worker;

12.10 复制延迟

常见原因:Replica 单线程应用事务、大事务、大表 DDL、表没有主键、随机写入、Source QPS 过高、Replica 磁盘性能差、网络延迟或带宽不足、Replica 承担大量读请求、复制过滤规则复杂、锁冲突、长事务。

并行复制配置:

ini 复制代码
replica_parallel_workers = 8
replica_parallel_type = LOGICAL_CLOCK

注意:并行复制不能消除所有延迟;大事务和 DDL 仍然可能成为瓶颈;多线程回放需要保证提交顺序和一致性。

12.11 复制一致性和可靠性

推荐配置:

ini 复制代码
sync_binlog = 1
innodb_flush_log_at_trx_commit = 1
relay_log_recovery = ON
gtid_mode = ON
enforce_gtid_consistency = ON

基本要求:所有节点的 server_id 必须唯一;复制账号只授予必要权限;从库开启 read_only 和 super_read_only;不要随意使用 sql_log_bin = 0;不要绕过 Binlog 写入数据;主从表结构必须保持一致;大事务和长时间 DDL 要拆分执行;Binlog 保留时间要覆盖最大复制延迟和恢复窗口;复制过滤规则必须经过验证。

12.12 主从复制不是备份

主从复制会同步以下错误:

sql 复制代码
DROP DATABASE production;
DELETE FROM orders;
UPDATE account SET balance = 0;

不同技术的职责:主从复制负责可用性和读扩展;Binlog 负责时间点恢复;逻辑备份负责单表恢复和数据迁移;物理备份负责实例级快速恢复。

完整方案通常是:XtraBackup 全量备份 + XtraBackup 增量备份 + Binlog 连续归档 + 主从复制 + 延迟从库 + 定期恢复演练。

12.13 主从复制优缺点

优点:提升读吞吐,支持读写分离,提供故障切换基础,降低单点故障风险,可在从库执行备份和报表查询,支持跨机房同步,支持滚动升级和迁移。

缺点:异步复制存在数据丢失窗口;复制延迟会影响读一致性;写扩展能力有限;拓扑复杂,故障切换需要工具;误操作和逻辑错误会同步传播;从库数据不一定实时;需要处理复制中断、GTID 空洞和版本兼容;不能替代备份。

12.14 物理备份和主从复制的关系

12.14.1 逻辑备份搭建从库

适合小数据库、可接受较长初始化时间、需要跨版本迁移的场景。基本流程:

  1. 使用 mysqldump --single-transaction 导出。
  2. 记录 GTID 或 Binlog Position。
  3. 导入 Replica。
  4. 配置 SOURCE_AUTO_POSITION=1。
  5. 或者指定 File 和 Position。
  6. 启动复制。
12.14.2 XtraBackup 搭建从库

适合大数据库、TB 级实例、快速构建副本。基本流程:

  1. 从 Source 执行 XtraBackup。
  2. 执行 prepare。
  3. 复制备份到 Replica。
  4. 执行 copy-back。
  5. 启动 MySQL。
  6. 从备份信息中获取 GTID。
  7. 配置复制。
  8. 启动 Replica。

XtraBackup 通常比逻辑备份更适合大库搭建从库,因为恢复时不需要重新执行全部 SQL 和重建索引。

12.15 面试总结

逻辑备份:通过 SQL 或数据文本导出,可读、灵活、跨版本能力较好;大库慢、恢复慢;适合小库、迁移和单表恢复。

物理备份:直接复制数据页、表空间和日志;XtraBackup 是典型工具;备份和恢复速度快;适合大库、灾备和快速恢复;版本和平台耦合较强。

主从复制:Source 写 Binlog;Replica IO Thread 拉取 Binlog;Replica 写 Relay Log;Replica SQL Thread 重放事务;GTID 适合自动切换和复杂拓扑;异步复制存在数据丢失窗口;半同步复制降低但不能完全消除丢失风险;主从复制不是备份;完整恢复能力必须结合备份、Binlog、复制和恢复演练。

相关推荐
Lyra_Infra2 小时前
从 MySQL 到达梦:一次信创隔离环境里的数据库迁移踩坑实录
数据库·后端·mysql
泡茶喝茶写代码3 小时前
量化数据开发实战系列(第 25 篇):基金基础接口实战:基金列表、ETF-LOF 分类、基金概况、净值数据
java·数据库·人工智能·python·mysql
worilb4 小时前
Win下使用同一套MySQL创建第二个独立实例
mysql
A心有千千结4 小时前
Nginx网关可观测建设:打通流量入口,加速线上故障诊断
nginx·prometheus·devops
风哥2号5 小时前
数据库教程FGMT40‑MySQL性能优化之性能基准测试
数据库·mysql
葡萄成熟时 !5 小时前
MySQL 全套学习笔记(JDBC+连接池)
mysql
梦帮科技6 小时前
一次性会员支付系统的可靠性设计:幂等、金额校验、Webhook 与链上确认
人工智能·神经网络·mysql·区块链·建造者模式·合成复用原则·加密货币
初願致夕霞6 小时前
MySQL_事务(MVCC机制详解)
数据库·mysql
geovindu6 小时前
sql: Data Modeling Patterns using mysql
sql·mysql·设计模式·数据库开发