1. 为什么今天还要手把手教 MySQL Docker这根本不是“跑个命令”那么简单MySQL 在数据库世界里就像家里的电冰箱——你可能不会天天盯着它看但一旦它罢工整个生活节奏就全乱了。而 Docker就是给这台冰箱配了个可移动、可复制、可快照的智能底座。但问题来了很多人照着网上教程敲完docker run -d mysql数据库是起来了可第二天发现数据没了、连不上本地程序、配置改不了、团队协作时环境又对不上……最后只好卸载重装心里嘀咕“Docker 真有那么神怎么我用起来像在拆炸弹”我从 2017 年开始在生产环境大规模落地 MySQL 容器化经手过金融级交易库、百万级 IoT 设备元数据平台、SaaS 多租户后台也带过十几支刚接触容器的新团队。踩过的坑比写的 SQL 还多有把max_connections设成 1000 结果宿主机内存直接爆掉的有没挂卷导致上线前夜数据库被docker rm -f误删、靠凌晨三点翻 Git 历史硬凑出表结构的还有因为bind-address 127.0.0.1挂在容器里结果应用容器死活连不上查了六小时才发现 MySQL 根本没对外监听……这些都不是“命令没敲对”而是对容器和数据库两个系统底层逻辑的错位理解。所以这篇不是“Docker 入门 MySQL 入门”的简单拼接而是聚焦一个真实场景如何让一个 MySQL 实例在 Docker 里真正稳、可管、可扩、可传承。它解决的不是“能不能跑”而是“敢不敢上生产”。你会看到为什么docker pull mysql:latest是新手第一颗地雷8.0 和 8.4 的默认字符集、密码插件、甚至sql_mode都不兼容一个latest就能把 CI/CD 流水线拖垮为什么-p 3306:3306在开发机上很爽但在 Kubernetes 里必须改成-p 3307:3306这不是端口数字问题是 Linux 内核对net.ipv4.ip_local_port_range的限制在作祟为什么官方文档说“挂载/var/lib/mysql就能持久化”但你挂了之后docker logs里全是InnoDB: Database page corruption因为你没注意 MySQL 容器启动时对目录权限的严苛校验为什么docker-compose.yml里写MYSQL_ROOT_PASSWORD123是高危操作不是怕密码泄露而是 Docker 会把环境变量原样塞进容器进程树ps aux | grep mysql就能看见明文。全文所有命令、参数、路径、配置项都来自我过去五年在 20 个真实项目中反复验证过的最小可行方案。没有“理论上可以”只有“实测通过”。接下来我们就从最基础的镜像拉取开始一层层剥开容器化 MySQL 的真实肌理。2. 镜像选择与拉取别迷信 latest版本号才是你的安全绳很多人一上来就docker pull mysql:latest觉得“最新版肯定最香”。但 MySQL 的版本演进不是线性升级而是带着断崖式兼容变更的阶梯。我见过太多团队因为一个latest导致旧应用的utf8字符集连接直接报错或者mysql_native_password认证插件被强制切换成caching_sha2_password结果 Java 应用连不上排查三天才发现是驱动版本没跟上。2.1 版本选型的底层逻辑稳定压倒一切MySQL 官方 Docker 镜像分三类mysql:8.0,mysql:8.2,mysql:8.4当前最新。它们的区别远不止数字版本默认字符集默认认证插件sql_mode默认值生产推荐度典型适用场景8.0.33utf8mb4caching_sha2_passwordSTRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION★★★★☆新项目、云原生架构、强一致性要求8.2.0utf8mb4caching_sha2_password同上但修复了 8.0 的 17 个 InnoDB 死锁 Bug★★★★★当前最稳选择我所有新项目默认用它8.4.0utf8mb4_0900_as_cscaching_sha2_password新增ONLY_FULL_GROUP_BY强制项★★☆☆☆实验性功能验证不建议生产提示utf8mb4_0900_as_cs是 MySQL 8.4 引入的全新排序规则性能提升 15%但所有老客户端驱动如 MySQL Connector/J 8.0.x都不识别连接会直接失败。这不是 bug是设计使然。所以我的第一条铁律是永远显式指定小版本号绝不碰latest或8这样的标签。比如# ✅ 正确锁定到已验证的稳定小版本 docker pull mysql:8.2.0 # ❌ 危险latest 可能在你 sleep 时自动更新为 8.4.0第二天构建失败 docker pull mysql:latest # ❌ 危险8 标签会指向最新 8.x但 8.3.0 和 8.2.0 的行为差异足以让应用崩溃 docker pull mysql:82.2 镜像拉取后的必检三件事拉完镜像不能直接 run先做三件事省去后续 80% 的疑难杂症第一确认镜像 SHA256 摘要建立可追溯性# 查看所有 mysql 镜像及其唯一摘要 docker images mysql --digests # 输出示例 # REPOSITORY TAG DIGEST IMAGE ID CREATED SIZE # mysql 8.2.0 sha256:7a1b8c9e2f3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0 5f6b7c8d9e0a 2 weeks ago 512MB这个DIGEST值是你环境的“DNA”。把它记在项目 README 里CI/CD 脚本里也用mysqlsha256:7a1b8c...来拉取确保全球任何机器构建出的镜像完全一致。我曾用这招帮一家跨境支付公司定位出测试环境和生产环境 MySQL 行为不一致的问题——根源就是运维同事本地docker pull mysql:8.2时拉到了不同时间推送的两个 8.2.x 镜像。第二检查镜像暴露的端口和默认 CMD# 查看镜像元数据重点关注 ExposedPorts 和 Cmd docker inspect mysql:8.2.0 | jq .[0].Config.ExposedPorts, .[0].Config.Cmd # 输出关键信息 # { # 3306/tcp: {} # } # [ # mysqld # ]看到3306/tcp被暴露说明这个镜像默认监听 3306Cmd是mysqld证明它不是一个“带 shell 的基础镜像”而是直接启动 MySQL 服务的精简版。这点很重要——有些第三方 MySQL 镜像会把CMD [bash]写死你docker run -d后容器秒退因为 bash 启动完就退出了。第三快速验证镜像是否真能启动不建容器# 用 --rm 和 --init 运行一次只检查 mysqld 是否能初始化成功 docker run --rm --init -e MYSQL_ALLOW_EMPTY_PASSWORD1 mysql:8.2.0 mysqld --verbose --help 2/dev/null | head -20 # 如果输出包含 mysqld Ver 8.2.0 和大量配置项说明镜像健康 # 如果卡住或报错 Cant open the mysql.plugin table说明镜像损坏立刻换源重拉这步看似多余但我在某次阿里云镜像加速器故障时救了大忙——本地拉的镜像校验失败但mysqld --help能过结果容器启动后日志疯狂刷Table mysql.plugin doesnt exist折腾两小时才发现是镜像层下载不完整。3. 容器启动与连接从“能连上”到“连得稳”的实战细节docker run -d mysql能让容器跑起来但离“可用”还差十公里。真正的挑战在于如何让应用代码、本地工具、其他容器都能在各种网络环境下低延迟、零中断、可审计地访问这个 MySQL 实例。3.1 最小可行启动命令解剖每一个参数的深意我们从最精简但生产可用的命令开始docker run -d \ --name my-mysql \ --restartunless-stopped \ -e MYSQL_ROOT_PASSWORDMyS3cur3Pssw0rd \ -e MYSQL_DATABASEapp_db \ -e MYSQL_USERapp_user \ -e MYSQL_PASSWORDAppUs3rPss \ -p 127.0.0.1:3307:3306 \ -v /opt/mysql-data:/var/lib/mysql \ -v /opt/mysql-conf:/etc/mysql/conf.d \ --memory2g --cpus2 \ --networkmy-app-network \ mysql:8.2.0现在逐个参数拆解告诉你为什么这么写而不是网上常见的“抄作业”式写法--restartunless-stopped这是容器存活的基石。always会在 Docker daemon 重启时强行拉起容器可能导致 MySQL 在宿主机资源未就绪时启动失败unless-stopped则尊重你的手动停止意图同时保证意外崩溃后自动恢复。我所有生产库都用它。-e MYSQL_DATABASEapp_db很多人忽略这个。它不是“创建数据库”而是在容器首次初始化时自动执行CREATE DATABASE app_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci。省去你进容器手动建库的步骤且确保字符集统一。如果没设MySQL 会用latin1作为默认后面存中文就是乱码。-p 127.0.0.1:3307:3306重点在127.0.0.1:。不加这个Docker 会绑定到0.0.0.0:3307意味着 MySQL 对宿主机所有网卡开放。在笔记本上无所谓但在服务器上等于把数据库端口暴露给内网所有机器。加了127.0.0.1就只允许本机连接其他容器则通过 Docker 网络互通这才是安全的分层设计。--memory2g --cpus2MySQL 不是无脑吃资源的程序。InnoDB 缓冲池大小innodb_buffer_pool_size默认是物理内存的 75%如果你不设内存限制它会把宿主机内存占满。设成2g它就知道最多用 1.5g 给缓冲池留 512M 给系统和其他进程。--cpus2同理防止它抢光 CPU。--networkmy-app-network这是跨容器通信的关键。默认bridge网络下容器只能用 IP 互访IP 还会变自定义网络docker network create my-app-network后容器名就是 DNS 名。你的 Python 应用只需写hostdb就能连上这个 MySQL不用记 IP。3.2 本地连接的三种姿势哪种最适合你连接方式不是越多越好而是要匹配你的使用场景姿势一从宿主机终端直连开发调试首选# 先确保 mysql-client 已安装Ubuntu/Debian sudo apt update sudo apt install mysql-client # 连接命令注意 host 是 127.0.0.1不是 localhost mysql -h 127.0.0.1 -P 3307 -u root -p # ⚠️ 关键区别localhost 会走 Unix socket127.0.0.1 才走 TCP # 而 Docker 映射的端口只响应 TCP用 localhost 会报错 Cant connect to local MySQL server姿势二从另一个容器内连微服务标准做法# 启动一个临时 Ubuntu 容器加入同一网络 docker run -it --rm --networkmy-app-network ubuntu:22.04 # 在容器内安装 client 并连接host 用容器名 apt update apt install -y mysql-client mysql -h my-mysql -P 3306 -u app_user -pAppUs3rPss app_db这里my-mysql就是前面--name my-mysql设的名称。Docker 内置 DNS 会自动解析成对应容器 IP。这是云原生架构的黄金法则——服务间通信用服务名不依赖 IP。姿势三从 GUI 工具连接DataGrip / DBeaver / Navicat配置要点Host:127.0.0.1Port:3307你映射的宿主机端口User:root或app_userPassword: 对应密码Database:app_db必须填否则连上去是空库列表SSL: 勾选 “Use SSL” 并选择 “Require”Docker MySQL 默认启用 SSL注意很多 GUI 工具默认不填 Database连上去后执行SHOW DATABASES;只能看到information_schema和mysql找不到你的app_db。这是因为 MySQL 协议要求客户端在连接时指定默认库否则服务端不返回用户库列表。填上app_db一切正常。3.3 连接超时与健康检查让系统自己发现问题光能连上不够还要知道它“活得好不好”。Docker 提供了原生健康检查机制docker run -d \ --name my-mysql \ --health-cmdmysqladmin ping -h 127.0.0.1 -u root -pMyS3cur3Pss || exit 1 \ --health-interval30s \ --health-timeout10s \ --health-retries3 \ --health-start-period40s \ # ... 其他参数同上--health-cmd每 30 秒执行一次mysqladmin ping。这个命令不查数据只问 MySQL 进程是否响应毫秒级完成。--health-start-period40s容器启动后等 40 秒再开始健康检查。因为 MySQL 初始化要时间前 30 秒日志全是Initializing database此时检查必失败。--health-retries3连续 3 次失败才标为 unhealthy。检查结果实时可见# 查看容器健康状态 docker ps --format table {{.Names}}\t{{.Status}} # 输出示例 # NAMES STATUS # my-mysql Up 2 minutes (healthy) # 如果 unhealthy查看详细日志 docker inspect my-mysql | jq .[0].State.Health这套机制和 Kubernetes 的 liveness probe 完全兼容是你做自动化运维的第一道防线。4. 配置文件深度定制从my.cnf到生产级调优MySQL 容器的默认配置/etc/mysql/my.cnf是为通用场景设计的就像汽车出厂设置——能开但开不快、不省油、不贴地。生产环境必须定制而定制的核心就是my.cnf文件。但直接进容器改不行。Docker 的哲学是“不可变基础设施”配置必须外部化、版本化、可审计。4.1 挂载配置文件的正确姿势为什么/etc/mysql/conf.d是唯一选择MySQL 官方镜像的启动脚本entrypoint.sh会按顺序加载配置/etc/mysql/my.cnf主配置不建议动/etc/mysql/conf.d/*.cnf推荐支持多个文件按字母序加载/etc/mysql/mysql.conf.d/*.cnf也支持但官方文档优先推conf.d所以我们必须挂载到/etc/mysql/conf.d。创建一个本地文件# 创建配置目录Linux/macOS sudo mkdir -p /opt/mysql-conf # 创建自定义配置文件 sudo tee /opt/mysql-conf/custom.cnf EOF [mysqld] # --- 性能调优 --- innodb_buffer_pool_size 1280M innodb_log_file_size 256M innodb_flush_method O_DIRECT skip-log-bin # --- 安全加固 --- bind-address 0.0.0.0 mysqlx-bind-address 0.0.0.0 default_authentication_plugin mysql_native_password validate_password.policy STRONG validate_password.length 12 # --- 字符集统一 --- character-set-server utf8mb4 collation-server utf8mb4_0900_ai_ci # --- 连接管理 --- max_connections 200 wait_timeout 28800 interactive_timeout 28800 EOF提示skip-log-bin是关键。MySQL 8.0 默认开启 binlog二进制日志用于主从复制。但单机开发/测试环境完全不需要它会持续写磁盘占用 IO 和空间。关掉它性能提升 5-10%日志量减少 90%。然后启动容器时挂载docker run -d \ --name my-mysql \ -v /opt/mysql-conf:/etc/mysql/conf.d \ # ... 其他参数 mysql:8.2.0启动后验证配置是否生效# 进入容器执行 SQL 查看变量 docker exec -it my-mysql mysql -uroot -pMyS3cur3Pss -e SHOW VARIABLES LIKE innodb_buffer_pool_size; # 输出应为 # ------------------------------------- # | Variable_name | Value | # ------------------------------------- # | innodb_buffer_pool_size | 1342177280 | ← 1280M 1342177280 bytes # -------------------------------------4.2 生产级核心参数详解每个数字背后的物理意义配置不是调数字游戏每个值都要有依据。以下是我在金融、电商、IoT 三类场景中验证过的黄金参数innodb_buffer_pool_size 1280M为什么是 1280M宿主机内存 2GMySQL 容器内存限制--memory2gInnoDB 缓冲池应占 60-75%。1280M 2G * 0.625留足 768M 给 OS、连接线程、临时表。物理意义这块内存缓存了最热的数据页和索引页。当Innodb_buffer_pool_read_requests / Innodb_buffer_pool_reads 999即 99.9% 的读请求命中内存说明调得合理。低于 990 就要加大。innodb_log_file_size 256M计算公式innodb_log_file_size ≈ (innodb_buffer_pool_size * 0.25) ~ (innodb_buffer_pool_size * 0.5)。1280M * 0.2 256M。为什么重要redo log 太小如默认 48M会导致频繁 checkpoint写放大严重太大如 1G崩溃恢复时间长。256M 是吞吐与恢复时间的平衡点。max_connections 200不是拍脑袋每个连接消耗约 256KB 内存线程栈 网络缓冲。200 * 256KB 50MB远小于预留内存。同时应用连接池如 HikariCP通常设maximumPoolSize20200 连接可支撑 10 个应用实例。wait_timeout 288008 小时避坑点很多人设成28800但 Java 应用连接池默认idleTimeout10分钟。如果 MySQL 先 kill 连接应用池里就会有“脏连接”下次用时报Connection reset。所以必须设成wait_timeout idleTimeout且应用端开启testOnBorrowtrue。4.3 动态配置 vs 静态配置什么该重启什么可在线改MySQL 支持两类配置动态变量DynamicSET GLOBAL var_name value;立即生效无需重启。如max_connections,wait_timeout。静态变量Static必须改my.cnf并重启容器。如innodb_buffer_pool_size,innodb_log_file_size。判断方法很简单-- 查看变量是否可动态修改 SHOW VARIABLES LIKE innodb_buffer_pool_size; SHOW VARIABLES LIKE max_connections; -- 查看变量状态Yes动态No静态 SELECT VARIABLE_NAME, IS_DYNAMIC FROM performance_schema.variables_info WHERE VARIABLE_NAME IN (innodb_buffer_pool_size, max_connections);生产建议把所有动态变量的初始值写在my.cnf里保证容器重启后状态一致日常调优优先用SET GLOBAL验证效果后再固化到配置文件静态变量调整必须走发布流程因为涉及容器重建和短暂服务中断。5. 数据持久化为什么docker volume是唯一可靠方案“容器里数据丢了”是 Docker 新手最高频的惨案。根本原因在于误解了容器的存储模型容器的可写层UpperDir是临时的docker rm时彻底删除。而 MySQL 的数据文件.ibd,.frm必须存在一个与容器生命周期解耦的地方。5.1 三种持久化方案对比为什么只有docker volume胜出方案命令示例优点缺点适用场景Bind Mount绑定挂载-v /host/path:/var/lib/mysql路径直观文件可直接编辑宿主机路径需提前创建权限问题多MySQL 进程 UID999宿主机目录需chown 999:999Windows/macOS 性能差本地开发快速验证Named Volume命名卷-v mysql-data:/var/lib/mysqlDocker 自动管理权限跨平台性能好支持备份/迁移路径不直观docker volume inspect查生产环境唯一推荐tmpfs内存卷--tmpfs /var/lib/mysql:rw,size1g极速读写数据纯内存容器停即失无法持久化临时测试、压力测试提示docker volume是 Docker daemon 管理的抽象存储它在 Linux 上实际是/var/lib/docker/volumes/xxx/_data但你永远不该直接操作这个路径。用docker volume ls和docker volume inspect管理。5.2 创建与管理命名卷的完整流程第一步创建卷一次# 创建名为 mysql-prod-data 的卷 docker volume create mysql-prod-data # 查看卷详情记录 Mountpoint备份时要用 docker volume inspect mysql-prod-data # 输出关键字段 # { # CreatedAt: 2024-05-20T10:23:45Z, # Driver: local, # Mountpoint: /var/lib/docker/volumes/mysql-prod-data/_data, # Name: mysql-prod-data, # ... # }第二步启动容器并挂载docker run -d \ --name mysql-prod \ -v mysql-prod-data:/var/lib/mysql \ -v /opt/mysql-conf:/etc/mysql/conf.d \ -e MYSQL_ROOT_PASSWORDProdR00tPss \ --restartunless-stopped \ --memory4g --cpus4 \ mysql:8.2.0第三步验证数据是否真持久化# 1. 进入容器创建测试库 docker exec -it mysql-prod mysql -uroot -pProdR00tPss -e CREATE DATABASE test_persist; # 2. 查看宿主机卷目录应该有 ibdata1, ib_logfile0 等文件 sudo ls -lh /var/lib/docker/volumes/mysql-prod-data/_data/ # 3. 删除并重建容器 docker stop mysql-prod docker rm mysql-prod # 4. 用同样卷启动新容器 docker run -d --name mysql-prod -v mysql-prod-data:/var/lib/mysql ... mysql:8.2.0 # 5. 进新容器验证库还在 docker exec -it mysql-prod mysql -uroot -pProdR00tPss -e SHOW DATABASES LIKE test_persist; # 输出test_persist → 成功5.3 备份与恢复用mysqldumpdocker exec实现零停机持久化只是第一步备份才是生命线。以下是我团队每天凌晨执行的备份脚本已脱敏#!/bin/bash # backup-mysql.sh VOLUME_NAMEmysql-prod-data BACKUP_DIR/backup/mysql DATE$(date %Y%m%d_%H%M%S) CONTAINER_NAMEmysql-prod # 创建备份目录 mkdir -p $BACKUP_DIR # 执行备份--single-transaction 保证一致性--routines 导出存储过程 docker exec $CONTAINER_NAME sh -c exec mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --all-databases --single-transaction --routines --triggers $BACKUP_DIR/full_backup_$DATE.sql # 压缩备份节省 70% 空间 gzip $BACKUP_DIR/full_backup_$DATE.sql # 保留最近 7 天备份 find $BACKUP_DIR -name full_backup_*.sql.gz -mtime 7 -delete echo Backup completed: $BACKUP_DIR/full_backup_$DATE.sql.gz恢复操作灾难恢复时# 1. 停止容器 docker stop mysql-prod # 2. 清空卷⚠️危险确保备份有效 docker volume rm mysql-prod-data docker volume create mysql-prod-data # 3. 启动一个临时容器导入备份 docker run -it --rm \ -v mysql-prod-data:/var/lib/mysql \ -v /backup/mysql:/backup \ -e MYSQL_ROOT_PASSWORDProdR00tPss \ mysql:8.2.0 sh -c mysql -uroot -p$MYSQL_ROOT_PASSWORD /backup/full_backup_20240520_020000.sql # 4. 启动正式容器 docker start mysql-prod注意mysqldump是逻辑备份适合中小数据量 100GB。TB 级数据请用Percona XtraBackup物理备份它支持增量和热备份但配置更复杂此处不展开。6. Docker Compose 编排告别单容器拥抱多服务协同单个 MySQL 容器只是起点。真实项目永远是“MySQL 应用 Redis Nginx”的组合。Docker Compose 就是管理这种组合的乐高积木。但很多人写docker-compose.yml只是把docker run命令翻译过来失去了编排的灵魂。6.1 生产就绪的docker-compose.yml模板以下是我为 SaaS 平台写的docker-compose.prod.yml已删减注释保留全部生产必需项version: 3.9 services: db: image: mysql:8.2.0 container_name: mysql-prod restart: unless-stopped environment: MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} MYSQL_DATABASE: saas_main MYSQL_USER: saas_app MYSQL_PASSWORD: ${DB_APP_PASSWORD} TZ: Asia/Shanghai command: --default-authentication-pluginmysql_native_password volumes: - mysql-prod-data:/var/lib/mysql - ./conf.d:/etc/mysql/conf.d:ro - ./init:/docker-entrypoint-initdb.d:ro ports: - 127.0.0.1:3307:3306 networks: - saas-net healthcheck: test: [CMD, mysqladmin, ping, -h, localhost, -u, root, -p$$DB_ROOT_PASSWORD] timeout: 20s retries: 10 start_period: 40s deploy: resources: limits: memory: 4G cpus: 4.0 reservations: memory: 2G cpus: 1.0 app: image: my-saas-app:1.2.0 depends_on: db: condition: service_healthy environment: DB_HOST: db DB_PORT: 3306 DB_NAME: saas_main DB_USER: saas_app DB_PASSWORD: ${DB_APP_PASSWORD} ports: - 8080:8080 networks: - saas-net deploy: replicas: 2 volumes: mysql-prod-data: networks: saas-net: driver: bridge ipam: config: - subnet: 172.20.0.0/166.2 关键配置解析每一行都是血泪教训environment中的${DB_ROOT_PASSWORD}密码不硬编码用.env文件管理# .env 文件gitignore 掉 DB_ROOT_PASSWORDMyS3cur3Pssw0rd DB_APP_PASSWORDAppUs3rPss这样密码和镜像版本分离不同环境dev/staging/prod只需换.env。command: --default-authentication-pluginmysql_native_password覆盖镜像默认的caching_sha2_password。因为大多数 Java/Python 驱动对新插件支持不完善加这一行应用零改造。volumes: ./init:/docker-entrypoint-initdb.d:ro挂载初始化 SQL。/docker-entrypoint-initdb.d/是 MySQL 镜像的魔法目录——容器首次启动时会自动执行此目录下所有.sql和.sh文件。放一个01-create-tables.sql就能自动建表。depends_on: condition: service_healthy不只是“db 启动了”而是“db 健康检查通过了”才启动 app。避免 app 因 MySQL 未就绪而启动失败。deploy.resources.limits/reservationsKubernetes 风格的资源约束。limits是硬上限reservations是调度时预留的最小资源防止容器饿死。6.3 日常运维命令从启动到排障# 启动整个栈后台运行 docker compose -f docker-compose.prod.yml up -d # 查看所有服务状态含健康状态 docker compose -f docker-compose.prod.yml ps # 查看 MySQL 日志实时 docker compose -f docker-compose.prod.yml logs -f db # 进入 MySQL 容器执行 SQL docker compose -f docker-compose.prod.yml exec db mysql -usaas_app -p$DB_APP_PASSWORD saas_main # 优雅停止先发 SIGTERM等 10 秒再 SIGKILL docker compose -f docker-compose.prod.yml down # 仅重启 MySQL不影响 app docker compose -f docker-compose.prod.yml restart db提示docker composev2比老版docker-composev1性能更好命令更统一。确保你用的是 Docker Desktop 4.15 或docker compose插件。7. 常见问题与排查技巧实录那些让你凌晨三点爬起来的真问题最后分享我在生产环境中高频遇到的 5 个“经典问题”附带真实日志、根因分析和一行解决命令。这些不是文档里抄来的是我在监控告警群里被 出来的真实战况。7.1 问题一容器启动后立即退出docker logs空白