mysql utf8mb4 造成慢_mysql使用utf8mb4经验吐血总结

1. utf8 与 utf8mb4 异同

1

2

3

4

The character set named utf8 uses a maximum of three bytes per character and contains only BMP characters. The utf8mb4 character set uses a maximum of four bytes per character supports supplementary characters:

- For a BMP character, utf8 and utf8mb4 have identical storage characteristics: same code values, same encoding, same length.

- For a supplementary character, utf8 cannot store the character at all, whereas utf8mb4 requires four bytes to store it. Because utf8 cannot store the character at all, you have no supplementary characters in utf8 columns and need not worry about converting characters or losing data when upgrading utf8 data from older versions of MySQL.

MySQL在 5.5.3 之后增加了 utf8mb4 字符编码,mb4即 most bytes 4。简单说 utf8mb4 是 utf8 的超集并完全兼容utf8,能够用四个字节存储更多的字符。

但抛开数据库,标准的 UTF-8 字符集编码是可以用 1~4 个字节去编码21位字符,这几乎包含了是世界上所有能看见的语言了。然而在MySQL里实现的utf8最长使用3个字节,也就是只支持到了 Unicode 中的 基本多文本平面(U+0000至U+FFFF),包含了控制符、拉丁文,中、日、韩等绝大多数国际字符,但并不是所有,最常见的就算现在手机端常用的表情字符 emoji和一些不常用的汉字,如 “墅” ,这些需要四个字节才能编码出来。

注:QQ里面的内置的表情不算,它是通过特殊映射到的一个gif图片。一般输入法自带的就是。

也就是当你的数据库里要求能够存入这些表情或宽字符时,可以把字段定义为 utf8mb4,同时要注意连接字符集也要设置为utf8mb4,否则在 严格模式 下会出现 Incorrect string value: /xF0/xA1/x8B/xBE/xE5/xA2… for column 'name'这样的错误,非严格模式下此后的数据会被截断。

提示:另外一种能够存储emoji的方式是,不关心数据库表字符集,只要连接字符集使用 latin1,但相信我,你绝对不想这个干,一是这种字符集混用管理极不规范,二是存储空间被放大(读者可以想下为什么)。

2. utf8mb4_unicode_ci 与 utf8mb4_general_ci 如何选择

字符除了需要存储,还需要排序或比较大小,涉及到与编码字符集对应的 排序字符集(collation)。ut8mb4对应的排序字符集常用的有 utf8mb4_unicode_ci、utf8mb4_general_ci,到底采用哪个在 stackoverflow 上有个讨论,What’s the difference between utf8_general_ci and utf8_unicode_ci

主要从排序准确性和性能两方面看:

准确性

utf8mb4_unicode_ci 是基于标准的Unicode来排序和比较,能够在各种语言之间精确排序

utf8mb4_general_ci 没有实现Unicode排序规则,在遇到某些特殊语言或字符是,排序结果可能不是所期望的。

但是在绝大多数情况下,这种特殊字符的顺序一定要那么精确吗。比如Unicode把ß、Œ当成ss和OE来看;而general会把它们当成s、e,再如ÀÁÅåāă各自都与 A 相等。

性能

utf8mb4_general_ci 在比较和排序的时候更快

utf8mb4_unicode_ci 在特殊情况下,Unicode排序规则为了能够处理特殊字符的情况,实现了略微复杂的排序算法。

但是在绝大多数情况下,不会发生此类复杂比较。general理论上比Unicode可能快些,但相比现在的CPU来说,它远远不足以成为考虑性能的因素,索引涉及、SQL设计才是。 我个人推荐是 utf8mb4_unicode_ci,将来 8.0 里也极有可能使用变为默认的规则。相比选择哪一种collation,使用者应该更关心字符集与排序规则在db里要统一就好。

这也从另一个角度告诉我们,不要可能产生乱码的字段作为主键或唯一索引。我遇到过一例,以 url 来作为唯一索引,但是它记录的有可能是乱码,导致后来想把它们修复就特别麻烦。

3. 怎么从utf8转换为utf8mb4

3.1 “伪”转换

如果你的表定义和连接字符集都是utf8,那么直接在你的表上执行

1

ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8mb4;

则能够该表上所有的列的character类型变成 utf8mb4,表定义的默认字符集也会修改。连接的时候需要使用set names utf8mb4便可以插入四字节字符。(如果依然使用 utf8 连接,只要不出现四字节字符则完全没问题)。

