--- 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 的参数配置