美文网首页
访问字典is.columns出现大量的警告(字符集相关)

访问字典is.columns出现大量的警告(字符集相关)

作者: 重庆八怪 | 来源:发表于2024-10-22 18:09 被阅读0次

一、问题描述

最近遇到问题,版本5.7线上访问is.columns出现大量的警告,我们进行了模拟,当然模拟只模拟了一个警告,如下,

image.png

二、问题分析

从报错来看其中有字节 \xBA\xC3 也就是0XBAC3无法被解析(这个码点实际上是GBK的'好'字),而对于 information_schema.columns的字段COLUMN_COMMENT字符集为UTF8类型,因此实际上就是0XBAC3无法被解析为UTF8字符,下面我将网上找到的UTF8编码的范围放出来如下,


image.png

可以看到不管是任何字节的UTF8编码中第一个字节,0xBA都不是其中任何字节编码的第一个字节,因此导致了这种报错,这个检测的代码在函数field_well_formed_copy_nchars中。报错的时候将无法解析为UTF8编码的二进制按照单个字节进行了输出,函数convert_to_printable负责完成这个输出动作,如下,


image.png

也就是我们看到的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直接解析的编码,


image.png
image.png

这种情况可能发生在注释的编码不正确,比如注释给的GBK的编码,并且set names 为UTF8,这样注释在没有任何转换的情况下进行了存储,也就是GBK编码当做UTF8编码进行了存储,刚好UTF8编码无法解析这个GBK的编码,如果这里我们使用GBK 显示就是发现这是0xBAC3 实际上是一个好字。

image.png

四、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码点),


image.png

上面的案例 8.0中测试,发现有了变化,当建立的时候就会抛出错误警告,并且出现乱码,代表无法识别comment的字符集。如下,


image.png
image.png

因此我们在建表等操作的时候一定要注意到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,&not_used);
        charset_is_collation_connection = !String::needs_conversion(0,variables.character_set_client, variables.collation_connection,&not_used);
        charset_is_character_set_filesystem = !String::needs_conversion(0,variables.character_set_client,variables.character_set_filesystem,&not_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 1
mysql> show create table testooo1 \G
*************************** 1. row ***************************
       Table: testooo1
Create Table: CREATE TABLE `testooo1` (
  `name` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 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)

----- 其他案例 2

comment 如果是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): 74E5A5BD74
1 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好t
1 row in set (0.00 sec)


----- TODO:另外一个案例

5.7/8.0

mysql> show create table tm1123 \G
*************************** 1. row ***************************
       Table: tm1123
Create Table: CREATE TABLE `tm1123` (
  `name` varchar(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
1 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 1
mysql> 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




相关文章

  • Python Error:TypeError: string i

    原因: 由于当前的’字典’并不是字典,而是字符串。导致了拿字典的访问方法去访问字符串就会出现访问字典的 下标,字典...

  • 字典

    Python的字典 字典:在python中,字典是一系列键-值对。每个键都与一个值相关联,可以使用键来访问与之相关...

  • Python基础学习day2|字典

    字典 字典及其使用 在Python中,字典是一系列键—值对。每个键都与一个值相关联,你可以使用键来访问与之 相关联...

  • Python中字典用法完全解读 !

    在本文中,你将学习能够将相关信息关联起来的Python字典。学习如何访问和修改字典中的信息,鉴于字典可储存的信息量...

  • Python入门之字典

    使用字典 在Python中,字典是一系列键-值对。每个键都与一个值相关联,可以使用键来访问与之相关联的值。与键相关...

  • redis出现Lettuce/HGETALL,系统访问卡顿

    一、访问卡顿 目前通过skywalking发现,redis出现大量的Lettuce/HGETALL访问,导致系统访...

  • Python系列5-Python字典介绍

    一.字典介绍 在Python中,字典 是一系列键—值对 。每个键 都与一个值相关联,你可以使用键来访问与之相关联的...

  • 警告⚠️:method '-top' in category f

    产生原因:最近项目突然出现了大量此类型警告,原因竟是Masonry里View+MASShorthandAdditi...

  • urllib.parse

    转换相关 .quote() 编码已构造查询字符串 .urlencode() 编码字典表 需要POST访问时需要...

  • 应对接口级故障:服务降级、熔断、限流、排队

    接口级故障:系统没有宕机、网络没有中断,但是业务却出现了问题:业务响应慢、大量访问超时、大量访问异常。本质:系统负...

网友评论

      本文标题:访问字典is.columns出现大量的警告(字符集相关)

      本文链接:https://www.haomeiwen.com/subject/ysrrdjtx.html