在现代云原生和微服务架构中,MySQL 8.0 凭借其窗口函数、CTE、角色管理、原子 DDL、并行查询等特性,已成为新一代应用的首选数据库。本文将手把手带你用二进制方式 部署一个高度优化、安全可靠的 MySQL 8.0 实例,并集成 Percona XtraBackup 8.0 实现自动化全量/增量备份策略。
💡 为什么不用 yum/apt/docker?
二进制部署能精准控制版本、路径、依赖,避免系统包管理器带来的"黑盒"问题,更适合对稳定性要求极高的生产环境。
本方案在多个实际项目中运用多次,稳定高效。
🔧 一、准备工作:下载部署包
1. MySQL 社区版(8.0)
前往 MySQL Archive 下载 Generic Linux (glibc 2.17 或 2.28) x86_64 版本的 tar.gz 包。
✅ 关键提示:
- glibc 2.17 → 对应 RHEL/CentOS 7(el7)
- glibc 2.28 → 对应 RHEL/CentOS 8+(el8/el9)
选错版本会导致
GLIBC_2.XX not found错误!
https://downloads.mysql.com/archives/community/
2. Percona XtraBackup(8.0)
⚠️ 重要:XtraBackup 8.0 仅兼容 MySQL 8.0,且需匹配 minor 版本(如 MySQL 8.0.32 需 XtraBackup 8.0.32+)。
下载地址:Percona XtraBackup 8.0 Binary Tarball
3. qpress(压缩工具)
XtraBackup 默认使用 qpress 进行压缩,需单独安装:
bash
yum install qpress
或者
https://repo.percona.com/yum/release/7/RPMS/x86_64/qpress-11-3.el7.x86_64.rpm 下载
✅ 说明:qpress 是一种快速压缩算法,比 gzip 更适合数据库备份场景(支持并行压缩/解压)。
📁 二、创建标准化目录结构
良好的目录规划是运维效率的基础:
bash
mkdir -p /mydata/{soft,script}
mkdir -p /mydata/{data,binlog,report/{inspection,logerr},backup/{xtrabackup/{full,fullx,incr},mylog/{errlog/{daily,weekly,monthly,yearly},slowlog/{daily,weekly,monthly,yearly},genlog}},dump}
| 目录 | 用途说明 |
|---|---|
/mydata/soft |
存放所有原始安装包(便于回滚或重装) |
/mydata/data |
MySQL 数据目录(InnoDB 表空间、ibdata、frm 等) |
/mydata/binlog |
二进制日志(用于主从复制、Point-in-Time 恢复) |
/mydata/backup/xtrabackup/full |
当前有效的全量备份(用于增量基准) |
/mydata/backup/xtrabackup/fullx |
历史归档全量(保留多个周期,防误删) |
/mydata/backup/xtrabackup/incr |
增量备份(每天基于 full 生成) |
/mydata/mylog/slowlog/... |
按时间维度归档慢查询日志,便于分析 |
✅ 设计原则:分离数据、日志、备份,避免 I/O 争抢;按时间分层,便于日志轮转和清理。
📦 三、解压部署包
bash
mkdir -p /usr/local/{mysql,xtrabackup}
# 解压 MySQL
tar -xf /mydata/soft/mysql8.tar.gz --strip-components=1 -C /usr/local/mysql
# 解压 XtraBackup
tar -xf /mydata/soft/xtrabackup.tar.gz --strip-components=1 -C /usr/local/xtrabackup
# 安装 qpress(解压工具)
rpm -ivh qpress-11-3.el8.x86_64.rpm
⚙️ 四、系统资源与内核参数优化
1. 用户资源限制(ulimit)
- 交互式 Shell 生效
bash
echo '
if [ $USER = "mysql" ]; then
if [ $SHELL = "/bin/ksh" ]; then
ulimit -u 16384
ulimit -n 65536
else
ulimit -u 16384 -n 65536
fi
fi' >> /etc/profile.d/root.sh
- 当前会话立即生效
bash
ulimit -Hu 16384 -Hn 65536
- PAM 永久生效
bash
echo '
mysql soft nproc 16384
mysql hard nproc 16384
mysql soft nofile 16384
mysql hard nofile 65536
mysql soft stack 16384
mysql hard stack 32768' >> /etc/security/limits.d/88-root-limits.conf
| 参数 | 含义 | 推荐值 | 作用 |
|---|---|---|---|
nproc |
最大进程数 | 16384 | 防止因连接数过多导致 fork 失败 |
nofile |
最大打开文件数 | 65536 | MySQL 需要大量 fd(表、连接、日志等) |
stack |
栈大小(KB) | 16M/32M | 避免递归调用栈溢出 |
✅ 为什么需要? MySQL 在高并发下会创建大量线程和文件句柄,系统默认限制(通常 1024)极易成为瓶颈。
2. 内核参数调优
bash
echo '
vm.dirty_background_ratio = 5
vm.dirty_ratio = 10
vm.overcommit_memory = 2
vm.overcommit_ratio = 90
vm.swappiness = 0' >> /etc/sysctl.conf
bash
sysctl -p
| 参数 | 作用解释 |
|---|---|
vm.dirty_background_ratio=5 |
当脏页占内存 5% 时,后台 flusher 线程开始写盘 |
vm.dirty_ratio=10 |
脏页达 10% 时,应用 write() 会被阻塞直到刷盘完成(防止突发写爆内存) |
vm.overcommit_memory=2 |
严格检查内存分配(配合 overcommit_ratio) |
vm.overcommit_ratio=90 |
允许分配的内存 = swap + 90% * RAM(避免 OOM) |
vm.swappiness=0 |
强烈建议!尽量不用 swap,防止数据库性能抖动 |
✅ 这些参数对 I/O 密集型数据库 至关重要,能显著提升写入稳定性和响应速度。
👤 五、创建用户 & 权限配置-
bash
# 创建 mysql 用户(禁止登录)
getent passwd | grep mysql &> /dev/null || useradd -s /sbin/nologin mysql
# 授权目录
chown -R mysql.mysql /mydata /usr/local/mysql
# 添加 PATH(root 用户)
sed -i 's#PATH=.*#PATH=$PATH:$HOME/bin:/usr/local/mysql/bin:/usr/local/xtrabackup/bin#' ~root/.bash_profile
. ~root/.bash_profile
✅ useradd -s /sbin/nologin:创建无 shell 登录权限的系统用户,提升安全性。
✅
chown -R mysql:mysql:确保 MySQL 进程有权限读写数据和程序目录。✅ 修改
PATH:方便 root 用户直接使用mysql、xtrabackup等命令。
🔒 六、SELinux 配置(关键!)
SELinux 常导致"权限拒绝"却无明显报错。推荐两种方式:
方式一:禁用 SELinux(简单粗暴)
⚠️ 适用于测试环境或对安全要求不高的场景。
bash
# 临时禁用
setenforce 0
# 永久关闭
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
方式二:精细化授权(推荐生产环境)
bash
# 先确认是否已安装了semanage
semanage -h
# 未安装的话先进行安装
yum install -y policycoreutils-python
# 标注数据目录
semanage fcontext -a -t mysqld_db_t "/mydata/data(/.*)?"
restorecon -R /mydata/data
# 标注 binlog 目录
semanage fcontext -a -t mysqld_log_t "/mydata/binlog(/.*)?"
restorecon -R /mydata/binlog
# 标注备份目录(按需选择类型)
semanage fcontext -a -t mysqld_db_t "/mydata/backup(/.*)?"
restorecon -R /mydata/backup
| SELinux 类型 | 用途 |
|---|---|
mysqld_db_t |
MySQL 数据文件(.ibd, .frm, ibdata1) |
mysqld_log_t |
日志文件(binlog, error log, slow log) |
mysqld_tmp_t |
临时文件(如 LOAD DATA 产生的 tmp 文件) |
✅ 原理:SELinux 通过"类型强制"(Type Enforcement)控制进程对文件的访问。即使文件权限是 777,若类型不匹配,仍会被拒绝。
✅
restorecon:根据策略重新打标签,使规则生效。
🔍 若遇到未知拒绝,用 ausearch -m avc | audit2why 分析建议类型。
📄 七、配置 my.cnf(性能核心!)
bash
# 备份原文件
[ -e /etc/my.cnf ] && cp /etc/my.cnf /etc/my.cnf.`date "+%Y%m%d-%H%M%S"`
注意,下面参数需要先计算好,非常重要!
💡 innodb_buffer_pool_size 计算公式:
物理内存 × 实例比例 × 0.7(一机一库则比例=1,一机两库则比例=1/2,以此类推)得出结果后,将下方配置中对应的参数进行修改,不要用示例的参数!!!
bash
echo "[client]
socket=/usr/local/mysql/mysql.sock
[mysqld]
##Base config
#字符集
character_set_server=UTF8MB4
#存储引擎
default_storage_engine=InnoDB
#mysql运行用户
user=mysql
#监听端口
port=3306
#mysql家目录
basedir=/usr/local/mysql
#mysql数据目录
datadir=/mydata/data
#最大连接数
max_connections=1000
table_open_cache = 4000
table_definition_cache = 2000
thread_cache_size = 100
#性能关键参数自动调整
#innodb_dedicated_server=on
#innodb 缓冲池(RAM*70%,适用于一机一库)
innodb_buffer_pool_size=3G
innodb_buffer_pool_instances = 8
innodb_fast_shutdown = 0
innodb_flush_log_at_trx_commit = 1
# SSD 推荐配置
innodb_flush_method = O_DIRECT
innodb_io_capacity = 2000
innodb_io_capacity_max = 4000
innodb_read_io_threads = 8
innodb_write_io_threads = 8
#socket位置
socket=/usr/local/mysql/mysql.sock
#pid文件位置
pid_file=/usr/local/mysql/mysqld.pid
#错误日志位置
log_error=/mydata/data/mysql-error.log
#错误日志级别(默认2:warning)
log_error_verbosity=3
#redo日志
innodb_log_file_size=2G
innodb_log_files_in_group=2
innodb_log_buffer_size=16M
#binlog日志
server_id=100
log_bin=/mydata/binlog/mysql-bin
binlog_format=row
binlog_expire_logs_seconds=604800
log_bin_trust_function_creators=1
#slowlog
slow_query_log=on
long_query_time=10
log_slow_extra=on
slow_query_log_file=/mydata/data/mysql-slow.log
#跳过名称解析
skip_name_resolve=1
#开启计划任务
event_scheduler=1
#数据包的最大大小
max_allowed_packet=256M
#不区分大小写配置,以小写保存磁盘
lower_case_table_names=1
#sql_text的长度
performance_schema_max_sql_text_length=10240
#打开文件数限制
open_files_limit=65536
#日志时间格式
log_timestamps=SYSTEM
#sql模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
#禁用timestamp字段update时更新时间戳
explicit_defaults_for_timestamp=on
#激活角色
activate_all_roles_on_login=1
##Replication config##
#gtid_mode=ON
#enforce_gtid_consistency=ON
#binlog_ignore_db=monitor
##slave config
#master_info_repository=table
#relay_log_info_repository=table
#relay_log=/mydata/binlog/relay-log
#relay_log_recovery=ON
#replicate_ignore_db=sys
#replicate_ignore_db=mysql
#replicate_ignore_db=information_schema
#replicate_ignore_db=performance_schema
#skip_slave_start
#slave_skip_errors=1062
#sql_slave_skip_counter=1
#查询日志(调试或审计)
#general_log=on
#general_log_file=/mydata/data/general.log
##Xtrabackup备份配置(请勿删除或修改)
[xtrabackup]
user=bkpuser
password=Bkpuser_2026
parallel=4
compress
compress-threads=4" > /etc/my.cnf
| 配置段 | 参数名 | 值 | 说明 |
|---|---|---|---|
[client] |
socket |
/usr/local/mysql/mysql.sock |
指定客户端连接本地 MySQL 实例时使用的 Unix socket 文件路径。 |
[mysqld] |
character_set_server |
UTF8MB4 |
设置服务器默认字符集为 utf8mb4,支持完整的 Unicode(包括 emoji)。 |
[mysqld] |
default_storage_engine |
InnoDB |
设置新建表的默认存储引擎为 InnoDB(事务安全、支持外键)。 |
[mysqld] |
user |
mysql |
指定 mysqld 进程运行的操作系统用户(提升安全性,避免以 root 运行)。 |
[mysqld] |
port |
3306 |
MySQL 监听的 TCP/IP 端口号,默认为 3306。 |
[mysqld] |
basedir |
/usr/local/mysql |
MySQL 安装目录(包含 bin、lib、share 等子目录)。 |
[mysqld] |
datadir |
/mydata/data |
MySQL 数据文件(如 ibdata1、.ibd、.frm 等)的存储目录。 |
[mysqld] |
max_connections |
1000 |
允许的最大并发连接数(包括后台线程),超过将拒绝新连接。 |
[mysqld] |
table_open_cache |
4000 |
表缓存大小,用于缓存打开的表描述符,减少重复打开/关闭开销。 |
[mysqld] |
table_definition_cache |
2000 |
表定义(.frm 或数据字典)缓存数量,避免频繁读取磁盘元数据。 |
[mysqld] |
thread_cache_size |
100 |
线程缓存大小,复用线程以减少创建/销毁开销,提升短连接性能。 |
[mysqld] |
innodb_buffer_pool_size |
3G |
InnoDB 缓冲池大小,用于缓存数据和索引,是 InnoDB 性能最关键参数。 |
[mysqld] |
innodb_buffer_pool_instances |
8 |
将缓冲池划分为 8 个独立实例,减少内部争用,提升并发性能。 |
[mysqld] |
innodb_fast_shutdown |
0 |
关闭时执行完整 purge 和 merge,确保下次启动快速(值 0 = 慢速干净关机)。 |
[mysqld] |
innodb_flush_log_at_trx_commit |
1 |
每次事务提交都刷 redo log 到磁盘,保证 ACID(最安全,但 I/O 最高)。 |
[mysqld] |
innodb_flush_method |
O_DIRECT |
InnoDB 数据文件 I/O 使用 O_DIRECT,绕过 OS 缓存,避免双缓冲(适合 SSD)。 |
[mysqld] |
innodb_io_capacity |
2000 |
设备每秒可处理的 I/O 操作数(IOPS),用于控制后台刷新速率(SSD 推荐值)。 |
[mysqld] |
innodb_io_capacity_max |
4000 |
后台任务(如脏页刷新)可突发达到的最大 IOPS。 |
[mysqld] |
innodb_read_io_threads |
8 |
InnoDB 用于读操作的 I/O 线程数(默认 4,SSD 可适当增加)。 |
[mysqld] |
innodb_write_io_threads |
8 |
InnoDB 用于写操作的 I/O 线程数(默认 4,SSD 可适当增加)。 |
[mysqld] |
socket |
/usr/local/mysql/mysql.sock |
mysqld 监听的 Unix socket 文件路径,供本地客户端连接使用。 |
[mysqld] |
pid_file |
/usr/local/mysql/mysqld.pid |
mysqld 进程 ID 文件路径,用于管理脚本识别进程。 |
[mysqld] |
log_error |
/mydata/data/mysql-error.log |
错误日志文件路径,记录启动、运行、关闭过程中的关键信息。 |
[mysqld] |
log_error_verbosity |
3 |
错误日志详细级别:1=错误,2=错误+警告,3=错误+警告+提示信息(MySQL 5.7+ / 8.0)。 |
[mysqld] |
innodb_log_file_size |
2G |
单个 redo log 文件大小(总大小 = 此值 × innodb_log_files_in_group)。 |
[mysqld] |
innodb_log_files_in_group |
2 |
redo log 文件数量(通常为 2,总 redo 空间 = 2 × 2G = 4G)。 |
[mysqld] |
innodb_log_buffer_size |
16M |
redo log 内存缓冲区大小,大事务可适当调高以减少磁盘写。 |
[mysqld] |
server_id |
100 |
服务器唯一标识,在主从复制或组复制中必须全局唯一。 |
[mysqld] |
log_bin |
/mydata/binlog/mysql-bin |
启用二进制日志并指定基础路径和前缀(用于复制、PITR 恢复)。 |
[mysqld] |
binlog_format |
row |
二进制日志格式为 ROW,记录每一行变更,最安全且兼容性好。 |
[mysqld] |
binlog_expire_logs_seconds |
604800 |
自动清理 7 天(604800 秒)前的 binlog 文件,防止磁盘爆满。 |
[mysqld] |
log_bin_trust_function_creators |
1 |
允许用户创建存储函数而不强制要求 DETERMINISTIC 或 READS SQL DATA(方便开发,但有安全风险)。 |
[mysqld] |
slow_query_log |
on |
启用慢查询日志,记录执行时间超过阈值的 SQL。 |
[mysqld] |
long_query_time |
10 |
慢查询阈值为 10 秒(单位:秒,可为小数如 0.1)。 |
[mysqld] |
log_slow_extra |
on |
(MySQL 8.0.14+)在慢日志中额外记录更多统计信息(如内存使用、临时表等)。 |
[mysqld] |
slow_query_log_file |
/mydata/data/mysql-slow.log |
慢查询日志文件路径。 |
[mysqld] |
skip_name_resolve |
1 |
跳过客户端主机名 DNS 反向解析,提升连接速度并避免 DNS 问题。 |
[mysqld] |
event_scheduler |
1 |
启用事件调度器(可执行定时任务,等价于 ON)。 |
[mysqld] |
max_allowed_packet |
256M |
单个 SQL 语句或结果集允许的最大大小(影响大 BLOB/批量插入)。 |
[mysqld] |
lower_case_table_names |
1 |
表名和数据库名以小写存储并比较(Windows/macOS 兼容模式,Linux 上需初始化时设定)。 |
[mysqld] |
performance_schema_max_sql_text_length |
10240 |
Performance Schema 中记录的 SQL 文本最大长度(默认 1024,此处扩展至 10KB)。 |
[mysqld] |
open_files_limit |
65536 |
设置 mysqld 可打开的最大文件描述符数(需操作系统 ulimit 支持)。 |
[mysqld] |
log_timestamps |
SYSTEM |
日志时间戳使用系统时区(而非 UTC),便于与系统日志对齐。 |
[mysqld] |
sql_mode |
NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES |
SQL 模式:• STRICT_TRANS_TABLES:对事务表启用严格模式(非法值报错)• NO_ENGINE_SUBSTITUTION:建表时若指定引擎不可用则报错(不降级)。 |
[mysqld] |
explicit_defaults_for_timestamp |
on |
禁用 TIMESTAMP 字段的隐式默认行为(如自动更新),需显式声明(MySQL 5.6+ 推荐开启)。 |
[mysqld] |
activate_all_roles_on_login |
1 |
用户登录时自动激活其拥有的所有角色(无需手动 SET ROLE)。 |
[xtrabackup] |
user |
bkpuser |
XtraBackup 连接 MySQL 所用的用户名(需具备 BACKUP_ADMIN 等权限)。 |
[xtrabackup] |
password |
Bkpuser_2026 |
XtraBackup 用户密码 |
[xtrabackup] |
parallel |
4 |
XtraBackup 并行备份线程数(加速备份过程)。 |
[xtrabackup] |
compress |
(无值) | 启用备份压缩(使用 zlib)。 |
[xtrabackup] |
compress-threads |
4 |
压缩所用的线程数(与 parallel 解耦,可单独设置)。 |
✅ [xtrabackup] 段:XtraBackup 会读取此段配置,实现免密、并行、压缩备份。
🛠️ 八、初始化 & 启动服务
1. 初始化数据目录(无密码 root)
bash
mysqld --initialize-insecure --user=mysql --datadir=/mydata/data
✅ --initialize-insecure:创建空密码 root 账号(仅限内网或首次初始化使用,后续必须改密)。
✅ 若用
--initialize,会生成随机密码并打印到 error log。
2. 配置 systemd 服务(现代 Linux 必备)
bash
echo '[Unit]
Description=MySQL 8 Server
Documentation=man:mysqld(8)
Documentation=http://dev.mysql.com/doc/refman/en/using-systemd.html
After=network.target
After=syslog.target
[Install]
WantedBy=multi-user.target
[Service]
User=mysql
Type=notify
# Disable service start and stop timeout logic of systemd for mysqld service.
TimeoutSec=0
# Execute pre and post scripts as root
PermissionsStartOnly=true
# Start main service
ExecStart=/usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf
# Use this to switch malloc implementation
EnvironmentFile=-/etc/sysconfig/mysql
# Sets open_files_limit
LimitNOFILE = 10000
#Restart=on-failure
RestartPreventExitStatus=1
# Set enviroment variable MYSQLD_PARENT_PID. This is required for restart.
Environment=MYSQLD_PARENT_PID=1
PrivateTmp=false' > /usr/lib/systemd/system/mysqld.service
✅ Type=notify:MySQL 8.0 使用 Type=notify(而非 5.7 的 forking),通过 sd_notify() 通知 systemd 启动完成。
3. 启动服务
bash
systemctl daemon-reload && systemctl enable mysqld
systemctl start mysqld
🔐 九、安全加固
1. 设置 root 密码
bash
mysql -u root --skip-password -e "alter user 'root'@'localhost' identified by 'Wenb1n_dev';"
# 验证是否成功
mysqladmin -uroot -pWenb1n_dev version
2. 启用密码策略插件
sql
#先登陆到mysql中
mysql -uroot -pWenb1n_dev
#执行sql
INSTALL PLUGIN validate_password SONAME 'validate_password.so';
✅ 启用后,创建用户时会强制校验密码强度(长度、数字、大小写、特殊字符等)。
3. 创建运维超级账号(锁定 root)
sql
## 创建一个名为 'u_admin' 的用户,仅允许从本地主机(localhost)连接,并设置密码为 'Wenb1n_dev'
CREATE USER 'u_admin'@'localhost' IDENTIFIED BY 'Wenb1n_dev';
## 授予 'u_admin'@'localhost' 用户对所有数据库和所有表的全部权限,并允许其将权限授予其他用户(即拥有授权权限)
GRANT ALL ON *.* TO 'u_admin'@'localhost' WITH GRANT OPTION;
## 锁定默认的 'root'@'localhost' 账户,使其无法登录(增强安全性,防止直接使用 root 登录)
ALTER USER 'root'@'localhost' ACCOUNT LOCK;
## 创建一个名为 ro_dql 的角色,用于只读查询操作(DQL: Data Query Language)
CREATE ROLE ro_dql;
## 将 SELECT 和 SHOW VIEW 权限授予 ro_dql 角色,使其能查询数据和查看视图定义(适用于所有数据库和表)
GRANT SELECT, SHOW VIEW ON *.* TO ro_dql;
## 创建一个名为 ro_dml 的角色,用于数据操作语言(DML: Data Manipulation Language)相关操作
CREATE ROLE ro_dml;
## 授予 ro_dml 角色一系列 DML 及辅助权限:
## 包括查询、查看视图、查看数据库列表、创建临时对象、索引管理、增删改数据、执行存储过程、
## 读取文件(FILE)、查看进程(PROCESS)、重载配置(RELOAD)、锁定表等(作用于所有数据库和表)
GRANT SELECT, SHOW VIEW, SHOW DATABASES, CREATE, INDEX, INSERT, UPDATE, DELETE, EXECUTE, FILE, PROCESS, RELOAD, LOCK TABLES ON *.* TO ro_dml;
## 创建一个名为 ro_ddl 的角色,用于数据定义语言(DDL: Data Definition Language)相关操作
CREATE ROLE ro_ddl;
## 授予 ro_ddl 角色一系列 DDL 权限:
## 包括创建/删除/修改数据库对象(如表、视图、存储过程、事件、触发器、表空间、临时表等),
## 以及引用外键(REFERENCES)等(作用于所有数据库和表)
GRANT CREATE, CREATE VIEW, CREATE ROUTINE, CREATE TABLESPACE, CREATE TEMPORARY TABLES, CREATE VIEW,
EVENT, REFERENCES, TRIGGER, DROP, ALTER, ALTER ROUTINE ON *.* TO ro_ddl;
✅ 最佳实践:生产环境不应使用 root 操作,应创建专用运维账号并锁定 root。
💡 提示:
ro_dql、ro_dml、ro_ddl是自定义角色,后续可通过GRANT ro_dql TO 'some_user'@'host';将角色分配给具体用户。- 锁定 root 账户后,请确保已有其他具备管理员权限的账户(如这里的
u_admin)可用,否则可能导致无法管理数据库。 - 生产环境中应谨慎授予
FILE、PROCESS、RELOAD等高危权限。
4. 开放防火墙
bash
firewall-cmd --permanent --add-port=3306/tcp && firewall-cmd --reload
✅ 仅开放必要端口,遵循最小开放原则。
🗃️ 十、创建业务 & 备份账号
sql
## 创建一个名为 'demo' 的用户,允许从任意主机('%' 表示所有 IP)连接,并设置密码为 'Power_2025'
CREATE USER 'demo'@'%' IDENTIFIED BY 'Power_2025';
## 将之前定义的角色 ro_dml 和 ro_ddl 授予 'demo'@'%' 用户,
## 使其拥有数据操作(DML)和数据定义(DDL)的广泛权限(具体权限由角色定义决定)
GRANT ro_dml, ro_ddl TO 'demo'@'%';
## 创建一个专用于备份操作的本地用户 'bkpuser',仅允许从 localhost 连接,密码为 'Bkpuser_2020'
CREATE USER 'bkpuser'@'localhost' IDENTIFIED BY 'Bkpuser_2026';
## 授予 'bkpuser'@'localhost' 执行数据库备份所需的最小必要权限:
## SELECT:用于读取数据;
## BACKUP_ADMIN:MySQL 8.0+ 中用于执行备份相关操作的动态权限(如使用 mysqlbackup 或克隆插件);
## RELOAD:用于执行 FLUSH 操作(如刷新日志、缓存等);
## PROCESS:查看当前运行的线程(用于监控或排除备份干扰);
## LOCK TABLES:在备份时锁定表以保证一致性;
## REPLICATION CLIENT:查看主从复制状态(如用于获取二进制日志位置,支持 PITR 点-in-time 恢复)
## 所有权限作用于所有数据库和表(*.*)
GRANT SELECT, BACKUP_ADMIN, RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'bkpuser'@'localhost';
✅ MySQL 8.0 角色优势:
- 权限集中管理(
GRANT role TO user)- 动态生效(无需
FLUSH PRIVILEGES)- 支持
activate_all_roles_on_login=ON自动激活✅ 备份账号权限说明:
BACKUP_ADMIN:MySQL 8.0 新增权限,专用于备份操作(替代旧版的SELECT+LOCK TABLES)REPLICATION CLIENT:获取 binlog 位置(用于 PITR)
🔄 十一、自动化备份脚本(XtraBackup)
将以下脚本保存为 /mydata/script/backup/xtrabackup_auto.sh,并添加 cron 任务:
# 每天凌晨 3 点执行
0 3 * * * /mydata/script/backup/xtrabackup_auto.sh >> /var/log/xtrabackup_cron.log2>&1
✅ 策略说明:
- 周日 :全量备份 → 存入
full/- 周一至六 :增量备份 → 基于
full/存入incr/YYYYMMDD/- 自动归档 :旧全量移至
fullx/,保留 38 天- 自动清理:增量保留 31 天,至少保留最新 1 份
📌 脚本已内置:并发锁、空目录防护、有效性校验、日志记录。
sql
首次备份手工执行全量备份
xtrabackup --backup --target-dir=/mydata/backup/xtrabackup/full
sql
#!/bin/bash
# =============================================================================
# MySQL XtraBackup 自动备份脚本(全量 + 增量)
#
# 功能说明:
# - 每周日 03:00 执行全量备份
# - 周一至周六 03:00 执行增量备份(基于最近一次有效全量)
# - 自动清理过期备份(保留策略:增量31天,全量保留两个周期共38天以上)
# - 防止空目录误删、并发执行、无效备份等问题
# 使用方式:通过 cron 调用,例如:
# 0 3 * * * /path/to/xtrabackup_auto.sh
# =============================================================================
# 加载 root 环境变量(确保 xtrabackup 命令可用)
source ~root/.bash_profile
# -----------------------------
# 1. 配置参数
# -----------------------------
# 备份周期(天):7天为一个完整周期(周日全量,其余增量)
BACKUP_CYCLE_DAYS=7
# 增量备份保留天数
INCREMENTAL_RETAIN_DAYS=31
# fullx 目录中全量备份保留时间 = 周期 + 保留天数(至少保留两个全量)
FULLX_RETAIN_DAYS=$((BACKUP_CYCLE_DAYS + INCREMENTAL_RETAIN_DAYS)) # 38天
# 当前星期几(0=周日, 1=周一, ..., 6=周六)
WEEKDAY=$(date +%w)
# 当前时间戳(用于日志)
CURRENT_TIME=$(date '+%Y-%m-%d %H:%M:%S')
# 备份目录定义
FULL_DIR="/mydata/backup/xtrabackup/full" # 当前有效的全量备份目录
FULLX_DIR="/mydata/backup/xtrabackup/fullx" # 归档的全量备份(历史)
INCR_DIR="/mydata/backup/xtrabackup/incr" # 增量备份目录
# 今日增量子目录名(格式:YYYYMMDD)
INCR_SUBDIR=$(date '+%Y%m%d')
# 7天前的日期(用于归档旧全量,格式:YYYYMMDD)
ARCHIVE_DATE=$(date -d "-${BACKUP_CYCLE_DAYS} days" '+%Y%m%d')
# 日志文件路径(带时间戳)
LOG_FILE="/mydata/script/backup/log/xtrabackup_$(date '+%Y%m%d-%H%M%S').log"
# 锁文件(防止脚本并发执行)
LOCK_FILE="/tmp/xtrabackup.lock"
# -----------------------------
# 2. 工具函数
# -----------------------------
# 写入日志(带时间前缀)
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# 安全获取目录中最新子目录(避免空目录问题)
get_latest_subdir() {
local dir="$1"
if [ ! -d "$dir" ] || [ -z "$(ls -A "$dir")" ]; then
echo "" # 返回空表示无内容
return
fi
# 按修改时间排序,取最新非隐藏目录
find "$dir" -mindepth 1 -maxdepth 1 -type d ! -name ".*" -printf '%T@ %f\n' 2>/dev/null | \
sort -n | tail -1 | cut -d' ' -f2
}
# 检查 XtraBackup 全量备份是否有效(存在关键文件)
is_valid_full_backup() {
local dir="$1"
[ -f "$dir/xtrabackup_checkpoints" ] && grep -q "full-backuped" "$dir/xtrabackup_checkpoints"
}
# -----------------------------
# 3. 初始化
# -----------------------------
# 创建必要目录
mkdir -p "$FULL_DIR" "$FULLX_DIR" "$INCR_DIR" "$(dirname "$LOG_FILE")"
# 初始化日志
log "========== 开始 XtraBackup 自动备份任务 =========="
log "当前时间: $CURRENT_TIME"
log "今天是星期: $WEEKDAY (0=周日)"
# 检查锁文件,防止并发
if [ -f "$LOCK_FILE" ]; then
log "错误:检测到上一次备份仍在运行(锁文件存在),退出。"
exit 1
fi
# 创建锁文件
trap 'rm -f "$LOCK_FILE"; log "脚本异常退出,已清理锁文件。"' EXIT
echo $$ > "$LOCK_FILE"
# -----------------------------
# 4. 清理过期备份
# -----------------------------
log "开始清理过期备份..."
# --- 清理增量备份(保留最近31天,但至少保留最新1个)---
LATEST_INCR=$(get_latest_subdir "$INCR_DIR")
if [ -n "$LATEST_INCR" ]; then
log "最新增量备份目录: $LATEST_INCR"
find "$INCR_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +$INCREMENTAL_RETAIN_DAYS ! -name "$LATEST_INCR" -exec rm -rf {} \; 2>>"$LOG_FILE"
log "已清理超过 ${INCREMENTAL_RETAIN_DAYS} 天的增量备份(保留最新一份)。"
else
log "增量备份目录为空,跳过清理。"
fi
# --- 清理归档全量备份(保留最近38天,但至少保留最新2个)---
LATEST_FULLX_1=$(get_latest_subdir "$FULLX_DIR")
LATEST_FULLX_2=$(find "$FULLX_DIR" -mindepth 1 -maxdepth 1 -type d ! -name ".*" -printf '%T@ %f\n' 2>/dev/null | \
sort -n | tail -2 | head -1 | cut -d' ' -f2)
if [ -n "$LATEST_FULLX_1" ]; then
log "最新两个归档全量: $LATEST_FULLX_2, $LATEST_FULLX_1"
# 构建 find 条件:排除最新两个
FIND_CMD="find \"$FULLX_DIR\" -mindepth 1 -maxdepth 1 -type d -mtime +$FULLX_RETAIN_DAYS"
[ -n "$LATEST_FULLX_1" ] && FIND_CMD="$FIND_CMD ! -name \"$LATEST_FULLX_1\""
[ -n "$LATEST_FULLX_2" ] && FIND_CMD="$FIND_CMD ! -name \"$LATEST_FULLX_2\""
eval "$FIND_CMD -exec rm -rf {} \;" 2>>"$LOG_FILE"
log "已清理超过 ${FULLX_RETAIN_DAYS} 天的归档全量备份(保留最新两份)。"
else
log "归档全量目录为空,跳过清理。"
fi
# -----------------------------
# 5. 执行备份
# -----------------------------
if [ "$WEEKDAY" -eq 0 ]; then
# ========== 周日:执行全量备份 ==========
log "今天是周日,执行全量备份。"
# 如果 FULL_DIR 存在且有效,先归档到 FULLX_DIR
if [ -d "$FULL_DIR" ] && is_valid_full_backup "$FULL_DIR"; then
ARCHIVE_PATH="$FULLX_DIR/$ARCHIVE_DATE"
log "将当前全量备份归档至: $ARCHIVE_PATH"
mv "$FULL_DIR" "$ARCHIVE_PATH"
elif [ -d "$FULL_DIR" ]; then
log "警告:当前全量目录存在但无效,直接删除。"
rm -rf "$FULL_DIR"
fi
# 创建新全量目录
mkdir -p "$FULL_DIR"
# 执行全量备份
log "开始执行 XtraBackup 全量备份..."
if xtrabackup --backup --target-dir="$FULL_DIR" >>"$LOG_FILE" 2>&1; then
log "✅ 全量备份成功完成。"
else
log "❌ 全量备份失败!请检查日志。"
exit 1
fi
else
# ========== 周一至周六:执行增量备份 ==========
log "今天是工作日(星期 $WEEKDAY),尝试执行增量备份。"
# 检查 FULL_DIR 是否存在且有效
if [ ! -d "$FULL_DIR" ] || ! is_valid_full_backup "$FULL_DIR"; then
log "错误:未找到有效的全量备份($FULL_DIR 不存在或无效),无法执行增量备份!"
log "建议:请先手动执行一次全量备份,或等待周日自动全量。"
exit 1
fi
# 创建今日增量目录
TODAY_INCR_DIR="$INCR_DIR/$INCR_SUBDIR"
mkdir -p "$TODAY_INCR_DIR"
# 执行增量备份
log "基于 $FULL_DIR 执行增量备份到 $TODAY_INCR_DIR ..."
if xtrabackup --backup --target-dir="$TODAY_INCR_DIR" --incremental-basedir="$FULL_DIR" >>"$LOG_FILE" 2>&1; then
log "✅ 增量备份成功完成。"
else
log "❌ 增量备份失败!请检查日志。"
exit 1
fi
fi
# -----------------------------
# 6. 结束
# -----------------------------
log "========== XtraBackup 备份任务结束 =========="
# 清理锁文件
rm -f "$LOCK_FILE"
trap - EXIT
exit 0
✅ 总结:这套方案的优势
| 特性 | 说明 |
|---|---|
| 纯净部署 | 无系统包依赖,版本可控 |
| 性能极致 | 内核 + MySQL 参数双重优化 |
| 安全合规 | SELinux 支持、最小权限账号、角色管理 |
| 灾备可靠 | XtraBackup 8.0 热备 + 自动化策略 |
| 运维友好 | 标准化目录 + systemd 管理 + 角色授权 |
关注我,获取更多 DBA 实战干货!
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ ❤️