暂无图片
暂无图片
7
暂无图片
暂无图片
暂无图片

开发实践|MySQL的字符集(三)

453

引言

上两篇讲到字符、字符集、字符编码,粗略的一笔带过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后,发现字段的汉字备注以及表名称备注已经被替换为空字符串了,语句如下:
image.png

如果你有遇到类似的问题,那么本文可以带你解决这些问题。

思考分析

经过百度了解到在使用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服务端,汉字编码还存在错误
image.png

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的字符集(二)

3、小白如何学习MySQL配置文件

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论