一、问题描述
最近遇到问题,版本5.7线上访问is.columns出现大量的警告,我们进行了模拟,当然模拟只模拟了一个警告,如下,
二、问题分析
从报错来看其中有字节 \xBA\xC3 也就是0XBAC3无法被解析(这个码点实际上是GBK的'好'字),而对于 information_schema.columns的字段COLUMN_COMMENT字符集为UTF8类型,因此实际上就是0XBAC3无法被解析为UTF8字符,下面我将网上找到的UTF8编码的范围放出来如下,
可以看到不管是任何字节的UTF8编码中第一个字节,0xBA都不是其中任何字节编码的第一个字节,因此导致了这种报错,这个检测的代码在函数field_well_formed_copy_nchars中。报错的时候将无法解析为UTF8编码的二进制按照单个字节进行了输出,函数convert_to_printable负责完成这个输出动作,如下,
也就是我们看到的16进制的(\xBA\xC3 ) 。当然如果这里的GBK编码能够正常的解析为UTF8编码,但是实际上它并不是UTF8的编码的时候就会出现显示乱码,而不是报错。
但是这个报错一般是在插入数据的时候报错的,而这里并没有插入操作,那么我们需要简单看看在5.7中information_schema.columns到底是什么操作,实际上这个视图是一个memory表,当进行查询的时候其中的数据会进行填充,而不是一直保存在那里面的,其可能从frm文件和table share中读取信息,如果没有table share就需要打开frm文件获取了,既然要进行填充就有一个插入的过程,具体可以参考填充 information_schema.columns表的回调函数get_schema_column_record,也就是在这个插入的过程中进行了字符集编码的检测,导致了这个警告。当然这里我可以猜测当建表的时候对于comment 在5.7中并没有进行严格的编码检测,而是直接写入到了frm文件中。 其次访问直接information_schema.columns可能伴随着大量的打开表的过程,可能会对当前的table cache/table share cache等缓存造成冲击,我们可以使用where语句过滤掉不需要访问的表,减少冲击的可能。
三、测试方法和可能的原因
这里我们简单用一个C代码,输出以下就可以生成语句了,我们在comment中加入两个二进制的字符,并且这两个字符就是0xBAC3,实际上是'好'字的GBK编码,并且这是不能被UTF8直接解析的编码,
这种情况可能发生在注释的编码不正确,比如注释给的GBK的编码,并且set names 为UTF8,这样注释在没有任何转换的情况下进行了存储,也就是GBK编码当做UTF8编码进行了存储,刚好UTF8编码无法解析这个GBK的编码,如果这里我们使用GBK 显示就是发现这是0xBAC3 实际上是一个好字。
四、insert中的类似报错和8.0的测试
当然这个错误并不一定完全在这个场景下,更多可能是插入场景,比如我们如下测试,
mysql> CREATE TABLE `ttname` (
-> `name` varchar(20) DEFAULT NULL -> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;Query OK, 0 rows affected (0.11 sec)mysql> insert into ttname values('去');ERROR 1366 (HY000): Incorrect string value: '\xE5\x8E\xBB' for column 'name' at row 1
而这里我输入的是UTF8的去字,其编码就是0XE58EBB,但是这种汉字是不可能转换为latin1字符集的,虽然latin1为单字节,但是在校验的时候会使用unicode编码进行校验,这样就会出现报错,因为latin1无法解析unicode编码21435(去字的unicode码点),
上面的案例 8.0中测试,发现有了变化,当建立的时候就会抛出错误警告,并且出现乱码,代表无法识别comment的字符集。如下,
因此我们在建表等操作的时候一定要注意到comment的字符集是否正确,建议输入源的字符集无脑UTF8即可,并且set names中的3个字符集都选用UTF8(UTF8MB4)。
其他
字典信息始终使用utf8进行存储,受到set names的影响,比如comment为gbk编码,那么set names也要使用gbk编码,否则会出现不能识别的情况。
同字符集检测字符是否符合字符集标准,也就是set names和字段的字符集相同
不同字符集转换为unicode然后进行转换,转换的时候检测字符集是否符合标准,也就是set names和字段的字符集不同。
set names 更改接口
set_var_collation_client::update
thd->variables.character_set_client= character_set_client;
thd->variables.character_set_results= character_set_results;
thd->variables.collation_connection= collation_connection;
->charset_is_system_charset
分别和
charset_is_system_charset = String::needs_conversion(0, variables.character_set_client, system_charset_info,¬_used);
charset_is_collation_connection = !String::needs_conversion(0,variables.character_set_client, variables.collation_connection,¬_used);
charset_is_character_set_filesystem = !String::needs_conversion(0,variables.character_set_client,variables.character_set_filesystem,¬_used);
->转换关键函数
field_well_formed_copy_nchars to_cs from_cs ->well_formed_copy_nchars to_cs from_cs
重点进行转换
my_mb_wc_utf8mb4
my_wc_mb_utf8mb4
my_mb_wc_gbk 就是用来实现讲gbk字符转换成unicode字符的函数
my_wc_mb_gbk 函数则是用来讲unicode字符转换成gbk字符的函数。
push_warning_printf
存储过程,Default_object_creation_ctx::change_env----- 其他案例 1[root@gdb5722 tmp]# more sql2.sql
insert into testooo1 values('t); //gbk 编码 好 UTF8显示错误无法复制这里是一个好字的GBK编码。't好t'。mysql> set names utf8;Query OK, 0 rows affected (0.00 sec)mysql> source /tmp/sql2.sql
ERROR 1366 (HY000): Incorrect string value: '\xBA\xC3t' for column 'name' at row 1mysql> show create table testooo1 \G*************************** 1. row ***************************
Table: testooo1
Create Table: CREATE TABLE `testooo1` (
`name` varchar(20) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb41 row in set (0.00 sec)mysql> set names gbk -> ;Query OK, 0 rows affected (0.00 sec)mysql> source /tmp/sql2.sql
Query OK, 1 row affected (0.00 sec)可以看到当以GBK 当做UTF8 插入到UTF8表中的时候还是要做UTF8编码的检测的,但是‘去’ 字的GBK 却能插入,因为他是UTF8编码的,检测通过
mysql> insert into test.testooo1 values('去'); Query OK, 1 row affected (0.00 sec)mysql> select hex(name) from test.testooo1;+------------+| hex(name) |+------------+| C8A5 |+------------+6 rows in set (0.01 sec)----- 其他案例 2comment 如果是GBK编码那么set names 要设置为GBK 这样 转码能够成功,
写入
GBK -> GBK -> UTF8
输出
UTF8->GBK ->GBK
或者输出
UTF8->GBK ->GBK
mysql> select hex(COLUMN_COMMENT) from information_schema.columns where table_schema='t1' and table_name='test1' \G*************************** 1. row ***************************hex(COLUMN_COMMENT): 74E5A5BD741 row in set (0.00 sec)mysql> select COLUMN_COMMENT from information_schema.columns where table_schema='t1' and table_name='test1' \G
*************************** 1. row ***************************COLUMN_COMMENT: t好t1 row in set (0.00 sec)----- TODO:另外一个案例5.7/8.0mysql> show create table tm1123 \G*************************** 1. row ***************************
Table: tm1123
Create Table: CREATE TABLE `tm1123` (
`name` varchar(20) DEFAULT NULL) ENGINE=InnoDB DEFAULT CHARSET=utf8mb41 row in set (0.00 sec)set names gbk;
mysql> insert into tm1123(name) values('去');ERROR 1406 (22001): Data too long for column 'name' at row 1mysql> show variables like '%char%';+--------------------------+----------------------------------------+| Variable_name | Value |+--------------------------+----------------------------------------+| character_set_client | gbk || character_set_connection | gbk || character_set_database | utf8mb4 || character_set_filesystem | binary || character_set_results | gbk || character_set_server | utf8mb4 || character_set_system | utf8 || character_sets_dir | /opt/mysql5740/install/share/charsets/ |+--------------------------+----------------------------------------+可能原因,讲UTF8 当做GBK去解析,UTF8中文3个字节,GBK解析2个字节后发现还存在一个字节,因此这个字符过长。
UTF8->GBK->UTF8
#0 my_wc_mb_gbk (cs=0x2c47940 <my_charset_gbk_chinese_ci>, wc=21435, s=0x7fff4c01b071 "def\002t1\006tm1123\006tm1123\004name\004name\f\034",
e=0x7fff4c01b077 "\006tm1123\006tm1123\004name\004name\f\034") at /opt/mysql-5.7.40/strings/ctype-gbk.c:10695#1 0x0000000001da7542 in my_convert_internal (to=0x7fff4c01b071 "def\002t1\006tm1123\006tm1123\004name\004name\f\034", to_length=6, to_cs=0x2c47940 <my_charset_gbk_chinese_ci>,
from=0x7fff4c3d581d "", from_length=3, from_cs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>, errors=0x7fff705d7684) at /opt/mysql-5.7.40/strings/ctype.c:1006#2 0x0000000001da7680 in my_convert (to=0x7fff4c01b071 "def\002t1\006tm1123\006tm1123\004name\004name\f\034", to_length=6, to_cs=0x2c47940 <my_charset_gbk_chinese_ci>, from=0x7fff4c3d581a "去",
from_length=3, from_cs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>, errors=0x7fff705d7684) at /opt/mysql-5.7.40/strings/ctype.c:1082#3 0x0000000000ead792 in copy_and_convert (to=0x7fff4c01b071 "def\002t1\006tm1123\006tm1123\004name\004name\f\034", to_length=6, to_cs=0x2c47940 <my_charset_gbk_chinese_ci>,
from=0x7fff4c3d581a "去", from_length=3, from_cs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>, errors=0x7fff705d7684) at /opt/mysql-5.7.40/include/sql_string.h:135#4 0x000000000143ff1f in Protocol_classic::net_store_data (this=0x7fff4c012c68, from=0x7fff4c3d581a "去", length=3, from_cs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>,
to_cs=0x2c47940 <my_charset_gbk_chinese_ci>) at /opt/mysql-5.7.40/sql/protocol_classic.cc:116#5 0x00000000014425f0 in Protocol_classic::store_string_aux (this=0x7fff4c012c68, from=0x7fff4c3d581a "去", length=3, fromcs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>,
tocs=0x2c47940 <my_charset_gbk_chinese_ci>) at /opt/mysql-5.7.40/sql/protocol_classic.cc:1285#6 0x00000000014427f2 in Protocol_text::store (this=0x7fff4c012c68, from=0x7fff4c3d581a "去", length=3, fromcs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>,
tocs=0x2c47940 <my_charset_gbk_chinese_ci>) at /opt/mysql-5.7.40/sql/protocol_classic.cc:1314#7 0x000000000144482e in Protocol_text::store (this=0x7fff4c012c68, from=0x7fff4c3d581a "去", length=3, cs=0x2cb0ba0 <my_charset_utf8mb4_general_ci>)
at /opt/mysql-5.7.40/sql/protocol_classic.h:229
编辑推荐:
- 主从从库MTS HANG死一列03-01
- 慢查询中关于MDL LOCK记录的变化03-01
- 主从半同步降级异步分析03-01
- 一个古老的死锁BUG终于修复了03-01
- mysql面试03-01
- 如何让SQL速度飞起来 入门YashanDB优化器03-01
- 数智化转型 | 星环科技Defensor 助力某银行数据分类分级03-01
- 刚刚!MySQL创新版9.2.0发布,学爆它!03-01
相关推荐
-
雷神推出 MIX PRO II 迷你主机:基于 Ultra 200H,玻璃上盖 + ARGB 灯效
2 月 9 日消息,雷神 (THUNDEROBOT) 现已宣布推出基于英
-
制造商 Musnap 推出彩色墨水屏电纸书 Ocean C:支持手写笔、第三方安卓应用
2 月 10 日消息,制造商 Musnap 现已在海外推出一款 Oce
热文推荐
- 主从从库MTS HANG死一列
主从从库MTS HANG死一列
26-03-01 - 慢查询中关于MDL LOCK记录的变化
慢查询中关于MDL LOCK记录的变化
26-03-01 - 主从半同步降级异步分析
主从半同步降级异步分析
26-03-01 - mysql面试
mysql面试
26-03-01 - 如何让SQL速度飞起来 入门YashanDB优化器
如何让SQL速度飞起来 入门YashanDB优化器
26-03-01 - 数智化转型 | 星环科技Defensor 助力某银行数据分类分级
数智化转型 | 星环科技Defensor 助力某银行数据分类分级
26-03-01 - 刚刚!MySQL创新版9.2.0发布,学爆它!
刚刚!MySQL创新版9.2.0发布,学爆它!
26-03-01 - 【SQLAOT】一个用来辅助SQL优化工作的工具
【SQLAOT】一个用来辅助SQL优化工作的工具
26-03-01 - 开发一个集婚恋相亲、交友、多人语音互动于一体的APP系统
开发一个集婚恋相亲、交友、多人语音互动于一体的APP系统
26-03-01 - 第26期 MySQL区分大小写
第26期 MySQL区分大小写
26-03-01
