Docker部署时Mysql导入初始数据乱码问题

2019-01-27 3716 ℃

前言

近几天在研究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-servercollation-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默认字符集确实已改为了utf8utf8mb4,但是执行脚本导入的数据依然是乱码。

我以为是我的导入方式不对,然后换成通过自定义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