引言
上两篇讲到字符、字符集、字符编码,粗略的一笔带过MySQL的编码,本篇想要讲讲docker中的编码应用问题。遇到问题莫要慌乱阵脚,而是去认认真真的检查下,总有你意想不到的知识等着你。本文标题为mysql的字符集,其实在排查过程中发现是docker部署mysql后,编码在搞怪,而并非是哪一个的字符集引起的问题。
问题
实践环境
在安装好机器所需要的软件后,没有留意到还有这个问题。我生产的环境如下:
- 操作系统:Linux(Kylin V10 SP1 aarch64)GUN/Linux
- 容器环境:docker Version 24.0.2
- 应用软件:MySQL 8.0.31
遇到问题
使用docker在安装mysql 8 后,没有及时测试支持中文的情况。导致的后果有下面两个(目前我遇到的是这种问题)。
问题一
第一个是导致在数据被导入后,执行 select 查询时出现???等乱码;
mysql > select * from dict;
+-----+-----------+----------+--------+------------+
| id | name | value | dic_id | is_default |
+-----+-----------+----------+--------+------------+
| 1 | ??? | 1 | 1 | 1 |
| 2 | ??? | 2 | 1 | 1 |
| 3 | ??? | 3 | 1 | 1 |
| 4 | ??? | 1 | 2 | 1 |
+-----+-----------+----------+--------+------------+
mysql >
问题二
第二个是在粘贴 update,insert,create语句时,中文汉字没有显示出来,而是显示了 ""。
首先是我有一个创建表语句,如下:
drop table if exists `td_sys_dept`;
create table `td_sys_dept` (
dept_id bigint(20) not null auto_increment comment '部门id',
parent_id bigint(20) default 0 comment '父部门id',
ancestors varchar(50) default '' comment '祖级列表',
dept_name varchar(30) default '' comment '部门名称',
order_num int(4) default 0 comment '显示顺序',
leader varchar(20) default null comment '负责人',
phone varchar(11) default null comment '联系电话',
email varchar(50) default null comment '邮箱',
status char(1) default '0' comment '部门状态(0正常 1停用)',
del_flag char(1) default '0' comment '删除标志(0代表存在 2代表删除)',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
primaxx key (dept_id)
) engine=innodb auto_increment=100 comment = '部门表';
当我拷贝到docker容器启动的mysql后,发现字段的汉字备注以及表名称备注已经被替换为空字符串了,语句如下:

