大概一个月前,我需要在一台全新的开发机上快速搭一套 MySQL 5.7 环境。项目比较老,依赖的 ORM 框架对 8.0 的认证方式支持得不好,必须用 5.7。本机装 MySQL 虽然不难,但系统是刚装的,不想往里塞一堆依赖,而且以后这台机器可能还要跨项目复用,环境越干净越好。于是自然就想到用 Docker 跑一个 MySQL 5.7.44 的容器。
这个过程看着简单——docker pull mysql:5.7.44、docker run一行命令就完事——但实际上手会发现,数据持久化、配置文件覆盖、字符集、权限、时区这些点,随便哪个没处理好,后面都会踩坑。尤其是如果你准备把这套东西用在生产环境,或是在团队内多人共用同一台开发机,问题会更明显。
这篇文章我就按自己的实操顺序,把 Docker 下安装 MySQL 5.7.44 的完整过程、命令参数背后的逻辑、以及我实际踩过的坑和排查思路都整理出来。不管你是第一次用 Docker 跑数据库,还是已经在用但想搞清楚数据目录和配置映射的细节,都可以把它当一份能直接照着做的参考。
1. 为什么偏偏选 5.7.44:版本选型的现实考量
先说版本。MySQL 官方在 Docker Hub 上维护的mysql镜像,标签非常多,常见的包括5.5、5.6、5.7、8.0。其中5.7系列目前最新的 tag 就是5.7.44,这是 5.7 分支的最终维护版本,之后再不会有新的小版本发布。
1.1 5.7 和 8.0 的核心差异,以及为什么老项目离不开 5.7
很多人不理解,为什么 2025 年了还有人要装 5.7。最典型的原因有几个:
- 认证插件不同。MySQL 5.7 默认的认证插件是
mysql_native_password,而 8.0 默认是caching_sha2_password。很多老版本的语言驱动、ORM 框架、可视化工具(尤其是那种多年没更新的)只支持前者,装上 8.0 之后反复报Authentication plugin 'caching_sha2_password' cannot be loaded,逼得你必须回头改配置。 - SQL 行为差异。8.0 对某些语法做了更严格的校验,比如窗口函数、公用表表达式、隐式类型转换的规则都变了。老项目里一些“祖传 SQL”在 5.7 上跑得好好的,迁到 8.0 可能就报错或得到不同结果。
- 运维习惯沉淀。很多团队内部的备份脚本、慢查询分析工具、监控方案都是围绕 5.7 长期调优过的,短期内没办法为 8.0 重新做一轮验证。
这些原因决定了:存量系统升级数据库,从来不是一个顺手就能做的事。选 5.7.44,相当于选了一个功能迭代、性能修复都冻结的稳定版。它的好处就是可预期——该踩的坑大家基本都踩过,资料最多,踩到坑之后能搜到的解决方案也最全。
1.2 用 Docker 跑 MySQL,而不是直接在宿主机安装
再说为什么用 Docker。直接apt install mysql-server或者wget一个 rpm 包装上去,数据库本身没问题,但会有几个隐藏成本:
| 对比项 | Docker 容器 | 宿主机直接安装 |
|---|---|---|
| 环境隔离 | 容器内自带依赖,不污染宿主机 | 依赖 GNU libc、Perl 脚本等,卸载不干净 |
| 多版本共存 | 同一台机器可以同时跑 5.7 和 8.0,端口分开即可 | 基本上只能装一个,切换成本高 |
| 数据目录迁移 | 数据放到挂载目录,换机器直接打包目录+配置文件 | 需要手动迁移 datadir,且容易遗漏配置文件 |
| 团队复制 | docker-compose up -d一键拉起 | 每台新机器都要手动执行安装脚本 |
| 资源限制 | 可以用--cpus、--memory精确控制 | 依赖宿主机 pid 管理和 cgroup,默认难做限制 |
简单说,Docker 把“安装 MySQL”这件事变成了一次性、可复现的动作。同一个容器镜像,在任何一台有 Docker 的机器上跑起来,得到的数据库环境和配置完全一致。对于团队协作来说,别人给你的不是一段安装文档,而是一个docker-compose.yml文件,这之间的效率差距是很明显的。
1.3 “-pulllatest 的坑”或“为什么不直接 docker pull mysql”
我见过不少人图省事,直接docker pull mysql。这样拉下来的是 latest 标签,在现在这个时间点指向的就是 8.x 最新版。如果项目明确要 5.7,那这就是灾难的开始——镜像版本不对,后面所有排查都在浪费时间。
一定要拉指定版本:docker pull mysql:5.7.44。这个 tag 是真实的官方镜像,已经过完整测试。5.7 系列的小版本号之间也有差异,比如 5.7.42 包含了一些 InnoDB 修复,5.7.44 是最终合集。
2. 从拉镜像到跑通:完整安装流程实录
这里我直接给出完整命令,然后逐个参数解释为什么这么写。下面的命令在我这边的环境(Linux + Docker 24.x)是直接跑通的。如果你是 Windows 的 Docker Desktop,路径风格略有不同,但思路完全一致。
2.1 基础环境准备
先把镜像拉下来:
docker pull mysql:5.7.44拉镜像这一步很关键,别跳过。有些人直接docker run也会自动拉,但那样一旦网络不好,拉了一半失败,后面反复重试很浪费时间。先显式拉一次,能保证镜像完整。
然后创建两个宿主机目录,一个放数据文件,一个放配置文件:
mkdir -p /data/mysql/data mkdir -p /data/mysql/conf这个目录放哪、叫什么名字都可以,但一定不要在容器内部。如果不做目录映射,容器一旦被删除,数据库数据跟着就没了,这是很多人第一次用 Docker 跑数据库最惨痛的教训。
2.2 启动第一个容器(核心命令逐参数讲解)
docker run -d \ --name mysql-5.7 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD='MyRoot@Pass123' \ -e MYSQL_DATABASE='demo_db' \ -e TZ='Asia/Shanghai' \ -v /data/mysql/data:/var/lib/mysql \ -v /data/mysql/conf:/etc/mysql/conf.d \ --restart=always \ mysql:5.7.44一行一行拆开看:
-d:后台运行。不加这个,容器会占用你当前的终端。--name mysql-5.7:给容器起个名字。以后docker exec -it mysql-5.7 bash或者docker logs mysql-5.7都用这个名字来引用,比记一长串容器 ID 方便得多。-p 3306:3306:把宿主机的 3306 端口映射到容器的 3306 端口。左边是宿主机端口,右边是容器端口。如果你本机已经有别的 MySQL 占了 3306,可以改成-p 3307:3306,这样宿主机上用 3307 端口访问。-e MYSQL_ROOT_PASSWORD:设置 root 用户的初始密码。这是官方镜像指定的必需环境变量之一,不设置它会报错并自动退出。-e MYSQL_DATABASE:镜像在首次初始化时会自动创建一个数据库。不是必须的,但对大多数业务来说挺方便,省得启动后还要再连接进去CREATE DATABASE。-e TZ='Asia/Shanghai':设置容器时区。不加这个,容器默认是 UTC 时间,你会发现NOW()返回的时间和本地时间差 8 个小时。对开发调试来说这可能没影响,但也可能引发莫名其妙的日志时间错位问题。-v /data/mysql/data:/var/lib/mysql:把宿主机目录映射到容器内 MySQL 的数据目录。这是数据持久化的关键。容器删了、重建了,只要这个目录里的文件在,数据就在。-v /data/mysql/conf:/etc/mysql/conf.d:把宿主机目录映射到容器内的配置目录。这个目录里放.cnf文件,MySQL 启动时会被自动加载。后面我们改字符集、改性能参数就靠它。--restart=always:Docker 守护进程启动时自动拉起这个容器。机器重启后数据库不用手动启动,对生产环境很友好。
2.3 启动日志检查与连接验证
启动之后,不要急着连数据库,先看一眼日志:
docker logs mysql-5.7正常情况下,日志末尾会出现[Note] mysqld: ready for connections.和[Note] ... Version: '5.7.44' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)。看到这两行,才能确认 MySQL 真正起来了。
然后等几秒,试着连接:
docker exec -it mysql-5.7 mysql -uroot -p输入刚才设置的 root 密码,能进入mysql>提示符就说明安装流程已经通了。再执行一条简单的查询确认版本:
SELECT VERSION();输出应该就是5.7.44。
2.4 一个容易被忽略的细节:容器初始化的时机
官方镜像在容器首次启动时,会执行初始化过程:初始化数据目录、设置 root 密码、创建MYSQL_DATABASE、执行/docker-entrypoint-initdb.d/目录下的初始化 SQL。这个过程只发生一次。
也就是说,如果你在数据目录已经存在的情况下改动了MYSQL_ROOT_PASSWORD,再次启动容器,密码是不会变的。密码修改只对首次启动生效。以后想改密码,要么用ALTER USER语句,要么清空数据目录重新初始化。很多人对这一点没概念,改环境变量重启容器后连不上,就开始怀疑人生。
3. 持久化、配置注入与初始化脚本:让容器不再是随时丢数据的玩具
跑通容器只是第一步。把数据库真正用起来,还需要把持久化、自定义配置、初始化脚本这几件事搞清楚。这节是本篇最值得细看的部分——很多网上教程只教你docker run一行命令,却没说数据存哪里、配置怎么覆盖。
3.1 数据目录的持久化边界:哪些文件算“数据”
我把/data/mysql/data映射到了/var/lib/mysql。在 MySQL 官方镜像里,这个目录包含所有数据库的数据文件、ibdata1、ib_logfile0/1、undo日志等。简而言之,业务数据全部都在这里面。
有一点要明确:/data/mysql/data这个目录的权限和属主有讲究。MySQL 容器内部的 mysql 用户 UID 是 999,如果宿主机目录权限不对,容器启动时可能因为无法写入而报错。我遇到过一次比较大的坑,会在后面“排查”章节详细说,这里先记住一个原则:如果启动失败且日志提示无法写入数据目录,最简单的处理是执行chown -R 999:999 /data/mysql/data。
3.2 注入自定义 my.cnf 的正确姿势
官方镜像默认的配置文件在/etc/mysql/my.cnf,它内部会引入/etc/mysql/conf.d/这个目录下所有.cnf文件。所以我映射了/data/mysql/conf到/etc/mysql/conf.d,在里面放自定义配置,就能实现“不修改镜像内部文件,只追加配置”。
比如我在/data/mysql/conf/my.cnf里写入:
[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci default-time-zone='+08:00' max_connections=500 skip-name-resolve lower_case_table_names=1几个关键配置的解释:
character-set-server=utf8mb4:MySQL 5.7 默认字符集是latin1,如果你不设置,建表时不指定字符集,建出来的表默认latin1,中文数据存进去就是乱码。utf8mb4才是完整的 UTF-8 支持,能存 emoji 和生僻字。collation-server=utf8mb4_unicode_ci:排序规则。utf8mb4_unicode_ci对多语言排序更准确,utf8mb4_general_ci性能略好但排序精度差一点。业务上选unicode_ci更稳妥。max_connections=500:默认值只有 151,连接池稍大一点就容易打满。开发环境通常没事,万一周边服务多了,链接耗尽是常见故障。skip-name-resolve:跳过反向 DNS 解析。不加这个,客户端连接时 MySQL 会尝试解析客户端主机名,网络环境复杂时解析会超时,表现为“连接很久然后失败”。加上了,权限表里的 host 就只能用 IP 匹配,不能再用主机名。lower_case_table_names=1:表名不区分大小写。Windows 开发环境默认就是 1,而 Linux 默认是 0,同一个项目跨平台开发时如果表名大小写风格不统一,这配置能替你挡掉大部分问题。但注意,这个参数只在初始化数据目录时生效,如果数据目录已经初始化,修改无效。
还有一点非常关键:/etc/mysql/conf.d目录下的配置会被全局加载,但如果你在宿主机挂载了/data/mysql/conf,这个目录本身不能是空的,否则容器启动时 MySQL 可能因为读取目录出错而异常。我之前就遇到过空目录挂载导致的启动失败。稳妥的做法是:第一次启动前,先把配置文件放进去,再运行docker run。
3.3 初始化脚本:首次启动时自动建库、建用户、导入数据
官方镜像还支持一个特殊的目录:/docker-entrypoint-initdb.d/。放在这个目录下的.sh、.sql、.sql.gz文件,会在数据目录初始化完成之后、MySQL 启动完成之前自动按文件名排序执行。
这个功能太实用了。比如我下面这个脚本init.sql:
CREATE USER 'app_user'@'%' IDENTIFIED BY 'App@Pass123'; GRANT ALL PRIVILEGES ON demo_db.* TO 'app_user'@'%'; FLUSH PRIVILEGES;我把它放到宿主机的/data/mysql/initdb/目录,然后挂载:
-v /data/mysql/initdb:/docker-entrypoint-initdb.d这样每次新建一个全新的数据目录环境时,root 密码、业务库、业务账号、初始化数据,一条命令全部自动完成。对于团队拉新环境,或者我本地开一个新的项目,这个流程能把投入时间从半小时压缩到两分钟。
3.4 升级使用 docker-compose
如果只是本机临时用,docker run完全够。但如果这台机器要长期承担某个项目的数据库,或者团队需要共享配置,我建议直接上docker-compose。写一个docker-compose.yml:
version: '3.8' services: mysql: image: mysql:5.7.44 container_name: mysql-5.7 restart: always ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: "MyRoot@Pass123" MYSQL_DATABASE: "demo_db" TZ: "Asia/Shanghai" volumes: - /data/mysql/data:/var/lib/mysql - /data/mysql/conf:/etc/mysql/conf.d - /data/mysql/initdb:/docker-entrypoint-initdb.d然后一句docker-compose up -d就能拉起整套环境。以后不管换机器还是换同事,把这一个文件给他就行。版本、账号、端口、持久化目录,一目了然。
4. 实测中最容易踩的坑:完整排查链路
下面这些是我在实际使用中,包括帮同事排查过程中遇到过的真实问题。每个问题我都会把当时的“错误现场”和排查思路写出来,而不是只给最终答案,这样你再遇到类似情况时,能更快定位。
4.1 第一次启动就失败:端口占用还是目录权限?
现象:执行docker run之后,容器状态一直显示Exited (1)。
排查过程:我先看日志:
docker logs mysql-5.7如果是端口被占用,日志会显示:
[ERROR] Can't start server: Bind on TCP/IP port: Address already in use这个很直接,检查宿主机端口占用:
netstat -tlnp | grep 3306如果找到占用进程,要么杀掉,要么改映射端口。
但更隐蔽的是数据目录权限问题。有一次日志显示的是:
[ERROR] InnoDB: Operating system error number 13 in a file operation. [ERROR] InnoDB: The error means mysqld does not have the access rights to the directory.这类错误几乎可以断定是宿主机目录权限的问题。Docker 容器里 mysqld 运行在 UID 999 下面,宿主机上创建目录用的 UID 往往是 1000 或 root,映射进去之后,那个容器里的 mysql 用户对数据目录没有写权限。此时执行:
chown -R 999:999 /data/mysql/data然后重新启动容器即可。
根因理解:Docker 的卷挂载把宿主机目录原封不动地暴露给容器进程,宿主机上文件系统的权限规则也一并生效。Docker 不会帮你做任何权限转化。所以,数据目录的属主必须匹配容器内 mysqld 运行用户的 UID。官方 MySQL 镜像里 mysql 用户的 UID 是 999,这个值不是随便定的,是官方固定的。
4.2 容器起来了但连接被拒绝:网络与监听地址问题
现象:容器状态正常,日志显示 MySQL ready for connections,但用 Navicat 或应用程序连接时报:
Can't connect to MySQL server on '127.0.0.1' (10061)排查过程:先确认 MySQL 是否真的在监听。进容器里看:
docker exec -it mysql-5.7 netstat -tlnp | grep 3306如果看到监听地址是127.0.0.1:3306或者:::1(IPv6 的 localhost),却用不了局域网 IP 访问,那就是绑定地址的问题。MySQL 默认配置里的bind-address通常是*或者没有,但如果你在自定义配置里不小心设成了127.0.0.1,它就只监听本机回环地址。
解决方法是把自定义配置里的bind-address=127.0.0.1改成0.0.0.0,或干脆删掉这一行。
还有另一种常见的连接问题:宿主机防御墙拦截了 3306 端口。这时候把云服务器的安全组开一下 3306 端口就行。不过从安全角度,我不建议直接把端口直接开在公网,尤其 MySQL 的账号如果还是默认 root+弱密码,就等于裸奔。
4.3 中文乱码的根源:字符集没设置或设置晚了
现象:建了表,插入中文,查出来全是???,或者客户端显示乱码。
排查过程:乱码问题牵扯到三层字符集:客户端、连接层、服务端。先查数据库侧当前状态:
SHOW VARIABLES LIKE 'character_set%';如果输出显示character_set_server是latin1,那就是服务端默认字符集的问题。解决方法就是前面说的,在自定义配置里写character-set-server=utf8mb4,并重启容器。
这时候必须注意:如果 MySQL 的数据目录已经初始化过了,这个配置在 5.7 中不会自动把已有库表的字符集改掉,它只影响后续新建的库表。因此最佳实践是:第一次启动容器之前就把字符集配置放到位。如果你已经启动了容器、建了库,那还需要额外执行ALTER DATABASE demo_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;,并且对已有的表做转换:
ALTER TABLE table_name CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;而且,有时候即使库和表都是 utf8mb4,客户端仍乱码,问题出在连接参数上。比如 JDBC 连接串里没加characterEncoding=utf8。这种情况和 MySQL 无关,是驱动在设置连接字符集时没有正确协商。
经验总结:字符集必须在初始化之前就确定,是持久化环境里的原子属性,事后迁移成本别看它只是几条 SQL,如果表数量上百,转换时间会很长,而且中途可能有锁表风险。
4.4 容器重启后数据不在了或者密码不生效
现象:用docker rm删除容器,再重新docker run同一个镜像,发现数据库是空的,root 密码也不是新设置的。
排查链路:如果在启动时没有挂载数据目录,那么数据在容器内部/var/lib/mysql,容器删除后数据就没了。解决方式就是我在第 2 节里给的-v映射。而密码不生效,则是因为同名数据目录已经初始化过一次,镜像的初始化逻辑不会重复执行,它只在数据目录为空时运行。
正确理解:MySQL 官方镜像是通过检测/var/lib/mysql是否为空来判断是否需要初始化的。非空就跳过初始化步骤,直接正常启动。
所以如果你换了密码想要生效,两个做法:
- 修改数据目录里的用户密码(用
ALTER USER或SET PASSWORD)。 - 让数据目录回到空状态,再启动容器重新初始化。
后者的前提是你确定旧数据不要了。这个判断要特别谨慎,分清“容器生命周期”和“数据生命周期”。容器没了可以随时重建,数据没了就只能找备份了。
4.5 合理利用 docker logs 定位问题
很多容器问题,第一视角就是日志。我在排查问题时的顺序都是:
docker ps -a看容器状态:是Up中还是Exited。docker logs mysql-5.7 --tail 100看最近 100 行日志。- 从日志里找
[ERROR]开头的行。 - 拿到错误信息之后,针对性地搜索,或者去查 MySQL 官方手册。
- 需要进入容器内部查实际配置时,
docker exec -it mysql-5.7 bash,进去看/etc/mysql/下的文件内容,确认自己挂载的配置是否真的被加载了。
这里有个经验:如果发现自定义配置完全没生效,先检查挂载目录是否对。比如我一度把/data/mysql/conf挂到了/etc/mysql/conf.d,但我以为挂到/etc/my.cnf更直接,结果因为覆盖了整个文件导致 MySQL 起不来。最后我查了官方镜像的 Dockerfile,确认了它的配置文件加载逻辑,才明白/etc/mysql/conf.d才是正确归档点。所以遇到配置不生效,先别急着改配置内容,先确认“挂载路径对不对”。
5. 生产环境进一步优化:资源限制、安全配置与备份恢复
容器跑起来了,数据也不会丢了,但如果这台机器要承担真实业务,还有几个维度值得花时间处理。
5.1 避免“宿主机资源被吃干净”:CPU 與内存限制
MySQL 默认不会主动限制自己的资源占用,有什么资源就用什么资源。某些场景下,InnoDB buffer pool 默认只有 128M,这不会占满内存。但并发高了之后,连接数、临时表、排序操作都会吃内存。而且如果宿主机上还跑着其他容器,没有限制的话,所有容器会互相争抢 CPU 和内存。
我通常在docker run时加上资源限制:
--cpus=2 \ --memory=2g \意思是容器最多使用 2 个 CPU 核心和 2GB 内存。docker-compose 里对应写法是:
deploy: resources: limits: cpus: '2' memory: 2g当然这个值要根据实际业务负载来定。如果是小型业务、个人项目,2G 内存一般够用。如果你是做数据分析、大规模读写并发,就得按压力测试的结果去调。
5.2 限制网络暴露面:别为了图省事把端口开到 0.0.0.0
我在第 2 节里写的命令是-p 3306:3306,这会默认绑定宿主机所有网络接口,包括公网接口。如果这台机器有公网 IP,MySQL 就相当于暴露在公网上。安全上这是大忌。
更稳的做法是绑定到本机回环地址,只允许本机访问:
-p 127.0.0.1:3306:3306这样宿主机上其他应用可以连 MySQL,外网完全访问不到。如果你的应用程序不在同一个宿主机上,而是分布在多台服务器上,那更好的选择是使用 Docker 内部网络。比如创建一个自定义网络,让 MySQL 容器和你的应用容器在同一个内网通信,不让 MySQL 直接暴露端口:
docker network create app_net docker run -d --network app_net --name mysql-5.7 ... mysql:5.7.44这样只有加入app_net网络的容器才能访问 MySQL,宿主机本身都无法直接连接。适合前后端容器化的部署场景。
5.3 初始化专用账号,别用 root 干所有事
很多开发者在本地直接用 root 连接,图省事。生产环境千万不能这样。root 账号权限太大,一旦密码泄露,数据库整个就没了。而且 root 默认的 host 是localhost,如果你用 root 从远程连接,MySQL 5.7 会直接拒绝(除非你显式创建root@'%',但那更危险)。
我建议的做法是,在业务初始化时创建专用账号,并只授予业务库的权限:
CREATE USER 'app_user'@'%' IDENTIFIED BY 'Strong@Pass123'; GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX ON demo_db.* TO 'app_user'@'%'; FLUSH PRIVILEGES;注意这里的'%'表示允许从任何主机连接。如果只允许某几台应用服务器连接,更严格一点应该是'app_user'@'10.0.0.5'这种写法。
5.4 时区和日志:容器里默认的 UTC 会让你对不上日志
标题已经提到TZ=Asia/Shanghai这个环境变量。这里我再说一个更细的坑:容器日志和 MySQL 内部时间戳是两套东西。
- 系统层面:
TZ环境变量影响的是操作系统层面date显示的时间。 - MySQL 层面:时间戳取决于
system_time_zone和time_zone。如果你只在配置里写了default-time-zone='+08:00',NOW()会正确返回本地时间,但 MySQL 的错误日志和 binlog 里记录的时间可能还是 UTC。
更完整的配置是:
docker run ... \ -e TZ='Asia/Shanghai' \ mysql:5.7.44同时在自定义配置文件里加一行:
[mysqld] default-time-zone='+08:00'这样从 SQL 查询、错误日志、连接会话三个层面都能看到一致的时间。
5.5 容器内做备份恢复:mysqldump 的 Docker 用法
备份这件事,等出事了再想起就晚了。我在本地开发时可能备份不频繁,但只要涉及生产环境,备份脚本必须提前准备好。
使用 Docker 后备份的核心理念是:备份工具在容器内运行,备份文件留在宿主机。例如:
docker exec mysql-5.7 sh -c 'exec mysqldump -uroot -p"$MYSQL_ROOT_PASSWORD" --single-transaction --routines --triggers --databases demo_db' > /backup/demo_db_$(date +%Y%m%d).sql注意--single-transaction这个参数非常重要。它通过 InnoDB 的事务特性,在导出时获得一致性快照,不会锁表。这在线上备份时是默认选项。如果没有它,你会看到备份期间所有写操作都被阻塞。
恢复也很简单:
cat /backup/demo_db_20250101.sql | docker exec -i mysql-5.7 mysql -uroot -p"$MYSQL_ROOT_PASSWORD"注意这里是-i而不是-it,因为通过管道输入数据时不应该分配 TTY,否则可能报the input device is not a TTY。
5.6 别忘了设置max_connections和连接池参数
前面已经提过max_connections。我再给一个适合中小规模业务的参考配置组合:
[mysqld] max_connections = 500 max_connect_errors = 1000 innodb_buffer_pool_size = 256M innodb_log_file_size = 64M wait_timeout = 60 interactive_timeout = 28800参数含义简要说明:
innodb_buffer_pool_size:InnoDB 的缓存池大小。不是越大越好,一般设置为物理内存的 50%~70%。但如果容器只给了 2G 内存,你给 1.5G 的 buffer pool 就容易 OOM,需要和容器资源限制一起规划。innodb_log_file_size:redo log 大小,影响写入性能和崩溃恢复时间。5.7 默认是 48M,可以适当调大。wait_timeout:连接最大空闲时间。设置太大会导致大量空闲连接占用资源,设置太小又会让短连接频繁超时。60 秒对这个参数来说是一个常见选择,但如果你的业务是长连接模式,可能需要更大。
这些参数调校不是一篇文章能讲完的事,但对中小项目来说,上面的配置可以作为初始值。
收尾:这套环境我用了半年的几点体会
最后再分享几点这次从 Docker 装 MySQL 5.7.44 到现在,半年来反复被验证有效的体会。
第一,容器化数据库最大的收益并不是“一条命令装完”,而是“环境可复制”。我后来在另一台服务器上搭一模一样的库,直接把docker-compose.yml和数据目录打包过去,解压启动,IPv6 问题、字符集问题、端口冲突问题,全都提前规避了。
第二,遇到问题先看日志,而不是先怀疑镜像有问题。MySQL 官方镜像非常成熟,出错的概率远低于挂载配置、目录权限、网络映射出错的概率。熟练使用docker logs、docker inspect和docker exec三个命令,能解决 90% 的容器问题。
第三,不要在最开始想着“一次性配完美”。先按默认配置跑通,再逐步加自定义配置。每次改动一个配置项,重启容器,用一条简单 SQL 验证效果,比一口气写一大堆配置最后全不生效要高效得多。比如字符集,跑通后再加,You 可以得到 real-time 反馈,也知道差异在哪儿。
如果这篇文章对你的部署有实质帮助,或者你在实际操作中遇到了我没覆盖到的问题,欢迎在评论区留下你的情况,我们一起做个参考。