使用Docker官方MySQL镜像建库遇ERROR 2002,为何需sleep才能正常执行
Docker MySQL容器启动后无法立即执行SQL的成因分析
问题场景
你编写了如下Shell脚本,用于通过Docker官方MySQL 5.7镜像启动容器并创建多个业务数据库:
#!/bin/bash mysql_apim_password=root mysql_apim_host=localhost mysql_apim_username=root if [ ! "$(docker ps -q -f name=mysql-5.7)" ]; then echo -e "---> Starting MySQL docker container..." container_id=$(docker run -d --name mysql-5.7 -p 3306:3306 -e MYSQL_ROOT_HOST=% -e MYSQL_ROOT_PASSWORD=$mysql_apim_password mysql:5.7.19) echo $container_id docker ps -a echo -e "---> Waiting for MySQL to start on 3306..." while ! nc -z $mysql_apim_host 3306; do sleep 1 printf "." done echo "" echo -e "---> MySQL Started." else echo -e "---> MySQL is already running..." fi #sleep 10 echo -e "---> Creating databases..." docker exec -it mysql-5.7 mysql -h$mysql_apim_host -u$mysql_apim_username -p$mysql_apim_password -e "DROP DATABASE IF EXISTS \"am_db\"; DROP DATABASE IF EXISTS \"um_db\"; DROP DATABASE IF EXISTS \"reg_db\"; CREATE DATABASE \"am_db\"; CREATE DATABASE \"um_db\"; CREATE DATABASE \"reg_db\";"
但如果注释掉sleep 10,执行时会报错:
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
即使容器启动后socket文件已经存在,只有加上10秒延迟才能正常执行。
核心成因
这个问题的关键在于你对MySQL服务就绪的判断逻辑不准确:
- 端口监听≠服务就绪:
nc -z命令只是检测3306端口是否被容器绑定监听,但MySQL容器启动时,端口会很快被打开,而后台还在执行一系列初始化操作——比如初始化系统数据库(如mysql库)、加载权限表、完成启动前的配置加载等。 - Socket存在≠可处理连接:虽然
/var/run/mysqld/mysqld.sock文件已经生成,但此时MySQL进程可能还在初始化内部状态,并未进入可以处理客户端连接请求的阶段。这时候尝试通过socket连接执行SQL,就会触发上述错误。 - Sleep的作用:
sleep 10本质是给MySQL足够的时间完成所有初始化流程,等服务完全进入就绪状态后再执行SQL命令,因此能成功执行。
更可靠的替代方案
依赖固定时长的sleep并不稳定(不同环境下MySQL初始化时间可能不同),推荐用MySQL自带的mysqladmin ping命令来循环检测服务是否真的就绪:
echo -e "---> Waiting for MySQL to be fully ready..." until docker exec mysql-5.7 mysqladmin ping -h"$mysql_apim_host" -u"$mysql_apim_username" -p"$mysql_apim_password" --silent; do sleep 1 printf "." done echo "" echo -e "---> MySQL is fully ready."
这个命令会实际尝试与MySQL服务建立连接并发送ping请求,只有当服务真正能响应时,才会退出循环执行后续的数据库创建操作,比端口检测或固定sleep更可靠。
内容的提问来源于stack exchange,提问作者Chiranga Alwis
相关产品推荐
相关产品推荐

