☰
Docker部署MySQL全攻略:从安装配置到容器内执行SQL
2026/10/10 19:47:38 网站建设 项目流程

1. 环境准备与整体思路

先说结论:如果你还在本地直接装MySQL,真的要试试Docker这条路。我最早也是老老实实下载MySQL安装包,一路Next,结果因为版本不对、环境变量没配、端口被占、卸载不干净这些问题折腾了好几天。后来切到Docker,整个过程压缩到十分钟以内,而且换版本、迁移、删掉重来都特别干净,宿主机上一点垃圾都不留。

这篇内容围绕的就是我在纯新手阶段摸索出来的完整路径:Docker下MySQL的安装、启动、配置、进入容器执行SQL。适合完全没接触过Docker的初学者,也适合已经在用Docker但每次都被MySQL容器细节卡住的朋友。只要你按着步骤走,基本能做到一遍跑通。

1.1 为什么推荐用Docker跑MySQL

一句话解释Docker:它把MySQL和它所依赖的运行环境一起打包成一个“容器”,容器里的东西跟你的Windows或Linux系统是隔离的,但通过端口可以对外提供服务。你可以把容器想象成一套精装修的小房间,里面什么都配好了,你只需要把门打开一个口子让外面的人能进来访问就行。

用Docker跑MySQL有四个很实际的收益:

  • 安装快,一条命令拉镜像,不用跑安装向导。
  • 环境隔离,不会污染宿主机的依赖,卸载时直接删容器删镜像,干净利落。
  • 版本随意切换,同一台机器上同时跑MySQL 5.7和8.0都没有问题,容器之间互不干扰。
  • 迁移方便,数据目录用数据卷挂载出来后,整个数据跟着走,备份还原很直观。

当然也有代价:Docker本身需要额外安装,内存占用会比裸装MySQL略高,而且初学阶段需要理解几个新概念,比如镜像、容器、数据卷、端口映射。但这一点点学习成本,换来的是一劳永逸的开发环境管理体验,绝对值回票价。

1.2 安装Docker Desktop:Windows和macOS的通用做法

如果你用的是Windows 10/11或macOS,最强的选择就是装Docker Desktop。这是Docker官方出的图形化客户端,自带Docker Engine,装好后可以在界面上直接管理容器、镜像、数据卷,对新手特别友好。

去Docker官网下载对应版本的Docker Desktop安装包,双击安装。Windows下需要注意几点:

  • 务必开启Windows的WSL 2功能,具体是在PowerShell里执行wsl --install,装完重启电脑。Docker Desktop现在默认依赖WSL 2来跑Linux容器,这一步不做后面启动会报错。
  • 安装过程中如果提示需要Hyper-V,按提示启用后重启。部分老版本Docker Desktop依赖Hyper-V,新一代版本主要依赖WSL 2。
  • 安装完成后打开Docker Desktop,第一次启动会花一两分钟初始化,看到左下角出现“Engine running”的绿色状态,就说明Docker核心已经跑起来了。

macOS用户更简单,把安装包拖进Applications就行,Docker Desktop会自动处理好虚拟化相关的问题。

1.3 Linux环境下直接装Docker Engine

如果你用的是Ubuntu/CentOS这类Linux服务器,不需要装Docker Desktop,直接安装Docker Engine。这里以Ubuntu为例,一条条命令来:

sudo apt update sudo apt install docker.io -y sudo systemctl enable docker sudo systemctl start docker

CentOS则是:

sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce docker-ce-cli containerd.io -y sudo systemctl start docker sudo systemctl enable docker

启动后验证一下Docker是否正常,运行下面这条命令,能打印出版本信息就说明装好了:

docker version
Client: Docker Engine - Community Version: 27.x.x ... Server: Docker Engine - Community Engine: Version: 27.x.x

这里有个关键点:如果docker version只显示了Client部分,Server部分报错或卡住,大概率是Docker服务没起来,回看上一节的systemctl start docker是否执行成功。