上面的 convert 有两个问题,一是它不能ONLINE,也就是执行之后全表禁止修改,有关这方面的讨论见 mysql 5.6 原生Online DDL解析;二是,它可能会自动该表字段类型定义,如 VARCHAR 被转成 MEDIUMTEXT,可以通过 MODIFY 指定类型为原类型。

另外 ALTER TABLE tbl_name DEFAULT CHARACTER SET utf8mb4 这样的语句就不要随便执行了,特别是当表原本不是utf8时,除非表是空的或者你确认表里只有拉丁字符,否则正常和乱的就混在一起了。

最重要的是,你连接时使用的latin1字符集写入了历史数据,表定义是latin1或utf8,不要期望通过 ALTER ... CONVERT ... 能够让你达到用utf8读取历史中文数据的目的,没卵用,老老实实做逻辑dump。所以我才叫它“伪”转换

3.2 character-set-server

一旦你决定使用utf8mb4,强烈建议你要修改服务端 character-set-server=utf8mb4,不同的语言对它的处理方法不一样,c++, php, python可以设置character-set,但java驱动依赖于 character-set-server 选项,后面有介绍。

同时还要谨慎一些特殊选项,如 遇到腾讯云CDB连接字符集设置一个坑。个人不建议设置全局 init_connect。

4. key 768 long 错误

字符集从utf8转到utf8mb4之后,最容易引起的就是索引键超长的问题。

对于表行格式是 COMPACT或 REDUNDANT,InnoDB有单个索引最大字节数 768 的限制,而字段定义的是能存储的字符数,比如 VARCHAR(200) 代表能够存200个汉字,索引定义是字符集类型最大长度算的,即 utf8 maxbytes=3, utf8mb4 maxbytes=4,算下来utf8和utf8mb4两种情况的索引长度分别为600 bytes和800bytes,后者超过了768,导致出错:Error 1071: Specified key was too long; max key length is 767 bytes。

COMPRESSED和DYNAMIC格式不受限制,但也依然不建议索引太长,太浪费空间和cpu搜索资源。

如果已有定义超过这个长度的,可加上前缀索引,如果暂不能加上前缀索引(像唯一索引),可把该字段的字符集改回utf8或latin1。

但是,( 敲黑板啦,很重要),要防止出现 Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation '=' 错误:连接字符集使用utf8mb4,但 SELECT/UPDATE where条件有utf8类型的列,且条件右边存在不属于utf8字符,就会触发该异常。表示踩过这个坑。

再多加一个友好提示:EXPLAIN 结果里面的 key_len 指的搜索索引长度,单位是bytes,而且是以字符集支持的单字符最大字节数算的,这也是为什么 INDEX_LENGTH 膨胀厉害的一个原因。

5. C/C++ 内存空间分配问题

这是我们这边的开发遇到的一个棘手的问题。C或C++连接MySQL使用的是linux系统上的 libmysqlclient 动态库,程序获取到数据之后根据自定义的一个网络协议,按照mysql字段定义的固定字节数来传输数据。从utf8转utf8mb4之后,c++里面针对character单字符内存空间分配,从3个增加到4个,引起异常。

这个问题其实是想说明,使用utf8mb4之后,官方建议尽量用 varchar 代替 char,这样可以减少固定存储空间浪费(关于char与varchar的选择,可参考 这里)。但开发设计表时 varchar 的大小不能随意加大,它虽然是变长的,但客户端在定义变量来获取数据时,是以定义的为准,而非实际长度。按需分配,避免程序使用过多的内存。

6. java驱动使用

Java语言里面所实现的UTF-8编码就是支持4字节的,所以不需要配置 mb4 这样的字眼,但如果从MySQL读写emoji,MySQL驱动版本要在 5.1.13 及以上版本,数据库连接依然是 characterEncoding=UTF-8 。

但还没完,遇到一个大坑。官方手册 里还有这么一段话:

1

2

3

4

Connector/J did not support utf8mb4 for servers 5.5.2 and newer.

Connector/J now auto-detects servers configured with character_set_server=utf8mb4 or treats the Java encoding utf-8 passed

