
1. 写在前面为什么我推荐在 Mac 上通过 Docker 装 MySQL先交代一下背景。我平时主要用 Mac 做开发前后端都会碰数据库这块尤其依赖 MySQL。早期在 Mac 上装 MySQL 的方式无非两种直接下 dmg 安装包或者用 Homebrew 装。这两种方式我都用过但说实话各有各的坑——dmg 版本升级麻烦卸载不干净还会在系统里留下一堆守护进程Homebrew 虽然命令行操作爽但版本切换不够灵活而且一旦系统环境出问题MySQL 经常会跟着遭殃。后来我切到 Docker 方案这才算是把数据库这块彻底驯服了。这篇文章想写的就是在 macOS 上用 Docker 安装 MySQL 的完整实操过程。涵盖镜像选择、容器启动、数据持久化、客户端连接、常见报错排查等环节甚至包括 Docker Desktop 本身偶尔抽风怎么处理。无论你是刚接触 Docker 的新手还是用 Mac 做开发想统一数据库环境的同学这篇都可以直接当操作手册用。可能有人会问我直接在 Mac 上装一个 MySQL 不就行了费劲用 Docker 干嘛这个问题我后面会专门解释但先给个最直接的理由Docker 带来的环境隔离和可重置性是本地安装完全比不了的。2. 动手前的准备Docker 环境与镜像选型2.1 在 Mac 上安装 Docker Desktop用 Docker 装 MySQL前提是先让 Mac 上能跑 Docker。目前 macOS 上最主流的方案是 Docker Desktop它自带图形界面装完就能用对新手非常友好。安装过程其实没什么难度去 Docker 官网下载 Docker Desktop for Mac 的 dmg 安装包双击拖进 Applications 文件夹然后打开即可。这里有几个细节需要注意第一Docker Desktop 是基于虚拟化技术运行的。在 2023 年以后的版本上Docker Desktop 会默认使用 Apple Silicon 芯片M1/M2/M3/M4的虚拟化框架性能非常可观。如果你的 Mac 是 Intel 芯片的老款那 Docker Desktop 会通过 macOS 自带的 Hypervisor.framework 来运行速度会慢一些但也能接受。第二安装完成后务必打开 Docker Desktop 的 Settings在 Resources 里面给 Docker 分配足够的内存。我建议至少分配 4GB因为 MySQL 8.0 的默认内存占用并不低如果配额太小容器启动后容易出现莫名其妙的问题。第三启动 Docker Desktop 后记得在终端里跑一下docker version确认客户端和服务端都处于活跃状态。如果 docker daemon守护进程没有起来后续所有命令都会报无法连接 Docker 守护进程的错误。docker version正常情况下会输出 Client 和 Server 两段信息Server 段能看到版本号和操作系统类型。如果只显示 Client 而没有 Server说明 Docker Desktop 还没完全启动等十秒再试一次。注意如果执行 docker 命令提示权限不足permission denied大概率是当前终端会话没有继承 Docker Desktop 的环境变量。重启终端或者用source ~/.zshrc刷新一下即可。2.2 MySQL 镜像选型8.0 还是 5.7Docker 装 MySQL 有个天然优势你可以随意切换版本不用在系统里反复卸载重装。目前常用的官方镜像主要是 mysql:5.7、mysql:8.0以及面向新架构的 mysql:8.4LTS 版本。我的建议是新项目、新环境无脑选 8.0严格来说可以选 8.0.x 的最新小版本例如 mysql:8.0.36。原因是 8.0 默认的认证插件是 caching_sha2_password安全性更好而且在 8.0.28 以后字符集默认已经是 utf8mb4对中文和 emoji 的支持非常完备省去了很多配置烦恼。如果你是为了兼容老项目或者某些旧版 ORM 组件对 MySQL 8.0 的认证方式不兼容可以退回到 5.7。但要注意MySQL 5.7 官方镜像已经停止更新维护新漏洞不会再有补丁能不用就别用了。镜像下载用一个命令搞定docker pull mysql:8.0下载完后可以用docker images查看本地镜像列表。如果你下载了多个版本的 MySQL 镜像启动容器时可以用不同的名字区分比如 mysql8、mysql57。3. 开始实操创建并启动 MySQL 容器3.1 最简启动方式先让 MySQL 跑起来如果你只是想在本地临时跑一个 MySQL验证一下语法或者给测试环境用那么一条命令就够了docker run --name mysql-test -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -d mysql:8.0这个命令做的事情可以拆开来看--name mysql-test给容器起个名字后续操作直接引用这个名字就不用记容器 ID 了。-e MYSQL_ROOT_PASSWORD123456设置 MySQL root 用户的密码。这是官方镜像初始化时读取的环境变量没有它容器会启动失败这是新手最常踩的第一道坑。-p 3306:3306端口映射。左边宿主机端口右边容器端口。这样你就能用 Mac 的 3306 端口去访问容器里 MySQL 的 3306 端口。-d后台运行终端不会卡住。mysql:8.0使用的镜像名。跑完以后用docker ps看容器状态STATUS 显示Up就说明已经在运行了。这时候你在 Mac 终端里执行mysql -uroot -p123456 -h127.0.0.1 -P3306就能进数据库。但注意这种方式启动的容器有个致命问题容器一旦被删除容器内的所有数据全部没了。因为数据写在了容器可写层里而容器生命周期结束这一层就被销毁了。这显然不适合长期使用所以接下来我要给出一个更完整的启动命令。3.2 生产可用版启动命令一次到位我自己在 Mac 上日常使用的启动命令是这样的docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDMyPass123 \ -e TZAsia/Shanghai \ -v ~/docker/mysql8/conf:/etc/mysql/conf.d \ -v ~/docker/mysql8/data:/var/lib/mysql \ -v ~/docker/mysql8/logs:/var/log/mysql \ --restartalways \ mysql:8.0相比最简版本这里多了几个关键点数据卷挂载-v。-v ~/docker/mysql8/data:/var/lib/mysql表示把宿主机目录~/docker/mysql8/data映射到容器里的数据目录。MySQL 的所有库表数据、binlog 都在/var/lib/mysql下挂载之后容器删了、重建了、甚至镜像换了数据都还会留在宿主机目录里。这个习惯一定要养成否则迟早会为丢数据买单。时区设置-e TZAsia/Shanghai。MySQL 容器默认时区是 UTC如果不改NOW()函数返回的时间会比北京时间慢 8 个小时。这在开发环境可能没什么感觉一旦涉及到定时任务、日志统计、订单时间比对就会发现时间对不上排查起来特别痛苦。配置文件挂载-v ~/docker/mysql8/conf:/etc/mysql/conf.d。MySQL 容器也支持自定义配置文件官方镜像会读取/etc/mysql/conf.d目录下的 .cnf 文件。你可以把自定义配置放在宿主机的~/docker/mysql8/conf/my.cnf里比如修改最大连接数、默认字符集、慢查询阈值等改完重启容器即可生效不用重新构建镜像。自动重启--restartalways。这个参数能让 Docker 守护进程在容器异常退出、或者 Mac 重启之后自动拉起容器。我个人的体会是开发机上开着 MySQL 容器突然重启电脑是家常便饭没有这个参数的话开机后你极有可能忘记启动数据库然后一脸懵地排查半天为什么连不上数据库。最后检查一下执行docker ps看容器状态和端口映射情况。如果一切正常输出里应该能看到0.0.0.0:3306-3306/tcp。3.3 参数背后的原理为什么挂载目录不能乱选和直接安装 MySQL 不同Docker 容器里的进程看不到宿主机的真实文件系统它只能看到自己所在容器里的虚拟文件系统。如果不做挂载容器里/var/lib/mysql目录下的所有数据都保存在容器的可写层里。这里要理解一个关键概念可写层的生命周期和容器绑定。用docker stop停止容器数据不会消失但用docker rm删除容器可写层会被回收数据就彻底没了。而挂载数据卷相当于把宿主机真实目录借给容器使用容器的进程往/var/lib/mysql写文件实际写到了宿主机目录。这种数据持久化的方式保证了即使容器被删除重建数据依然在宿主机磁盘上完好无损。所以要注意选一个稳定、好记、有备份习惯的目录。我见过有人把数据卷挂在/tmp下结果系统一清理临时文件数据库直接回到初始化状态——数据全没了。推荐挂在用户目录下比如~/docker/mysql8/data不要放在系统盘之外的其他盘符如果有移动硬盘避免意外拔插导致数据损坏。注意如果宿主机挂载目录不存在Docker 会自动创建这很方便。但目录权限可能出现问题。如果容器启动时报 Permission denied 或者数据目录无法访问检查一下宿主机的目录权限必要时用chmod -R 777 ~/docker/mysql8放宽权限。4. 配置优化与客户端连接4.1 进入容器命令行操作 MySQL容器起来以后有两种方式进入 MySQL 命令行环境。方式一通过 docker exec 进入容器再调用 mysql 客户端。docker exec -it mysql8 mysql -uroot -p输入密码后就进入了 MySQL 交互式命令行。方式二直接通过宿主机安装的 mysql 客户端连接。Mac 上可以通过 Homebrew 装 mysql-clientbrew install mysql-client mysql -h127.0.0.1 -P3306 -uroot -p这两种方式各有适用场景。docker exec的方式不依赖宿主机有没有 mysql 客户端是通用解法宿主机安装客户端的方式则更适合日常调试因为你可以直接用其他命令配合而且退出容器不影响任何东西。我个人会优先推荐方式二因为在宿主机上执行 SQL 文件、导入导出数据库时不用多一层 docker exec 的封装脚本写起来清爽很多。4.2 创建业务用户并授权MySQL 装了以后直接用 root 连接不利于安全管理尤其多人协作时所有人都共享 root 密码是件危险的事。所以装完 MySQL第一件事就是创建业务用户和独立的数据库。在 MySQL 命令行里执行以下 SQLCREATE DATABASE IF NOT EXISTS app_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER app_user% IDENTIFIED BY AppPass2024; GRANT ALL PRIVILEGES ON app_db.* TO app_user%; FLUSH PRIVILEGES;解释几个关键点字符集选utf8mb4而不是utf8是因为utf8在 MySQL 里最多只支持 3 字节像 emoji 这种四字节字符会报错。utf8mb4是完整的 UTF-8 实现配合utf8mb4_unicode_ci排序规则编码问题最少。用户后面的%表示任何主机都可以用这个账号连接。如果只想本机访问可以改成localhost这样更安全。FLUSH PRIVILEGES在修改授权表后执行确保权限立即生效。4.3 图形化客户端连接DataGrip/Navicat/TablePlus命令行虽好但日常开发时图形化客户端能显著提高效率。Docker 里的 MySQL 和本地安装的 MySQL 在连接上没有任何区别因为端口已经映射到宿主机了客户端直接连127.0.0.1:3306即可。我用过的几款客户端里简单说下特点客户端优点缺点适合人群DataGrip智能提示强支持所有主流数据库内存占用大License 收费JetBrains 生态用户Navicat功能全面可视化建表很顺手收费网络口碑存疑数据库重度使用者TablePlus轻量原生 Mac 风格流畅免费版限制连接数Mac 用户新手MySQL Workbench官方免费功能完整界面略显笨重不想装收费软件的人连接参数的设置都是一样的Host: 127.0.0.1Port: 3306User: app_userPassword: 上面设置过的密码Database: app_db可跳过这里有一个常见误区。有些同学以为宿主机端口映射成 3306 后客户端应该连容器 IP 比如 172.17.0.2。其实不然。客户端跑在宿主机上它访问的是宿主机网络的 3306 端口Docker 的网络层会把宿主机 3306 的流量转发到容器内的 3306。所以客户端还是连127.0.0.1或localhost。4.4 用 docker-compose 管理一劳永逸的方式如果项目比较规范我建议别直接 docker run而是用 docker-compose.yml 把 MySQL 的配置、端口、密码、数据卷都记录下来。这样无论是新同事搭建环境还是自己换电脑后恢复环境执行一条命令就能拉起来整套数据库服务。下面是我常用的 docker-compose 配置version: 3.8 services: mysql: image: mysql:8.0 container_name: mysql8 restart: always ports: - 3306:3306 environment: MYSQL_ROOT_PASSWORD: MyPass123 MYSQL_DATABASE: app_db MYSQL_USER: app_user MYSQL_PASSWORD: AppPass2024 TZ: Asia/Shanghai command: - --character-set-serverutf8mb4 - --collation-serverutf8mb4_unicode_ci - --default-time-zone8:00 volumes: - ~/docker/mysql8/data:/var/lib/mysql - ~/docker/mysql8/conf:/etc/mysql/conf.d - ~/docker/mysql8/logs:/var/log/mysql这里额外说明一下 command 部分。character-set-server和collation-server是 MySQL 服务端的全局默认字符集和排序规则配置后建库建表不显式指定字符集也不会出现中文乱码。default-time-zone8:00是直接让 MySQL 服务端的会话时区强制为东八区比只在环境变量里设TZ更保险——因为TZ主要影响系统层面的时间MySQL 的 time_zone 参数是独立的。在 docker-compose.yml 所在目录执行docker compose up -d查看服务状态docker compose ps这种管理方式的另一大好处是停止、启动、重建都可以用命令精确控制不需要记容器 ID。docker compose stop mysql docker compose start mysql docker compose down提示执行docker compose down会停止并删除容器但不会删除挂载数据卷。数据依然保留在宿主机的~/docker/mysql8/data目录下。这是 compose 的默认行为也正是我们想要的效果。5. 实操中的五个经典问题与排查技巧5.1 端口占用3306 被本机 MySQL 或其他进程占用了怎么办这是最常见的问题。很多 Mac 用户之前用 Homebrew 装过 MySQL开机自启没有关掉3306 端口一直被本机 MySQL 占着Docker 容器自然映射失败。排查命令lsof -i :3306输出里能看到占用 3306 端口的进程 PID。如果这个进程是 mysqld说明是本机装了 MySQL需要先停掉它# 通过 brew services 关闭自启的 MySQL brew services stop mysql # 或者直接杀掉进程慎用确保不是其他重要服务 kill PID停掉之后再执行docker start mysql8端口就能正常映射了。另一种方案是既然本机 MySQL 还偶尔要用那就让 Docker 使用另一个端口比如 3307。把容器端口映射改成-p 3307:3306客户端连接时端口填 3307。这个方法可以作为权宜之计但长期看最好统一使用一种方案避免混乱。5.2 连接报错ERROR 2003 / 1045 / 1130连接 MySQL 时报错是最让人崩溃的先从最常见的几个错误说起。ERROR 2003 (HY000): Cant connect to MySQL server on 127.0.0.1这个错误表示客户端根本连不上 MySQL 服务端。先执行docker ps确认容器在运行再确认端口映射是否正常docker port mysql8能显示映射关系最后检查防火墙是否拦截了 3306 端口。在 Mac 上默认防火墙不拦本地回环流量所以绝大多数情况是容器没起来或端口没映射。ERROR 1045 (28000): Access denied for user rootlocalhost用户名或密码不对。MySQL 官方镜像初始化时MYSQL_ROOT_PASSWORD环境变量被读取一次后续容器初始化时不会再次覆盖已存在的数据库目录。如果你在启动容器时改了密码但数据卷还是旧目录新密码是不会生效的。这时候有两种处理方式用旧密码登录或者清空数据卷重新初始化开发环境允许的情况下。ERROR 1130 (HY000): Host x.x.x.x is not allowed to connect to this MySQL server客户端 IP 不在允许访问的白名单内。如果用户创建语句用了app_userlocalhost那局域网其他机器就无法连接。解决办法是把用户的主机域改成%或者单独创建一个app_user192.168.%的账号让它只允许特定网段访问。生产环境不建议%本地开发图省事无所谓。5.3 MySQL 8.0 认证插件导致的客户端兼容问题MySQL 8.0 默认认证插件从mysql_native_password改为caching_sha2_password这个变化对新客户端没有影响但一些老版本的客户端如 PHP 7.1 之前的 pdo_mysql、旧版 Navicat、旧驱动连接时会报Authentication plugin caching_sha2_password cannot be loaded或类似的错误。解决方案有两种第一种改用户认证插件。MySQL 8.0 依然支持老插件ALTER USER app_user% IDENTIFIED WITH mysql_native_password BY AppPass2024; FLUSH PRIVILEGES;但要注意mysql_native_password插件在新版本里标记为过期未来版本会移除。这不是长久之计。第二种升级客户端。换新版的 MySQL 客户端、JDBC 驱动8.0.30、Navicat 17 等从根源上解决问题。我建议优先升级客户端毕竟数据库安全性不能为了兼容老工具而降级。5.4 容器停止了数据会丢吗分两种情况用docker stop停止容器数据不丢用docker rm删除容器如果容器启动时创建了数据卷数据也还在宿主机挂载目录里如果没有挂载数据卷数据随容器销毁而彻底消失。验证数据是否安全最简单的方法是查看挂载目录里的文件ls -l ~/docker/mysql8/data如果能看到 mysql、ibdata1 等文件或目录说明数据确实持久化在宿主机上。容器删除重建后只要挂载到同一个目录数据库里的库、表、数据都会毫发无损地回来。这里补充一个小经验。我在 Mac 上用过一段时间 Docker 容器放 MySQL每周也会用docker exec mysql8 sh -c exec mysqldump -uroot -p $MYSQL_ROOT_PASSWORD --all-databases ~/backup/all.sql做一次逻辑备份。平时感觉无所谓但有一次 Mac 系统崩溃重装后数据卷没有备份彻底就靠这些 dump 文件恢复了大部分数据。所以建议大家Docker 里的数据也别完全依赖挂载周期性备份不能省。5.5 Docker Desktop 启动失败一个典型案例标题里的热搜词里有 virtualization support not detected docker desktop failed to start 相关的内容虽然这是 Windows 的常见报错但 Mac 上 Docker Desktop 也有类似的启动失败问题。我遇到过几种典型情况。情况一Docker Desktop 卡在启动页不动这个一般不是资源不足就是 Docker Desktop 进程卡死。先尝试完全退出 Docker Desktop状态栏右键 Quit再启动。如果不行执行pkill -f Docker然后重新打开 Docker Desktop。情况二提示 Cannot connect to the Docker daemon at unix:///var/run/docker.sock这表示客户端找到了 Docker CLI但 Docker daemon 没有响应。最直接的节点是 Docker Desktop 没有启动成功。打开 Docker Desktop 界面等鲸鱼图标停止动画冷却。也可以执行docker info如果输出里没有 Server 信息说明 daemon 还有问题重启 Docker Desktop。情况三Apple Silicon 上运行 Intel 镜像时提示架构不兼容在 M1/M2 芯片上部分 MySQL 老镜像比如 5.6、5.7没有 ARM64 版本Docker 会通过模拟方式运行有时候会报错或者性能极差。这种情况最好的方案是换用兼容新架构的镜像版本比如改用 MySQL 8.0或者使用--platform linux/amd64强制模拟运行不推荐性能损失明显。6. 最后再分享几个使用 Docker 装 MySQL 的小心得一路踩坑过来有几个体会想单独拿出来说说。第一把环境配置当代码管理。docker-compose.yml 其实就是你 MySQL 环境的源代码把它放进 Git 仓库里团队共享、版本回退都很方便。这个习惯后来帮了我大忙——有次同事在 Mac 上折腾了三天装不上数据库我把 compose 文件丢给他一条docker compose up -d直接解决。第二不要轻易删除数据卷。确实有人为了重置环境习惯性docker compose down -v。那个-v会连数据卷一起删掉如果里面刚好有重要数据找回难度极大。在我工作的项目里我已经明确要求大家默认不加-v只有确认数据可丢时才允许加。第三MySQL 容器不是万能的。虽然 Docker 装了 MySQL 用起来很方便但如果你要做主从复制、需要操作裸设备级别的参数比如调整 InnoDB 刷盘策略、或者对磁盘 IO 性能极致敏感建议还是单独用物理机或云厂商的托管实例。容器在很多极端场景下的表现不算最优开发环境和中小型项目才是 Docker 装 MySQL 的主战场。第四在 Mac 上跑 Docker 的体验和 Linux 不完全一样。Docker Desktop 在 Mac 上底层通过虚拟化运行 Linux VM磁盘 IO 和网络吞吐都比 Linux 原生 Docker 差一些。做本地开发没问题但如果是压测或者跑对性能要求极高的 SQL最好还是在真正的 Linux 服务器上验证。不要因为本地跑得流畅就草率地认为生产环境也会一样流畅。第五密码和配置不要写在命令行历史里。启动 MySQL 容器时密码写在 docker run 命令里确实是常规操作但注意不要在公共机器上这么干shell history 一翻就能看到密码。安全点的方式是使用.env文件配合 docker-compose并将 .env 文件加入 .gitignore防止误提交到仓库。这篇实操就写到这儿。从下载镜像、启动容器、配置优化到常见问题排查基本上覆盖了 Mac 上用 Docker 装 MySQL 的全过程。如果你是按步骤操作容器此刻应该已经跑起来了。有操作问题的话回到文中对应的排查小节对号入座大多都能解决。以后遇到更细的场景比如搭主从、读性能监控我们到时候再接着聊。