Files
cs-note/hhs/MySQL/01-入门基础/02-安装与初始化.md
T

339 lines
14 KiB
Markdown
Raw Normal View History

2026-05-24 11:42:38 +08:00
---
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 的参数配置