Files
2026-05-24 11:42:38 +08:00

339 lines
14 KiB
Markdown
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.
---
tags: [MySQL, 安装, Docker, 配置]
create time: 2026-05-16 00:00
---
# MySQL 安装与初始化
## 概述
本文系统讲解 MySQL Community Edition 的安装部署和初始化配置,涵盖三种主流方式:**Docker Compose**(推荐开发环境)、**YUM/RPM 包安装**、**通用二进制包安装**。同时深入解析 `my.cnf` 配置文件的核心参数体系,以及 `mysql_install_db` 初始化时的底层行为。
> [!TIP] 学习建议
>
> 1. **初学者**优先掌握 Docker Compose 方式,快速搭建可复现的开发环境;
> 2. **运维/生产场景**参考二进制安装流程,理解服务初始化的完整链条;
> 3. `my.cnf` 参数部分按优先级阅读,先吃透 InnoDB 和网络两大块,日志和 Binlog 可在后续进阶时补充。
>
> *思考题:为什么 InnoDB Buffer Pool 通常建议设为物理内存的 50%~70%,而不是 90%?*(答案见下文 InnoDB 参数段落的注释)
## Docker Compose 方式(推荐开发环境)
这是最轻量且可复现的方案,适合日常开发和 CI 测试。
```yaml
version: '3.8'
services:
mysql:
image: mysql:8.0
container_name: mysql-dev
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: rootpassword # 首次启动自动创建 root 用户
MYSQL_DATABASE: app_db # 自动创建初始数据库
MYSQL_USER: app_user # 创建普通应用账户
MYSQL_PASSWORD: app_password # 该账户对 MYSQL_DATABASE 拥有完全权限
ports:
- "3306:3306" # 宿主机 3306 → 容器内 3306
volumes:
- ./init-db:/docker-entrypoint-initdb.d # 首次启动自动执行 SQL
- mysql-data:/var/lib/mysql # 持久化数据(容器删除后不丢失)
- ./my-custom.cnf:/etc/mysql/conf.d/my.cnf # 覆盖默认配置
command: >
--character-set-server=utf8mb4 # 统一字符集
--collation-server=utf8mb4_unicode_ci # 排序规则(不区分大小写)
--default-authentication-plugin=mysql_native_password # 兼容旧版客户端认证
--max-connections=500 # 最大并发连接数
--innodb-buffer-pool-size=256M # 开发机无需太大
volumes:
mysql-data:
```
### 启动与常用操作
```bash
docker compose up -d # 后台启动
docker compose down # 停止并移除容器(保留 volume)
docker compose logs -f # 查看实时日志
docker compose exec mysql mysql -u root -p # 进入容器内客户端
docker compose ps # 查看容器状态
docker compose restart # 重启(配置变更后适用)
```
> [!QUESTION] 生产环境应该硬编码密码吗?
> 当然不。上述 `docker-compose.yml` 中的明文密码仅适用于本地开发。
> 生产环境应使用 Docker Secrets、环境变量文件 `.env` 或密钥管理工具(如 HashiCorp Vault)来注入凭据。
>
> ```bash
> # .env 文件示例(加入 .gitignore!)
> MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASS}
> MYSQL_DATABASE=myapp_prod
> ```
>
> Docker Compose 会自动将 `${VAR}` 替换为环境变量值。
### init-db 目录妙用
`docker-entrypoint-initdb.d/` 下只要是 `.sql` 或 `.sh` 脚本,会在数据库**首次启动**时按字母序自动执行:
| 文件类型 | 执行时机 | 典型用途 |
|---------|---------|---------|
| `.sql` | 首次初始化 | DDL建表、默认数据、权限分配 |
| `.sh` | 首次初始化 | 调用 `mysql` CLI 执行动态逻辑 |
> [!WARNING] 注意
> - 容器已有数据时,init-db 脚本**不会被重新执行**。修改脚本后需删除 volume 重建:`docker compose down -v && docker compose up -d`
> - `.sh` 脚本中若需连接 MySQL,务必等待 ready(可通过 `docker compose wait` 或健康检查机制)
## 二进制安装(生产环境参考)
对于需要精细控制的场景,官方推荐的两种方式:YUM/RPM 包适合标准发行版;通用二进制包提供最大灵活性。
### 方式一:YUM/RPM(CentOS / RHEL / Amazon Linux)
```bash
# 1. 下载 YUM 仓库配置(以 EL9 为例)
sudo rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el9-3.noarch.rpm
# 2. 安装 MySQL Server
sudo yum install mysql-server -y
# 3. 启动服务并设置开机自启
sudo systemctl start mysqld
sudo systemctl enable mysqld
# 4. 获取临时 Root 密码(首次安装后打印到日志)
sudo grep 'temporary password' /var/log/mysqld.log
# 5. 安全加固(修改默认密码、移除匿名账户等)
sudo mysql_secure_installation
```
> [!TIP] 临时密码在哪?
> MySQL 8.0 首次启动时会在 `/var/log/mysqld.log` 中生成一个随机临时密码。
> 找到类似 `A temporary password is generated for root@localhost: xxxxxxxx` 的行,使用该密码登录 `mysql_secure_installation`。
### 方式二:通用二进制包(跨发行版兼容)
适用于非标准发行版或需要自定义目录结构的场景:
```bash
# 1. 下载并解压(替换实际版本号)
wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.xx-linux-glibc2.28-x86_64.tar.xz
tar -xvf mysql-8.0.xx-linux-glibc2.28-x86_64.tar.xz
sudo mv mysql-8.0.xx-linux-glibc2.28-x86_64 /usr/local/mysql
# 2. 创建专用系统用户(不建议用 root 运行 MySQL)
cd /usr/local/mysql
sudo groupadd mysql
sudo useradd -r -g mysql -s /bin/false mysql
# 3. 设置权限 + 初始化数据目录
sudo chown -R mysql:mysql .
sudo bin/mysqld --initialize --user=mysql --datadir=/data/mysql
# 4. 配置文件(可选,放在 /etc/my.cnf)
# 指定 basedir=/usr/local/mysql 和 datadir=/data/mysql
# 5. 初始化 SSL 证书(MySQL 8.0 必需)
sudo bin/mysql_ssl_rsa_setup --datadir=/data/mysql
# 6. 启动服务
sudo bin/mysqld_safe --user=mysql &
# 7. 验证登录
sudo bin/mysql -u root -p
```
> [!QUESTION] caching_sha2_password vs mysql_native_password?
> MySQL 8.0 将默认认证插件从 `mysql_native_password` 切换为 `caching_sha2_password`:
> - **caching_sha2_password**(新默认):更强的 SHA2 加密缓存机制,推荐所有新版客户端使用
> - **mysql_native_password**(旧版):兼容性最好,老旧语言驱动可能不支持 SHA2
>
> *如果你的连接报 `client does not support authentication protocol` 错误,临时解决方案是切换回旧版认证:*
> ```sql
> ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'new_password';
> ```
>
> *但长期方案应是升级客户端库。*
## 配置文件 my.cnf
### 配置文件加载优先级
MySQL 按以下顺序查找配置文件,**后读取的覆盖先读取的**(命令行参数始终最高):
```mermaid
graph LR
E["~/.my.cnf"] --> D["$MYSQL_HOME/my.cnf"]
D --> C["/etc/mysql/my.cnf"]
C --> B["/etc/my.cnf"]
B --> A["--defaults-file=<指定路径>"]
A --> F["命令行参数"]
F -.->|"最终生效"| Z["生效配置"]
```
> [!NOTE] 关键理解
> - `/etc/my.cnf` 通常是入口文件,内部可能 `!include` 其他目录;
> - `--defaults-file` 是**唯一**使用的配置文件,跳过所有默认路径(极少场景使用);
> - Docker 中常用 `--config` 或 `conf.d/` 目录方式挂载自定义配置。
### 核心配置参数详解
MySQL 配置集中在 `[mysqld]` 段落下。按功能域分类如下:
#### 网络
```ini
# ==================== 网络 ====================
port = 3306 # MySQL 默认端口
bind-address = 0.0.0.0 # 监听所有网卡(Docker 内用 127.0.0.1)
max_connections = 500 # 最大并发连接数
max_connect_errors = 1000000 # 同一主机连续中断次数上限,超限将被 block_host
```
#### 字符集
```ini
# ==================== 字符集 ====================
character-set-server = utf8mb4 # 支持 emoji 的完整 UTF-8 实现
collation-server = utf8mb4_unicode_ci # 不区分大小写的排序规则
```
*为什么用 `utf8mb4` 而非 `utf8`?* MySQL 的 `utf8` 只支持最多 3 字节,无法存储 emoji;`utf8mb4` 才是标准 UTF-8(最长 4 字节)。
#### InnoDB 存储引擎
```ini
# ==================== InnoDB ====================
innodb_buffer_pool_size = 1G # 缓存数据和索引的物理内存占比
innodb_log_file_size = 512M # Redo Log 文件大小
innodb_flush_log_at_trx_commit = 1 # ACID 一致性级别控制
innodb_flush_method = O_DIRECT # 绕过 OS page cache,避免双重缓冲
innodb_file_per_table = 1 # 每张表独立 .ibd 文件
innodb_io_capacity = 2000 # SSD 建议调高,HDD 约 100~200
```
**关键概念解释:**
| 参数 | 原理 | 调整建议 |
|------|------|---------|
| `innodb_buffer_pool_size` | InnoDB 的核心缓存区域,存放数据页和索引页 | **物理内存的 50%~70%**——为 OS 和其他进程预留空间,占满 90% 反而导致性能下降 |
| `innodb_log_file_size` | Redo Log 保证持久性(WAL 机制) | 越大容纳未刷盘事务越多,但重启恢复越慢;`512M` 是通用起点 |
| `O_DIRECT` | 直接 I/O 模式 | 启用后 MySQL 自己管理 Buffer Pool,不再经过 OS Cache(避免双重缓冲浪费) |
### 慢查询日志
```ini
# ==================== 日志 ====================
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2
log_error = /var/log/mysql/error.log
```
**使用方式:** 启用后可以通过 `mysqldumpslow` 分析或直接用文本工具查看超时查询:
```bash
# 找出执行时间最长的 10 条
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
# 实时追踪慢查询(运维调试利器)
tail -f /var/log/mysql/slow.log
```
### Binlog(二进制日志)配置
Binlog 是 MySQL 最核心的日志类型,用于主从复制和数据恢复:
```ini
# ==================== Binlog ====================
server_id = 1 # 集群内唯一标识(1~2^32-1)
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW # 记录每一行变更明细
binlog_expire_logs_seconds = 604800 # 7 天后自动清理
max_binlog_size = 100M # 单文件上限(实际可能略超)
gtid_mode = ON # 全局事务 ID,简化主从切换
enforce_gtid_consistency = ON # 只允许 GTID 安全的事务
```
> [!TIP] Binlog Format 对比
> | 格式 | 内容 | 优点 | 缺点 |
> |------|------|------|------|
> | **STATEMENT** | 原始 SQL | 文件小,审计直观 | 函数调用可能导致主从不一致 |
> | **ROW**(推荐) | 每行变更前后值 | 数据一致性最强 | 文件较大 |
> | **MIXED** | 混合模式 | 兼顾两者 | 逻辑复杂,难以预测 |
>
> **结论**:生产环境统一用 `ROW`,现代磁盘成本已不再是瓶颈。
> [!QUESTION] innodb_flush_log_at_trx_commit 怎么选?
> | 值 | 行为 | 丢失数据风险 | 性能 |
> |---|------|------------|------|
> | **1** | 每次事务 commit 都刷盘 | **零**(ACID 完全符合) | 最低 |
> | **2** | 每次 commit 写入 OS 缓存,每秒刷盘 | 系统断电丢失 < 1 秒 | 高 |
> | **0** | 每秒都写并刷盘,commit 仅写入缓冲区 | 可能丢多秒数据 | 最高 |
>
> **建议**:金融/支付类业务必须用 1;内容类业务可以用 2 换取性能提升。
## mysql_install_db 初始化原理
当首次启动 MySQL(或手动执行 `mysql_install_db`)时,底层会完成以下几件事:
```mermaid
flowchart TD
S["mysqld 启动"] --> I["数据目录检查"]
I -->|为空| D["执行 mysql_install_db"]
I -->|非空| C["跳过初始化,直接加载现有数据"]
D --> A["创建系统数据库"]
A --> A1["mysql schema — 用户权限、角色、插件"]
A --> A2["sys schema — 性能分析视图集合"]
A --> A3["performance_schema — 运行时指标采集"]
A --> A4["information_schema — SQL 元数据虚拟表"]
A --> B["创建管理员账户"]
B --> B1["root@localhost — 超级管理员"]
B --> B2["使用 caching_sha2_password 认证"]
A --> E["写入其他系统文件"]
E --> E1["ibdata1 — InnoDB 系统表空间"]
E --> E2["auto.cnf — Server UUID"]
C --> F["加载 my.cnf 配置并监听端口"]
```
**逐段拆解:**
| 步骤 | 说明 | 常见关联问题 |
|------|------|-------------|
| 创建 system schemas | `mysql` 存权限,`performance_schema` 供性能监控,`information_schema` 是 SQL 标准提供的元数据字典 | 删了这些库会导致实例无法正常运行 |
| 创建 root 账户 | 8.0 不再允许匿名访问 `test` 库;密码通过临时日志输出而非自动设为空 | 忘记临时密码 → 删除 data dir 重新初始化 |
| 初始化 InnoDB 表空间 | `ibdata1` 存放系统表和 undo log;`ib_logfile*` 是 Redo Log(两份互为镜像) | Redo Log 损坏 → 实例无法启动 |
| 写入 auto.cnf | 包含唯一的 Server UUID,主从复制和 GTID 依赖此标识 | 克隆虚拟机未重置 UUID → 主从冲突 |
### 数据目录结构
初始化完成后,数据目录的组织方式如下:
```
/var/lib/mysql/ (datadir 根目录)
├── ibdata1 # InnoDB 系统表空间(存放系统表 + undo log)
├── ib_logfile0 # Redo Log 文件 1
├── ib_logfile1 # Redo Log 文件 2(与 1 互为镜像)
├── auto.cnf # Server UUID(UUID 不变,主从复制不冲突)
├── mysql/ # 系统数据库(user, role, db, columns_priv 等 .ibd 文件)
├── performance_schema/ # 运行时性能数据采集
├── sys/ # 基于 performance_schema 的友好视图
├── app_db/ # 每个用户数据库一个目录
│ ├── users.ibd # 单表独立表空间(innodb_file_per_table=1)
│ └── orders.ibd
└── relay-log.index # 主从复制的中继日志索引(仅作为 replica 时存在)
```
## 关联笔记
- [[hhs/Redis/01-安装与部署]] — Redis 的安装方式与 MySQL 对比
- [[hhs/EXAM/Week05]] — Docker Compose 多服务编排示例
- [[hhs/DEV/Go-Database]] — Go 驱动连接 MySQL 的参数配置