Docker部署时Mysql导入初始数据乱码问题
Docker部署时Mysql导入初始数据乱码问题
2019-01-27 7760 ℃
前言
近几天在研究Docker部署项目的方法,找到了一篇不错的实战文章:Spring Boot 2 (五):Docker Compose + Spring Boot + Nginx + Mysql 实践,参照上面的写的方式进行了配置,去除了nginx:
version: '3'
services:
mysql:
container_name: blog-mysql
image: mysql/mysql-server:5.7
environment:
MYSQL_DATABASE: blog
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: '%'
ports:
- "3306:3306"
restart: always
blog:
container_name: blog
restart: always
build: .
working_dir: /blog
volumes:
- .:/blog
- ~/.m2:/root/.m2
expose:
- "8080"
depends_on:
- mysql
command: mvn clean spring-boot:run -Dspring-boot.run.profiles=docker
然后遇到一个问题,我需要在运行容器的时候运行初始化数据库的脚本才行,不然项目将无法使用,然后就开始在网上找方法。
Docker MySQL启动时自动执行初始建表脚本
通过查找资料,了解到:在mysql官方镜像中提供了容器启动时自动执行/docker-entrypoint-initdb.d
文件夹下的脚本的功能(包括shell脚本和sql脚本) docker-entrypoint.sh
中下面这段代码就是干这事儿的。
ls /docker-entrypoint-initdb.d/ > /dev/null
for f in /docker-entrypoint-initdb.d/*; do
process_init_file "$f" "${mysql[@]}"
done
# usage: process_init_file FILENAME MYSQLCOMMAND...
# ie: process_init_file foo.sh mysql -uroot
# (process a single initializer file, based on its extension. we define this
# function here, so that initializer scripts (*.sh) can use the same logic,
# potentially recursively, or override the logic used in subsequent calls)
process_init_file() {
local f="$1"; shift
local mysql=( "$@" )
case "$f" in
*.sh) echo "$0: running $f"; . "$f" ;;
*.sql) echo "$0: running $f"; "${mysql[@]}" < "$f"; echo ;;
*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${mysql[@]}"; echo ;;
*) echo "$0: ignoring $f" ;;
esac
echo
}
也就是说只要把自己的初始化脚本放到/docker-entrypoint-initdb.d/
文件夹下就可以了,由于在docker-compose中没有找到copy指令,所以我用volumes代替,然后将MySql部分改成了这样:
mysql:
container_name: blog-mysql
image: mysql/mysql-server:5.7
environment:
MYSQL_DATABASE: blog
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: '%'
volumes:
- ./data/blog.sql:/docker-entrypoint-initdb.d/blog.sql
ports:
- "3306:3306"
restart: always
MySQL导入初始数据乱码问题
在创建mysql容器的时候,成功的初始化了数据库,但却有了一个新问题:数据库中的中文都是乱码的。
接着就开始找中文乱码的解决办法。网上的方法大概有两种:
- 通过在命令中加入
character-set-server
和collation-server
两个选项来指定mysql的编码。
--character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- 挂载
my.cnf
,在my.cnf
文件中设置字符编码
[mysqld] character_set_server=utf8
这两种方式我都尝试了,但是,全都没有效果。
通过show variables like 'character%'
命令查看时,mysql默认字符集确实已改为了utf8
或utf8mb4
,但是执行脚本导入的数据依然是乱码。
我以为是我的导入方式不对,然后换成通过自定义shell脚本的方式来导入。
docker-compose.yaml
mysql:
build: ./mysql
ports:
- "3306:3306"
Dockerfile
FROM mysql:5.7
#这条语句可有可无
ENV MYSQL_DATABASE test
#设置免密登录
ENV MYSQL_ALLOW_EMPTY_PASSWORD yes
#将所需文件放到容器中
COPY setup.sh /mysql/setup.sh
COPY blog.sql /mysql/blog.sql
COPY privileges.sql /mysql/privileges.sql
#设置容器启动时执行的命令
CMD ["bash", "/mysql/setup.sh"]
privileges.sql
use mysql;
grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option;
flush privileges;
blog.sql略
setup.sh
#!/bin/bash
set -e
echo `service mysql status`
echo '1.启动mysql....'
#启动mysql
service mysql start
sleep 3
echo `service mysql status`
echo 'show databases'
DATABASES=$(mysql -e "show databases")
DATABASE="blog"
echo $DATABASES
echo $DATABASE
if [[ "$DATABASES" =~ "$DATABASE" ]];then
echo '--------mysql容器重启--------'
echo '2.数据库已存在,无需初始化数据....'
else
echo '--------mysql容器第一次启动--------'
echo '2.开始导入数据....'
#导入数据
mysql < /mysql/blog.sql
echo '3.导入数据完毕....'
sleep 3
echo `service mysql status`
#重新设置mysql密码
echo '4.开始修改密码....'
mysql < /mysql/privileges.sql
echo '5.修改密码完毕....'
echo `service mysql status`
echo 'mysql容器启动完毕,且数据导入成功'
fi
tail -f /dev/null
不过,很可惜,这种方式依然乱码。
这个乱码问题困扰了我很久,一直不知道原因,后来我试着先创建mysql镜像,然后手动导入SQL,用Navicat for MySQL执行sql完全没问题,但是,我在docker容器中以命令行的方式导入,却是乱码的。
mysql -uroot -p123456 blog < blog.sql
后来我在自己的windows系统上使用命令行的方式导入SQL,发现出现了同样的问题。我这里用的SQL文件是通过Navicat for MySQL导出的,接着我就把SQL文件换成了通过命令行的方式导出。
mysqldump -uroot -p123456 blog > blog.sql
然后重新构建docker,发现不再乱码。
后续
问题虽然成功解决了,但是没找到真正的原因有点不甘心,对比两个SQL文件,最大的不同就是,通过mysqldump导出的的脚本中多了许多/*! */;
这种语句,而这种语句是一种特殊的注释,其他的数据库产品不会执行。mysql特殊处理,会选择性的执行。可以认为是:预编译中的条件编译。所以问题应该就出在这上面。
后面在Mysql字符集设置中看到了这么一句话:
设置了表的默认字符集为utf8并且通过UTF-8编码发送查询,存入数据库的仍然是乱码。那connection连接层上可能出了问题。
解决方法是在发送查询前执行一下下面这句:SET NAMES 'utf8'
;它相当于下面的三句指令:
SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;
于是,我在Navicat for MySQL导出的sql文件的开头,加上了SET NAMES 'utf8';
,重新构建docker,确实不再乱码。看来问题应该就是出在这里,而且mysqldump导出的SQL中也有这么一句/*!40101 SET NAMES utf8 */;
,我将该语句删除后,mysqldump导出的SQL也出现了乱码问题!
自己数据库层面的知识果然还是了解得太少了,除了CRUD啥都不会。
最后,经过修改,我的docker-compose.yaml
改成了这样:
version: '3'
services:
mysql:
container_name: blog-mysql
image: mysql/mysql-server:5.7
privileged: true
restart: always
environment:
MYSQL_DATABASE: blog
MYSQL_ROOT_PASSWORD: 123456
MYSQL_ROOT_HOST: '%'
volumes:
- ./data/mysql:/var/lib/mysql
- ./data/blog.sql:/docker-entrypoint-initdb.d/blog.sql
expose:
- "3306"
# 测试时使用,正式环境最好不暴露端口到宿主机
# ports:
# - "3306:3306"
command: [
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_unicode_ci'
]
blog:
image: maven:3.5-jdk-8
container_name: blog
privileged: true
working_dir: /blog
restart: always
volumes:
- .:/blog
- ~/.m2:/root/.m2
ports:
- "8080:8080"
depends_on:
- mysql
command: mvn clean spring-boot:run -Dspring-boot.run.profiles=docker
文章链接:http://www.iszhouhua.com/docker-deploy-mysql-messy-code.html
发表时间:2019-01-27 17:34
最后更新时间:2019-01-27 17:37
0条评论