如果你有遇到类似的问题,那么本文可以带你解决这些问题。
思考分析
经过百度了解到在使用docker默认安装后,是不支持中文的。在外部使用rpm安装mysql后是可以使用中文,可以排除宿主机器编码问题,问题的矛头被指向了docker容器。这也印证了本段开头的描述——docker内部继承系统可能不支持中文的假设。提出假设后,引起乱码或者为空的情况可能有如下方向,检查并核对假设是否准确。
I 机器环境编码
【宿主机器当前使用语言】
$ locale # 宿主机器
LANG="zh_CN.UTF-8"
LC_COLLATE="zh_CN.UTF-8"
LC_CTYPE="zh_CN.UTF-8"
LC_MESSAGES="zh_CN.UTF-8"
LC_MONETARY="zh_CN.UTF-8"
LC_NUMERIC="zh_CN.UTF-8"
LC_TIME="zh_CN.UTF-8"
LC_ALL=
# 宿主机器当前支持语言:此处可以省略,不在浪费时间讲述,宿主机器都已经使用,肯定都是支持的。
【docker容器当前使用语言】
bash-4.4# locale # docker容器当前使用语言 LANG= LC_CTYPE="POSIX" LC_NUMERIC="POSIX" LC_TIME="POSIX" LC_COLLATE="POSIX" LC_MONETARY="POSIX" LC_MESSAGES="POSIX" LC_PAPER="POSIX" LC_NAME="POSIX" LC_ADDRESS="POSIX" LC_TELEPHONE="POSIX" LC_MEASUREMENT="POSIX" LC_IDENTIFICATION="POSIX" LC_ALL= bash-4.4# locale -a # docker容器当前支持语言 C C.utf8 POSIX bash-4.4#
🧠分析:宿主机器的编码中包含中文语言zh_CN.UTF-8,而在docker容器中的语言均为POSIX。在支持语言上,宿主机器已经支持很多中文,而在docker容器中,并没有中文语言的支持。此时可以增加语言支持即可。
II MySQL内部使用编码
查看当前MySQL的字符集,可以通过登录到MySQL后使用以下命令:
root $ docker exec -it mysql /bin/bash
Enter password:
mysql> show variables like '%character%';
+--------------------------+--------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
+--------------------------+--------------------------------+
8 rows in set (0.01 sec)
mysql>
查询出来的一些变量的含义:
- character_set_client:客户端字符集,也就是连接MySQL的客户端所使用的字符集;
- character_set_connection:连接字符集,也就是服务器与客户端之间的字符集;
- character_set_database:数据库字符集,新创建的数据库会按此字符集创建;
- character_set_filesystem:文件系统字符集,为二进制数据;
- character_set_results:结果字符集,SELECT查询返回的结果所使用的字符集;
- character_set_server:服务器字符集,MySQL服务器本身所使用的字符集。
- character_set_system:系统字符集,默认为UTF8;
🧠分析:在mysql的内部,客户端、连接器、查询展示数据的编码字符集都是latin1(默认情况下,MySQL的字符集是Latin1,也就是我们常说的ISO_8859_1,如果此处不是很理解,可以参考博主的其他相关博文),所以此时也是不支持中文的。
III docker容器连接编码
想到这个问题时,也是我最直观的解决问题的方式,因为问题的出现缘由是从docker开启,而非我宿主机器。
🧠分析:比较费脑筋,那就是需要再次查阅命令参数。
$ docker exec --help
Usage: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Execute a command in a running container
Aliases:
docker container exec, docker exec
Options:
-d, --detach Detached mode: run command in the background
--detach-keys string Override the key sequence for detaching a container
-e, --env list Set environment variables
--env-file list Read in a file of environment variables
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
-t, --tty Allocate a pseudo-TTY
-u, --user string Username or UID (format: "<name|uid>[:<group|gid>]")
-w, --workdir string Working directory inside the container
解决问题
我在上述分析的基础上进行测试,发现并没有按照我的预想生效,也就是说,上述的解决办法或多或少的解决我的问题。
1、临时方法(无需重启mysql)
此时我想着叫临时方法可能更好一些,这里主要是从问题本身出发来避免编码乱码。例如既然服务端、客户端、连接器的编码都是使用默认,那么我是否可以从连接时的参数选项入手来避免这个乱码问题。查询了一下资料,发现还是有解决的办法,并且发出来提供给大家参考下。
修改连接器连接方式,增加 -e LANG=C.UTF-8 选项,指定编码格式直接使用默认的shell命令进入到容器中的mysql,最后进行查询操作,汉字正常显示。
$ docker exec -it -e LANG=C.UTF-8 [容器名称或ID] mysql -uroot -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 19
Server version: 8.0.31 MySQL Community Server - GPL
Copyright (c) 2000, 2022, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use db_docker_chapter
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql>
mysql>
mysql> show create table td_sys_dept \G;
*************************** 1. row ***************************
Table: td_sys_dept
Create Table: CREATE TABLE `td_sys_dept` (
`dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id',
`parent_id` bigint DEFAULT '0' COMMENT '父部门id',
`ancestors` varchar(50) DEFAULT '' COMMENT '祖级列表',
`dept_name` varchar(30) DEFAULT '' COMMENT '部门名称',
`order_num` int DEFAULT '0' COMMENT '显示顺序',
`leader` varchar(20) DEFAULT NULL COMMENT '负责人',
`phone` varchar(11) DEFAULT NULL COMMENT '联系电话',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`status` char(1) DEFAULT '0' COMMENT '部门状态(0正常 1停用)',
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`dept_id`)
) ENGINE=InnoDB AUTO_INCREMENT=100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='部门表'
1 row in set (0.00 sec)
ERROR:
No query specified
mysql>
2、临时解决(无需重启mysql)
登录进入mysql服务端后,设置客户端、服务端、连接器的编码格式都为 utf8,然后检查编码是否正常显示。
mysql>
mysql> set character_set_connection = utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql>
mysql> set character_set_results = utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql>
mysql> set character_set_client = utf8;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql>
设置后,检查预测结果是否正确,检测结果如下,重新进入mysql服务端,汉字编码还存在错误:

3、完全解决(需要重启mysql)
(1)编辑配置文件
进入容器修改mysql的配置文件 my.cnf ,使用vim或vi进行编辑。
……
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
secure-file-priv= NULL
character-set-server=utf8
[mysql.server]
### 服务端默认编码
default-character-set = utf8
[mysqld_safe]
### 安全模式默认编码
default-character-set = utf8
[client]
### 客户端默认编码
default-character-set = utf8
……
编辑完成后,保存退出编辑,退出容器。
(2)重新启动mysql
重新启动mysql,使配置文件生效,使用docker命令重启启动容器的mysql。
### 使用docker命令重启启动容器的mysql
docker restart mysql
(3)检查编码情况
检查汉字已经正确显示。
4、临时解决(无需重启mysql)
修改docker容器的环境语言编码,并使生效。这里的问题是,当我们修改完环境语言编码支持中文后看到的登录用户也改为了 root@容器ID ,这里也说明一个问题,那就是为什么方法都是临时改变编码,一旦从容器中退出,然后再进去,编码集还原,需要再次使用 source /etc/profile 再次刷新才可。
bash-4.4# echo "export LANG=C.UTF-8" >>/etc/profile && source /etc/profile [root@9cd278ce1684 /]#
退出容器,重新进去mysql查看已经创建的表,乱码依旧存在。
当然了,你也可以让容器中自动刷新/etc/profile 文件,更方便操作,操作方式这行命令加到.zshrc(或.bashrc)中即可,这种解决方案可以理解为准解决。
总结
在实际的开发中遇到问题,无需慌忙,根据提示寻找答案,根据已有知识寻求解决问题途径。其实对于编码乱码问题,一般常用的解决办法或者能想到的解决办法,可以先去尝试,再来讨论复盘,最终形成自己的知识。无论是我们上述提到的哪一种解决方案,无非需要具有以下两种特点:1⃣️为什么,2⃣️如何解决。希望通过本文,可以快速了解docker部署mysql后出现编码问题应该如何解决。
[引用]
1、MySQL的字符集(一)
2、MySQL的字符集(二)