2. 拉取镜像与容器创建

安装完Docker只是万里长征第一步,接下来要让MySQL跑起来。很多新手在这里容易踩坑,核心是没搞清楚镜像和容器的区别。镜像可以理解成MySQL的“安装包模板”,而容器是模板跑起来后的“实例”。你拉取镜像一次,之后可以基于它创建无数个容器,每个容器都是独立的MySQL实例。

2.1 MySQL版本怎么选:8.0还是5.7

这个选择直接影响你后续的配置方式和兼容性。我的建议是:新项目直接上MySQL 8.0,老项目需要兼容老代码再用5.7。

MySQL 8.0默认的认证插件是caching_sha2_password,比5.7的mysql_native_password安全性更高,但老版本的客户端和部分老JDBC驱动连接时会报认证错误。如果你用Navicat旧版本或者老项目里的驱动连接8.0,需要额外改认证方式,这个我在后面常见问题里会详细说。

拉取镜像的命令很简单:

docker pull mysql:8.0

如果想用5.7,就执行:

docker pull mysql:5.7

执行docker images可以查看本地已有的镜像,确认拉取成功。

2.2 启动第一个MySQL容器

拉好镜像后,用docker run命令启动容器。这里直接给出一个生产环境可用的最小命令,附带每个参数的解释:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root123456 \ -e TZ=Asia/Shanghai \ -v mysql_data:/var/lib/mysql \ mysql:8.0

逐个拆解:

  • -d:后台运行容器,终端不会卡住。
  • --name mysql8:给容器起个名字,后续操作全靠这个名字定位。
  • -p 3306:3306:端口映射。宿主机3306端口接收到的请求,会转发到容器内的3306端口。这样本地Navicat连接时填localhost:3306就能访问容器里的MySQL。
  • -e MYSQL_ROOT_PASSWORD=root123456:设置MySQL root用户的初始密码。这是官方镜像提供的最直接的设置方式,第一次初始化数据库时会自动生效。
  • -e TZ=Asia/Shanghai:把容器时区设置成东八区,否则数据库里的NOW()返回的是UTC时间,跟北京时间差8小时。
  • -v mysql_data:/var/lib/mysql:数据卷挂载。这是整个命令里最重要的一项。MySQL的数据文件默认存在容器内部的/var/lib/mysql目录,容器一旦删除,数据就没了。挂载到宿主机的一个名为mysql_data的Docker数据卷后,即使容器删了重建,数据依然还在。

如果是在开发机上想快速验证,不想挂载数据卷也可以,但我的建议是从一开始就养成挂载的好习惯,后面做数据备份、迁移都方便。

2.3 验证容器启动状态

执行下面的命令查看容器运行状态:

docker ps

如果看到类似下面这行,说明容器已经正常运行:

CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 mysql:8.0 "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 0.0.0.0:3306->3306/tcp, 33060/tcp mysql8

如果docker ps里没有这个容器,说明启动失败了。这时候先用docker ps -a看所有容器,然后查看日志定位原因:

docker logs mysql8

常见的失败原因我这里先提前说一下,后面还会展开:3306端口被宿主机上已有的MySQL占用、磁盘空间不足、内存不够。尤其是端口占用,本地如果已经装过MySQL,docker run会立刻报端口冲突。

注意:本地已有MySQL服务时,要么把本地MySQL停掉,要么把端口映射改成3307:3306,两种方式选一个就行。

3. 核心配置:容器外的关键操作

容器启动只是第一步,真正好不好用,还得看配置。这一节的内容重点在于:MySQL在容器里,但配置的主战场在宿主机上。

3.1 MySQL的配置文件怎么挂载进去

官方镜像默认的配置是够用的,但实际开发中经常要调整字符集、排序规则、最大连接数等参数。修改容器内配置有三种思路:

