用 Docker 启动 MySQL 8.0.35
正文
pacman 把我的 mysql 滚到 9.6.0 了,太新了导致以前很多命令无法使用,而比如 canal 又依赖这些过去的命令,所以一直有报错写进 logs/example.log:”Caused by: java.io.IOException: ErrorPacket [errorNumber=1064, fieldCount=-1, message=You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘master status’ at line 1, sqlState=42000, sqlStateMarker=#] with command: show master status.”即 show master status 在 9.0 的版本中是无法使用的。于是利用 docker 将 mysql 回退到之前的版本,退到 8.0.35。
首先,从本地高版本数据库安全导出所需要的业务库表( \ 是换行符,增强可读性):
mysqldump -u root -p \ |
然后,彻底清理本地占用进程,并取消开机自启动:
sudo systemctl stop mysqld |
安装 Docker,启动服务,并设置开机自启:
sudo pacman -S docker |
然后重启电脑,下次电脑启动后就会全局生效。也有一个临时方案,仅仅对当前 shell 会话生效(当然直接每次都 sudo 也可以):
newgrp docker |
用 Docker 启动 MySQL 8.0 的实例。这里使用的是稳定的 8.0.35:
docker run --name mysql8-canal \ |
参数解释:
docker run: Docker 的核心命令,用于新建并运行一个容器。--name mysql8-canal: 给容器指定一个自定义名称(mysql8-canal),方便后续通过名字进行管理,而不是使用随机生成的字符串 ID。-p 3306:3306: 端口映射。格式为宿主机端口:容器内端口。这里将宿主机(我的 linux)的 3306 端口映射到容器的 3306 端口,这样就很方便,发送到宿主机的 IP 的请求,经过 Docker 代理转发,从而访问到容器中的 mysqld。-v ~/mysql_data:/var/lib/mysql: 数据卷挂载(持久化)。将宿主机的~/mysql_data目录映射到容器内的数据库存储目录。这样即使容器被删除,数据库中的数据也不会丢失。-e MYSQL_ROOT_PASSWORD=root: 设置环境变量。此处指定了 Docker 中的 MySQL实例的root用户的初始登录密码为root,可以自行修改,也不要求和原本地上的 MySQL 实例一致,因为它们其实是独立的两个实例。-d: 后台运行模式(Detached mode)。让容器在后台执行,不占用当前的终端窗口。mysql:8.0.35: 指定使用的镜像(mysql)及版本标签(Tag,8.0.35)。如果本地没有,Docker 会去官方仓库(Docker Hub)里找名为 mysql 的镜像并拉取指定的版本然后下载到本地,最后基于这个本地镜像克隆出一个运行中的容器,即一个隔离的运行环境。
注意:如果上面的命令连接不到镜像,那么就需要配置镜像源,新建或编辑文件 /etc/docker/daemon.json,写入:
{ |
然后重启 docker 服务读取新配置,再重新 docker run ... 一次即可:
sudo systemctl restart docker |
使用下面的命令可以看到下载的镜像的信息:
sudo docker image ls |
run 成功后,本地就有了 8.0.35 的镜像,且对应的 MySQL 实例也启动成功。接下来就可以登陆 MySQL 容器了:
docker exec -it mysql8-canal mysql -uroot -proot |
参数解释:
docker exec: 在运行中的容器内执行新命令。-i(interactive)——交互模式,让容器的标准输入(STDIN)保持打开,确保容器里的程序能接收我输入的命令;-t(tty)——给容器分配一个伪终端(Pseudo-TTY),这个伪终端会模拟我输入所在的终端给容器看,进而容器可以按照我的终端的显示规则来输出数据,保证了可读性。这二者一般一起用。所以是-it。mysql8-canal: 之前指定的容器名称。mysql: 容器内要执行的程序名(即 MySQL 命令行客户端)。-uroot:mysql程序的参数,指定以root用户身份登录(-u后紧跟用户名)。-proot:mysql程序的参数,指定登录密码为root(-p后紧跟密码)。
这样就会连接到容器中的 MySQL 实例。接下来就可以配置 canal 的权限,用 canal 来监听数据库的变化。由于 8.0.35 的兼容性,需要给 canal 配置的其实很少,就下面几行:
-- 1. 创建 canal 用户并强制使用 mysql_native_password,使用 native 是考虑与 canal 的兼容 |
参数解释:
WITH mysql_native_password:MySQL 8.0 默认使用 caching_sha2_password,很多旧版 Canal 驱动不认识它,会导致连接失败。强制改成 native 模式能保证 Canal 连上。、
REPLICATION SLAVE:让 Canal 伪装成从库,这样它才有权限读取 Binlog。
然后退出 mysql 的交互命令行界面,回到原始终端,执行下面的代码,把备份的数据导入 docker 中的 mysql 实例。这会花费一点时间,完成后客户端会自动断开连接:
docker exec -i mysql8-canal mysql -uroot -proot < all_biz_data.sql |
然后再次进入:
docker exec -it mysql8-canal mysql -uroot -proot |
执行命令,可以看到已经有我们之前的数据库表了:
mysql> show databases; |
之后回到 IDEA 中,在 DataGrip 中断开原数据库的连接,重启 IDEA,然后重新连接数据库即可,这次将连接到容器中 8.0.35 的实例。连接成功后,需要根据 IDEA 的提示,同步更新 JDBC 的驱动。最后结果如下:

8.0 版本的好处在于,基本上默认的配置是和 canal 兼容的,只需要设置 canal 的权限即可。剩下的根据需求,配置 canal 的全局配置文件 conf/canal.properties 和 实例文件 conf/example/instance.properties 文件就好了。
以及如果遇到需要删除容器的情况,就停止、删除旧容器,并清理本地数据目录,以确保整洁:
docker stop mysql8-canal |
镜像、容器与程序的关系
镜像(Image) 是一个包含应用程序及其完整运行时环境的只读分层文件系统(RootFS)。它在构建阶段(Build Time)就通过 Dockerfile 预装了二进制程序(如 mysql 客户端和服务端)、依赖库和系统工具。可以说,最小依赖,开箱即用。
容器(Container) 则是镜像的运行实例。当 Docker 启动容器时,它在镜像的只读层之上添加了一个可写层(Copy-on-Write)。甚至可以将镜像看作类(Class),而容器是该类在内存中被实例化的对象(Object)。它是一个被隔离出来的运行环境,有自己的文件系统、自己的网络接口和自己的内存空间,但不像虚拟机有自己的独立内核,而是共享的宿主机的内核。
在容器启动(run)后,就至少会启动一个已经在镜像中打包好了的程序,来作为核心的程序。在我们的场景中,它就是 mysqld,MySQL 的服务端。容器的生命周期是和这个主程序绑定的。容器中运行的所有程序,都不是运行在虚拟机上的,而是一个直接运行在宿主机内核上的特殊进程,因此可以直接在宿主机上找到这个进程并杀死它——容器随即销毁。
exec 的场景,就是在容器中启动了一个辅助程序/进程(与上面的主进程相对),它与主进程共享同一个网络、文件系统和进程空间——即共处一个容器之中。而这个程序,同样,在镜像阶段就被打包好了,比如这里使用到的 mysql-client 来与 MySQL 服务端连接——它不是我们用 pacman 安装的客户端(当然也可以使用它来访问 3306 端口,然后请求会因为端口映射转发到 Docker 中),而是被打包进镜像的客户端。