**摘要:**本文整理了一套面向生产环境的部署与运维学习笔记,涵盖项目整体技术栈、基于 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。
重要注意事项:
- 同一个 upstream 通常只配置一种负载均衡算法,不要同时配置 ip_hash、least_conn、hash、random。
- ip_hash、hash、least_time、random 对版本和产品类型有不同限制,部署前执行 nginx -V 确认。
- 修改配置后必须先检查,再平滑重载:
shell
nginx -t
systemctl reload nginx
- 负载均衡算法不能替代健康检查。开源版健康检查能力有限,生产环境通常还需要 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 报文。工作过程:
- MASTER 节点定期发送 VRRP 心跳。
- BACKUP 节点持续监听心跳。
- 如果 BACKUP 在超时时间内没有收到心跳,就认为 MASTER 故障。
- BACKUP 提升为 MASTER,并将 VIP 添加到自己的网卡。
- 节点发送免费 ARP,通知交换机和其他主机更新 ARP 表。
- 流量随后切换到新的 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 自己猜测服务是否正常,而是由下面几个条件共同触发:
- VRRP 心跳中断。
- 网卡或网络链路故障。
- 健康检查脚本失败。
- 节点优先级下降到低于其他节点。
- 节点主动释放 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 主要缺点
- 只能解决节点级高可用,不负责数据同步。例如 MySQL 主从切换后,还要处理复制关系、事务一致性和数据回放。
- 健康检查配置不当会导致 VIP 不切换。如果只依赖 VRRP 心跳,服务进程崩溃但节点仍在线时,Keepalived 不会自动发现。
- 可能出现脑裂。心跳网络中断后,两个节点可能都认为对方故障,同时持有同一个 VIP,导致 ARP 冲突和流量异常。
- 云环境支持有限。很多公有云默认不允许 VRRP 组播、伪造 IP 或免费 ARP。云环境通常更适合使用云负载均衡、浮动 IP 或云厂商提供的 HA 方案。
- VIP 切换需要 ARP 收敛。即使 VIP 已经漂移,交换机、网关和客户端仍可能需要一段时间更新 ARP,切换期间会短暂丢包。
- 配置错误可能中断整个业务。例如 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 逻辑备份搭建从库
适合小数据库、可接受较长初始化时间、需要跨版本迁移的场景。基本流程:
- 使用 mysqldump --single-transaction 导出。
- 记录 GTID 或 Binlog Position。
- 导入 Replica。
- 配置 SOURCE_AUTO_POSITION=1。
- 或者指定 File 和 Position。
- 启动复制。
12.14.2 XtraBackup 搭建从库
适合大数据库、TB 级实例、快速构建副本。基本流程:
- 从 Source 执行 XtraBackup。
- 执行 prepare。
- 复制备份到 Replica。
- 执行 copy-back。
- 启动 MySQL。
- 从备份信息中获取 GTID。
- 配置复制。
- 启动 Replica。
XtraBackup 通常比逻辑备份更适合大库搭建从库,因为恢复时不需要重新执行全部 SQL 和重建索引。
12.15 面试总结
逻辑备份:通过 SQL 或数据文本导出,可读、灵活、跨版本能力较好;大库慢、恢复慢;适合小库、迁移和单表恢复。
物理备份:直接复制数据页、表空间和日志;XtraBackup 是典型工具;备份和恢复速度快;适合大库、灾备和快速恢复;版本和平台耦合较强。
主从复制:Source 写 Binlog;Replica IO Thread 拉取 Binlog;Replica 写 Relay Log;Replica SQL Thread 重放事务;GTID 适合自动切换和复杂拓扑;异步复制存在数据丢失窗口;半同步复制降低但不能完全消除丢失风险;主从复制不是备份;完整恢复能力必须结合备份、Binlog、复制和恢复演练。