第一种:进入容器直接改配置文件。先执行docker exec -it mysql8 bash进入容器,然后编辑/etc/my.cnf。缺点是容器一旦重建,配置就丢了,而且容器里默认没有vi/vim,还得先安装,麻烦。

第二种:启动时通过命令参数指定。比如加--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci。这种方式适合临时调整,命令会变得很长,不便于管理。

第三种(推荐):把宿主机上的配置文件挂载进容器。先在宿主机建一个目录,比如~/docker/mysql/conf,在里面新建my.cnf,然后启动容器时加参数-v ~/docker/mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf。这样以后要改配置,直接改宿主机文件,重启容器就生效。

以我最常用的开发配置为例,一段实用的my.cnf:

[mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_unicode_ci max_connections=500 default-time-zone='+08:00'

配置里的三块重点说一下:

  • character-set-server=utf8mb4:让数据库默认使用utf8mb4字符集。utf8mb4是utf8的超集,能完整存储emoji和四字节生僻字。我见过太多项目因为默认utf8而存表情符号失败,所以创建表的时候务必确认字符集。
  • collation-server=utf8mb4_unicode_ci:排序规则,影响查询时的排序和比较行为。unicode_ci精度较高,实际项目中用得最多。
  • default-time-zone='+08:00':跟前面-e TZ=Asia/Shanghai的效果类似,双重保险,确保SQL里的NOW()返回北京时间。

配置挂载后,重启容器让配置生效:

docker restart mysql8

3.2 首次连接与修改root密码

容器初始化完成后,用之前设置的MYSQL_ROOT_PASSWORD就能登录。但出于安全习惯,第一次启动后建议把密码换掉。进入容器执行SQL来修改:

docker exec -it mysql8 mysql -uroot -p

输入密码后进入MySQL命令行,然后执行:

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '新密码'; FLUSH PRIVILEGES;

这里有两个细节:

  • 'root'@'%'表示任意主机都能连接。官方镜像默认只允许root从localhost访问,如果你后面想用Navicat从宿主机连接,就必须允许%。
  • IDENTIFIED WITH mysql_native_password是为了兼容老客户端。MySQL 8.0默认认证插件是caching_sha2_password,老版本Navicat和旧驱动连接时会提示Authentication plugin 'caching_sha2_password' cannot be loaded,改成mysql_native_password是最快的解法。

如果你是5.7镜像,同样的语句也适用,只是5.7默认就是mysql_native_password,不需要刻意指定。

3.3 创建业务库和专用账号

永远不要用root账号跑业务。这是我在项目里踩过坑之后养成的习惯。之前图省事,Java项目里直接配root连接,结果有一次测试环境被人扫到弱口令,数据库被删了一堆表,教训深刻。

正确的做法是创建一个业务专用账号,只给它业务库的权限。在MySQL命令行执行:

CREATE DATABASE `demo_db` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'demo_user'@'%' IDENTIFIED BY 'demo_pass123'; GRANT ALL PRIVILEGES ON demo_db.* TO 'demo_user'@'%'; FLUSH PRIVILEGES;

这段SQL的执行逻辑很清楚:先建库,再建账号,然后授权,最后刷新权限表。这里的demo_db.*意思是对demo_db库下所有表开放权限,但不会影响其他库,风险被控制在一个库范围内。

4. 进入容器执行SQL的完整实操

这个标题的核心诉求其实就是这一节:怎么进入容器执行SQL。很多人第一次操作时,不明白“执行SQL”还有好几种方式,每一种的使用场景都不同。

4.1 交互式进入容器与MySQL命令

最直接的方式是两步:先进入容器的bash,再进入MySQL命令行。

docker exec -it mysql8 bash

进入容器后,你会看到命令行提示符变成类似root@容器ID:/#,说明你已经到了容器内部。此时执行:

mysql -uroot -p

输入密码后,看到mysql>提示符,就成功进入了MySQL命令行。这之后的所有操作,比如SHOW DATABASES;、SELECT NOW();、CREATE TABLE ...,都是在容器内的MySQL实例上执行的。

需要留意的是,docker exec -it后面的-it是两个参数,-i表示交互式输入,-t分配一个伪终端。少了-t你可能看不到彩色的命令行输出,少了-i,你输入的命令不会传给容器。所以这两个字母务必一起用。

我写了一个最常用的完整流程,帮你建立体感:

[root@host ~]# docker exec -it mysql8 bash root@a1b2c3d4e5f6:/# mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. ... mysql> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | demo_db | | mysql | | performance_schema | | sys | +--------------------+ 5 rows in set (0.00 sec) mysql> USE demo_db; Database changed mysql> SELECT NOW(); +---------------------+ | NOW() | +---------------------+ | 2025-01-15 14:30:22 | +---------------------+

4.2 不进入容器直接执行单条SQL

交互式操作适合调试,但如果你想在脚本或CI/CD流水线里执行SQL,不能每次都手动进入容器。这时可以直接用docker exec在宿主机上执行:

docker exec -i mysql8 mysql -uroot -p123456 -e "SHOW DATABASES;"

注意我这里用的是-i没有-t,因为是非交互式执行,不需要伪终端。-p123456的写法是把密码直接跟在-p后面,中间没有空格,这是MySQL命令的固定语法要求。

再举一个实用例子,直接查询业务数据:

docker exec -i mysql8 mysql -uroot -p123456 demo_db -e "SELECT id, name, create_time FROM users WHERE status=1 ORDER BY create_time DESC LIMIT 10;"

这里在容器名后跟了一个demo_db,表示默认使用这个数据库,不需要在SQL里写USE demo_db;。命令会在宿主机控制台直接打印查询结果,非常适合快速验证数据状态。

4.3 在容器内执行SQL脚本文件

实际开发中,初始化表结构、导入测试数据,动辄成百上千行SQL,不可能手动一条条敲。这时候需要执行SQL脚本文件。有两种场景,对应两种做法。

场景一:脚本已经在宿主机上。用重定向方式导入:

docker exec -i mysql8 mysql -uroot -p123456 demo_db < /path/to/init.sql

关键点在-i参数:它让容器保持标准输入打开,宿主机上的文件内容通过管道流进容器里的MySQL进程。少了-i,这个导入会静默失败,SQL文件不生效,还不报错,是很多人的第一个坑。

场景二:脚本在容器内部。先进入容器,再执行source:

docker exec -it mysql8 bash mysql -uroot -p123456 demo_db source /tmp/init.sql;

source是MySQL客户端的内部命令,它会逐条读取并执行sql文件中的语句。相比重定向方式,它会把执行结果打印在屏幕上,方便观察每条语句是否出错。

如果你在开发Java后端项目,用MyBatis-Plus这类框架时,通常会根据实体类自动生成建表SQL,然后把这些SQL存成脚本,在Docker容器里执行。做法就是上面这两条:要么重定向,要么source,没有第三条路。别想着把sql文件往容器里拖,最简单的办法是用命令直接拷贝进去:

docker cp /path/to/init.sql mysql8:/tmp/init.sql

然后进入容器执行source /tmp/init.sql;,完事之后记得清理:

docker exec mysql8 rm -f /tmp/init.sql

4.4 用图形化客户端连容器里的MySQL

数据库操作光靠命令行还是不够直观,日常我主要用Navicat或DBeaver这类图形客户端。连接参数就四个:主机地址填127.0.0.1,端口填3306,用户名填root或业务账号,密码填对应密码。

这里有个非常容易出问题的点:客户端连不上时,先检查容器端口是否真的映射出来了。执行docker ps看PORTS列,如果显示的是0.0.0.0:3306->3306/tcp,说明宿主机3306端口已经在监听。再用telnet 127.0.0.1 3306测试宿主机端口是否通。如果宿主机能通但Navicat连不上,多数是账号权限或认证插件问题。

还有一个小细节:Navicat连接MySQL 8.0时,如果报Authentication plugin 'caching_sha2_password' cannot be loaded,是因为Navicat老版本不支持新认证插件。解法有两个:把Navicat升级到新版本,或者按前文所述把账号的认证插件改成mysql_native_password。

ALTER USER 'demo_user'@'%' IDENTIFIED WITH mysql_native_password BY 'demo_pass123'; FLUSH PRIVILEGES;

5. 常见问题与排查技巧实录

这部分是我最想跟你分享的。Docker跑MySQL出错时,报错信息有时候很抽象,新手容易一头雾水。我把实际工作中高频踩过的坑按症状整理出来,每一条都给出排查思路和解决方法。

5.1 容器启动失败排查清单

症状一:端口冲突

报错信息长这样:

docker: Error response from daemon: driver failed programming external connectivity on endpoint mysql8: Bind for 0.0.0.0:3306 failed: port is already allocated.

这几乎可以肯定是宿主机3306端口被占用了。排查方法:

netstat -tlnp | grep 3306

在Windows上则是:

netstat -ano | findstr 3306

看到占用进程后,要么停掉占用进程,要么改端口名映射。改端口是最省事的:

docker run -d --name mysql8 -p 3307:3306 -e MYSQL_ROOT_PASSWORD=root123456 mysql:8.0

改完之后,客户端连接的端口也相应改成3307。

症状二:镜像启动后立即退出

docker ps -a看到容器状态是Exited (1),先用日志定位:

docker logs mysql8

如果日志里出现Can't start server: Bind on unix socket: No such file or directory或[ERROR] InnoDB: Unable to lock ./ibdata1,通常是因为挂载的数据卷权限不对。宿主机目录和数据卷的权限必须让容器内mysql用户可写。最简单粗暴的解法:删掉旧的匿名数据卷,全新重建后用具名数据卷。

症状三:内存不足

MySQL 8.0镜像默认会吃掉几百MB内存。如果你是在低配服务器或Windows上跑,容器可能因为OOM被杀掉。日志里会有Killed字样。解法是限制容器内存:

docker run -d --name mysql8 -p 3306:3306 -m 512m --memory-swap 512m -e MYSQL_ROOT_PASSWORD=root123456 mysql:8.0

-m 512m表示容器最多使用512MB内存。不过要注意,MySQL内存需求跟连接数和buffer pool正相关,限制太低可能反过来影响性能。

5.2 连接不上容器的MySQL

现象很常见:容器明明是Up状态,但Navicat或Java应用连不上。我按排查路径列一下顺序:

第一步,确认MySQL进程在容器内正常监听。进入容器看端口:

docker exec mysql8 netstat -tlnp | grep 3306

第二步,确认账号是否允许远程连接。如果mysql.user表里没有'root'@'%'记录,任何外部连接都会被拒绝。查看方式:

docker exec mysql8 mysql -uroot -p -e "SELECT user, host FROM mysql.user;"

如果只有root@localhost,执行前面说过的ALTER USER就好。

第三步,确认防火墙没有拦截。Linux服务器上要放行3306端口,很多云服务器还有安全组规则,默认只放行了22、80、443这几个端口,3306得单独去控制台加白名单。

第四步,确认application.yml里的连接串写对了。SpringBoot项目里常见的错误是时区没写或字符集不对,正确的连接串参考:

spring: datasource: url: jdbc:mysql://127.0.0.1:3306/demo_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

allowPublicKeyRetrieval=true这里特别提一句。MySQL 8.0使用caching_sha2_password认证时,客户端需要获取服务器公钥,JDBC驱动默认不允许明文传输公钥,连不上时会报:

Public Key Retrieval is not allowed

加这个参数就能解决。

5.3 慢SQL与MySQL 8.0配置优化

很多人在Docker里跑MySQL,发现同样一份数据,比本地的慢。这通常是默认配置没有针对你的工作负载调优。看几个关键指标:

最大连接数。默认只有151,如果你的Java应用连接池配了50个连接,再加上管理端和报表系统,很容易打满。启动容器时加参数,或在挂载的my.cnf里加:

max_connections=500

缓冲池大小。InnoDB的buffer pool默认是128MB,生产机建议调到物理内存的50%-70%。在my.cnf里设置:

innodb_buffer_pool_size=1G

设置后可以在MySQL命令行验证:

SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; SHOW VARIABLES LIKE 'max_connections';

慢SQL日志也建议开发阶段就打开,提前发现问题。my.cnf加:

slow_query_log=1 slow_query_log_file=/tmp/mysql-slow.log long_query_time=1

long_query_time=1表示超过1秒的查询都记录到慢日志里。定位问题时,执行SHOW FULL PROCESSLIST;能实时看到正在执行的SQL,再配合EXPLAIN分析执行计划,基本能解决95%的慢SQL问题。

5.4 容器删除与数据保留

最后一个高频疑问:容器删了数据还在吗?答案取决于你启动时有没有做数据卷挂载。

如果你用了-v mysql_data:/var/lib/mysql这种具名数据卷,即使执行docker rm -f mysql8删掉整个容器,数据卷里的数据依然完好。重新用同样的命令创建容器后,数据自动回来。这也是我强烈建议用具名数据卷的原因。

如果你没用数据卷,数据就在容器内部,删除容器等于数据全没。想保留数据的话,要先把容器里的数据复制出来:

docker cp mysql8:/var/lib/mysql ./mysql_bak

然后销毁容器,就可以在宿主机目录mysql_bak里拿到所有数据文件。

数据卷相关的常用操作也很简单:

# 查看所有数据卷 docker volume ls # 查看数据卷详情 docker volume inspect mysql_data # 手动删除无用数据卷 docker volume prune

6. 我的实操建议与后续扩展

最后再说几个从实战中沉淀下来的细节,希望能帮你少走弯路。

第一个建议:把常用的Docker命令整理成一份速查笔记。刚开始记不住很正常,我自己也是边查边用,用多了自然就记住了。但切记一条原则:凡是docker run创建容器,一定要把所有配置都写在命令里,别等启动完了再手动进容器改配置,那样容器重建后容易忘。

第二个建议:如果你只是想在本地快速搭一个MySQL学习环境,容器未必要设置太复杂。最简单的命令就是:

docker run -d --name mysql8 -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root123456 mysql:8.0

能用就行,等真正做项目再补数据卷和配置挂载。

第三个建议:容器内执行SQL脚本时,养成用-i重定向的习惯。因为脚本导入是自动化部署的一部分,纯交互式source方式虽然直观,但没法在脚本里复用。我把常用命令贴在这里,直接抄作业:

# 单条SQL查询 docker exec -i mysql8 mysql -uroot -p123456 -e "SHOW DATABASES;" # 执行SQL脚本文件 docker exec -i mysql8 mysql -uroot -p123456 demo_db < init.sql # 进入容器交互操作 docker exec -it mysql8 bash

还有一个不算太技术、但非常影响体验的建议:如果你的Docker Desktop启动很慢,或者经常启动失败,可以先检查WSL 2是否正常,再看看是否开启了Windows的虚拟化功能。这类环境问题最磨人,排查思路就一句话:先保证Docker本体能跑,再去折腾容器内部的东西。

关于后续扩展,我个人的使用习惯是在Docker里同时部署MySQL和Redis,用Docker Compose把它们编排在一起。一条docker compose up -d命令,整套开发环境直接拉起来,比一个个容器手动启动省心太多。等MySQL玩熟了,非常建议往这个方向走。

Docker下跑MySQL,本质上就是用一套标准化的流程,把数据库环境管理这件事变得可复现、可迁移。从头安装、启动、配置到进入容器执行SQL,流程不复杂,但每一步都有对应的细节。只要把这篇里的思路吃透,再遇到数据库部署,你大概率不会再想去装那个几GB的本地安装包了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询