using characterEncoding=... as utf8mb4 in the SET NAMES= calls it makes when establishing the connection. (Bug #54175)

意思是,java驱动会自动检测服务端 character_set_server 的配置,如果为utf8mb4,驱动在建立连接的时候设置 SET NAMES utf8mb4。然而其他语言没有依赖于这样的特性。

7. 主从复制报错

这个问题没有遇到,只是看官方文档有提到,曾经也看到过类似的技术文章。

大概就是从库的版本比主库的版本低,导致有些字符集不支持;或者人工修改了从库上的表或字段的字符集定义,都有可能引起异常。

8. join 查询问题

这个问题是之前在姜承尧老师公众号看到的一篇文章 MySQL表字段字符集不同导致的索引失效问题,自己也验证了一下,的确会有问题:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

CREATE TABLE t1 (

f_id varchar(20) NOT NULL,

f_action char(25) NOT NULL DEFAULT '' COMMENT '',

PRIMARY KEY (`f_id`),

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE t1_copy_mb4 (

f_id varchar(20) CHARACTER SET utf8mb4 NOT NULL,

f_action char(25) NOT NULL DEFAULT '' COMMENT '',

PRIMARY KEY (`f_id`),

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

1.

EXPLAIN extended select * from t1 INNER JOIN t1_copy_mb4 t2 on t1.f_id=t2.f_id where t1.f_id='421036';

2.

EXPLAIN extended select * from t1 INNER JOIN t1_copy_mb4 t2 on t1.f_id=t2.f_id where t2.f_id='421036';

对应上面1,2 的截图:

12606582.html

12606582.html

其中 2 的warnings 有convert:

(convert(t1.f_id using utf8mb4) = ‘421036’)

官网能找到这一点解释的还是开头那个地址:

1

2

3

4

Similarly, the following comparison in the WHERE clause works according to the collation of utf8mb4_col:

SELECT * FROM utf8_tbl, utf8mb4_tbl

WHERE utf8_tbl.utf8_col = utf8mb4_tbl.utf8mb4_col;

只是索引失效发生在utf8mb4列 在条件左边。(关于MySQL的隐式类型转换,见这里)。

9. 参考

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/505256.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java移动接口发短信_Java面试零碎知识点

1.Java文件经过JVM编译成字节码文件,即.class文件,将字节码文件在不同的操作系统中运行时,操作系统再将字节码文件编译成机器码文件。这就是Java跨平台2.首先明确一点,java 的 GC 回收是完全自动的,没有提供相关 api 手…

如何安装mysql5.7.21_Linux 安装MySql 5.7.21 操作步骤

一:到mysql官网下载最新的mysql包 mysql-5.7.21-linux-glibc2.12-x86_64二:在linux /usr/local/中解压mysql压缩包 改名为mysqlcd /usr/local/tar -xzvf mysql压缩包名三:创建用户组mysql,创建用户mysql并将其添加到用户组mysql中…

java重定向代码_Java程序员经典面试题集大全 (三十四)

341. Servlet API中forward() 与redirect()的区别?答:为实现程序的模块化,就需要保证在不同的Servlet之间可以相互跳转,而Servlet中主要有两种实现跳转的方式:FORWARD方式与redirect方式。 Forward() : 是服务器内部的…

mysql用户阻塞数_MySQL实例阻塞分析一例(线程statistics状态)

本文用实例来分析MySQL阻塞—线程statistics状态。一、 现象某日下午下班后低峰期,现网MySQL一个库突然报出大量慢sql,状态是 statistics,但是过后拿这些sql去执行的时候,实际很快。处于 statistics 状态的线程有个特征&#xff1…

无法获取未定义或 null 引用的属性“text”_【CSS】是时候开始用 CSS 自定义属性了...

自定义属性(有时候也被称作CSS变量或者级联变量)是由CSS作者定义的,它包含的值可以在整个文档中重复使用。由自定义属性标记设定值(比如:--main-color: black;),由var() 函数来获取值(比如:color: var(--main-color);)复杂的网站都…

斐波那契数列不用数组_兔子数列——斐波那契数列

相信人们都对斐波那契数列有或多或少的了解,如果没有,那你一定听过黄金分割比或是见过下面这种图片:斐波那契生活在十三世纪的意大利,原名列奥纳多皮萨诺(Leonardo Pisano),他出生在意大利那个后来因为伽里略做过自由落…

sqlserver服务启动失败_条码打印软件连接SQL数据库出现TCP连接失败解决办法

小编今天用条码打印软件连接SQL 数据库遇到了一个问题:通过端口1433连接到主机localhost的TCP/IP连接失败。错误:“Connection refused:connect。请验证连接属性。确保SQL Server的实例正在主机上运行,且在此端口接收TCP/IP连接&a…

python怎么播放mp3_python上播放mp3歌曲

试试这个。它过于简单但可能不是最好的方法。from pygame import mixer # Load the required librarymixer.init()mixer.music.load(e:/LOCAL/Betrayer/Metalik Klinik1-Anak Sekolah.mp3)mixer.music.play()请注意,支持MP3pip install pygame第二种方法试一试使用p…

python语言发明者 google_谷歌的语言何以战胜Python?50%的人都应该立即学习Golang...

全文共2623字,预计学习时长9分钟图源:dailybtc很多软件工程师视Python为最喜爱的语言之一,工作生活都和它死死绑定在一起。但Google, Uber, Dropbox, Soundcloud, Slack, 和Medium的软件工程师并不是这样。长期以来,顶级企业的程序…

python基础文档_python基本文件操作

python文件操作python的文件操作相对于java复杂的IO流简单了好多,只要关心文件的读和写就行了基本的文件操作要注意的是,当不存在某路径的文件时,w,a模式会自动新建此文件夹,当读模式,文件不存在&#xff0…

python如何计算整数和_python 整数和浮点数

Python支持对整数和浮点数直接进行四则混合运算,运算规则和数学上的四则运算规则完全一致。基本的运算:1 2 3 # > 64 * 5 - 6 # > 147.5 / 8 2.1 # > 3.0375使用括号可以提升优先级,这和数学运算完全一致,注意只能使用…

cocoscreator连接MySQL_CocosCreator幼麟麻将搭建步骤

我的客户端和服务器都是在本机Windows8上搭建的,软件环境为:MySQL:5.7Node.js:6.11.0CocosCreator:1.6.2步骤如下:一、服务器环境搭建1、安装MySql数据库(百度)2、登录MySql: mysql –u用户名 –p密码 (登录…

hikaricp mysql_使用 HikariCP 数据源

概述HikariCP是Spring Framework 5.0的默认 数据库连接池,这得益于他的高性能。但是如果配置不当,数据库连接池也可能因影响到系统性能。重要参数maximum-pool-sizeminimum-idlepool-nameauto-commitidle-timeoutmax-lifetimeconnection-timeout初始化过…

return两个返回值_异步函数的两个视角

我们来一起看一下两个程序员之间的故事。以下示例代码是用Scala写的,不过本文所讲的话题并不仅限于Scala,任何有Future/Promise支持的语言都是适用的。下面这个wiki页面罗列了各个有Future/Promise支持的语言,已经涵盖了大多数的常用语言。Fu…

python怎么控制速度_如何控制python的ThreadPoolExecutor的吞吐量速度?

我使用python的concurrent.futuresThreadPoolExecutor启动异步任务。按照this方法,我使用tqdm进度条监视异步调用的进度。在我的代码如下所示:with concurrent.futures.ThreadPoolExecutor(max_workers n_jobs) as executor:future_to_url {executor.s…

mysql57win10安装配置_Win10 OS安装(配置)MySQL 5.7(解压版)

Win10 OS安装(配置)MySQL 5.7(解压版)下载及解压文件名:mysql-5.7.27-win32.zipzip是解压版,msi是安装版,本教程仅说明zip格式的配置方法。解压(假设解压后根路径为D:\ide\mysql-5.7.27-win32)相关截图添加环境系统变量path 增加D:\ide\mysql…

mysql数据库更新数据库语句_MySQL数据库之UPDATE更新语句精解

UPDATE和REPLACE基本类似,但是它们之间有两点不同。1. UPDATE在没有匹配记录时什么都不做,而REPLACE在有重复记录时更新,在没有重复记录时插入。2. UPDATE可以选择性地更新记录的一部分字段。而REPLACE在发现有重复记录时就将这条记录彻底删除…

ubuntu安装python编译器_Ubuntu中安装VIM编辑器

Ubuntu安装好以后,默认是安装使用nano编辑器。不过这对于用惯了vim的人可能会有些不习惯。好在Ubuntu下安装vim还是比较简单的,使用如下命令即可:sudoapt-get install vim(apt-get install vim-full这下就好了 在输入 :syntax on 或者把/etc/…

pep8 python 编码规范_如何用好python编码规范,写一手漂亮的代码

前一段时间在编写python 代码的时候编辑器中一直在提示规范问题,因为强迫症的原因,我决定遵循python 的编码规范去编码,然后把需要注意的点记录下来, 帮助自己和大家一起成长。这是我的main.py文件中的一部分代码,经过…

MySQL故障检测_检测MySQL的表的故障的方法

表的故障检测和修正的一般过程如下:检查出错的表。如果该表检查通过,则完成任务,否则必须修复出错的数据库表。在开始修复之前对表文件进行拷贝,以保证数据的安全。开始修复数据库表。如果修复失败,从数据库的备份或更…