一、故事背景

记一次 sql_mode 非严格模式下的业务事故排查。当时数据库没有开启 sql_mode 为严格模式,并且数据表的编码是 utf8,表现为业务侧的 Insert SQL 语句执行成功,但是,

查询表记录的时候,发现字段的数据值缺失。示例:写入一条有特殊字符   的记录,记录里面字段值在 之后的字符都丢失了。

下面是,开启了严格模式:

问题原因定位到后,解决方案是,在不对数据库做任何配置调整的前提下,业务逻辑中增加对特殊字符的检测,过滤掉数据库不支持的特殊字符,从而杜绝写入数据表后出现数据缺失的事故。

那么,哪些字符是 MySQL 不支持的嘞?由此引出本文的探讨主题。

二、认识 MySQL UTF8 字符集

我们带着两个问题,去调研 MySQL 5.7 版本 UTF8 字符集。

2.1. MySQL 不支持的特殊字符有哪些?

PS: 这里贴的 MySQL 官方文档也是 5.7。

从文档提取下关键信息:

  • 在 MySQL 中 utf8 是 utf8mb3 的别名
  • utf8mb3 编码的每个字符最多三个字节

示例:特殊字符  特殊字符:

可以观察到这个字符,需要使用四个字节编码,因此这个字符不能被数据库 utf8mb3 编码支持。

说点题外话,在 Java 中 String 是 UTF-16 格式的,当我们用鼠标复制 字符到一个双引号中时,idea 编辑器,会自动转换为这样的格式:

那么,MySQL 的 utf8mb3 不支持哪些字符 ?

继续看 MySQL官方文档

可以看到,文档中已经给出了比较明确的描述:

  • 仅支持 BMP 字符
  • 一个字符的编码最多三个字节。

到这里,你可能又会问是什么 BMP 字符嘞,Wiki 百科看不懂啊!

在介绍这个问题之前,首先要了解一点基础知识 Code point

大家应该都认识这张表,ASCLL 包含 128 个 Code point 表示 128 个字符(也就是 0 ~ 127)。

在标准的 Unicode 中容纳了 1,114,112 code points,其中前 65,536 个 Code point (也就是 0 ~ 65535)称为 Basic Multilingual Plane(缩写:BMP

  • 查看一个字符的 Code point 可以使用 charbase.com,示例,查看大写字母 A :

  • 判断一个字符是否是 BMP

    首先计算出字符的 Code point,然后检查其范围,如果在 0 ~ 65535 内,就是 BMP 字符。

2.2. MySQL UTF8 和 标准 UTF-8 编码是一个概念吗?

通过上一个问题,我们了解到,MySQL 5.7 版本中 UTF8utf8mb3 的别名,utf8mb3 是使用 1 ~ 3 个字节对 Unicode 字符进行编码,仅支持 BMP 字符。

在 Wiki 百科里面对 UTF-8 的定义是:

简言之:使用 1 ~ 4 个字节对标准 Unicode 1,112,064 个有效的字符 Code point 进行编码。

因此,这两个 utf8 在不同的上下文背景下不是一个概念,很多开发人员包括我,经常在没有对事物做详细调研之前,凭借主观经验对事物妄下结论。

三、编程语言最佳实践

通过上面分析,我们知道问题的背景和原因。下面的给出最佳编程实践,选取前/后端使用的两门语言:

3.1. 在 Java 语言中检测字符串中的非 BMP 字符

public class Main {

    public static void main(String[] args) {
String str = "方程";
boolean contain = isContainsNonBmpUnicodeCharacter(str);
if (contain) {
System.out.println("The string contains non-BMP Unicode character.");
}
} private static boolean isContainsNonBmpUnicodeCharacter(String str) {
return str.length() != str.codePointCount(0, str.length());
}
}

3.2.在 Javascript 中检测字符串中非 BMP 字符

function main() {
let str = "方程";
let contains = isContainsNonBmpUnicodeCharacter(str);
if (contains) {
console.log("The string contains non-BMP Unicode character.");
}
} function isContainsNonBmpUnicodeCharacter(str) {
return str.split(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g).length != 1;
}

参考文献

  1. U+1D465: MATHEMATICAL ITALIC SMALL X - charbase.com

  2. How to convert a byte array to a hex string in Java? - stackoverflow.com

  3. Check if a String contains characters which aren't UTF-8 encoded in Java - stackoverflow.com

  4. Unicode Character Sets

  5. The utf8mb3 Character Set (3-Byte UTF-8 Unicode Encoding)

  6. Basic Multilingual Plane - wikipedia.org

  7. Code point - wikipedia.org

  8. How do I convert unicode codepoints to their character representation?

  9. UTF-8 - wikipedia.org

  10. JavaScript strings outside of the BMP - stackoverflow.com

MySQL 5.7 版本的 UTF8 字符集调研的更多相关文章

  1. MySQL字符集 GBK、GB2312、UTF8区别 解决 MYSQL中文乱码问题 收藏 MySQL中涉及的几个字符集

    MySQL中涉及的几个字符集 character-set-server/default-character-set:服务器字符集,默认情况下所采用的.character-set-database:数据 ...

  2. mysql 8 windows 版本zip方式安装步骤

    mysql 8 windows 版本zip方式安装步骤(下载地址:https://dev.mysql.com/downloads/mysql/)1,解压ZIP文件到指定目录下:如D:\mysql-8. ...

  3. MySQL数据库各个版本的区别

    MySQL数据库各个版本的区别 MySQL数据库 MySQL是一种开放源代码的关系型数据库管理系统(RDBMS),MySQL数据库系统使用最常用的数据库管理语言--结构化查询语言(SQL)进行数据库管 ...

  4. utf8字符集下的比较规则

    前言: 在MySQL中,比较常用的字符集是utf8和utf8mb4.这两个字符集是类似的,utf8是utf8mb3的别名,所以之后在MySQL中提到utf8就意味着使用1~3个字节来表示一个字符,如果 ...

  5. ubuntu14.04中mysql的安裝及utf8编码集配置

    mysql的安裝使用sudo apt-get install mysql-server即可安裝,我安裝的是5.6版.安装过程中会要求输入root账户的密码,按提示输入即可. Mysql Workben ...

  6. Mysql修改已有数据的字符集

    Mysql修改已有数据的字符集 问题 在生产环境中跑了很久,发现MysqlClient连接的字符集是默认的latin1,我们一直以为都是utf8,造成这样的误解,是因为在内网环境中,我们是源码编译的M ...

  7. MySQL 8.0版本连接报错:Could not create connection to database server.

    准备搭建一个Spring Boot 组合mybatis的项目,数据库采用的是MySQL 8.0.11按照以往的配置,使用插件mybatis-generator-maven-plugin生成代码时,一直 ...

  8. Linux下修改MySQL数据库字符编码为UTF-8解决中文乱码

    由于MySQL编码原因会导致数据库出现乱码. 解决办法: 修改MySQL数据库字符编码为UTF-8,UTF-8包含全世界所有国家需要用到的字符,是国际编码. 具体操作: 1.进入MySQL控制台 &g ...

  9. centos 安装 mysql(指定安装版本)

    第一步: 下载 mysql 包 第二步:   rpm -Uvh mysql文件名.rpm ,这里是 rpm 其实不是安装mysql ,而是安装了一个mysql 的 yum 源 仓库 /etc/yum. ...

随机推荐

  1. k8s-statefulset

    1. 简介 StatefulSet 是用来管理有状态应用的工作负载Api对象. StatefulSet 用来管理某 Pod 集合的部署和扩缩, 并为这些 Pod 提供持久存储和持久标识符. 和 Dep ...

  2. CF637B Chat Order 题解

    Content 有 \(n\) 个字符串,每次出现这个单词就把这个单词放到队列的队首(若已经出现就把原队列里面的那个单词提到队首),求最后的队列由队首到队尾的元素依次是多少. 数据范围:\(1\leq ...

  3. 复杂SQL案例:用户听课情况查询

    供参考: select h.course_id, h.course_type, i.course_title, r.id res_id, r.res_title, h.user_id, u.user_ ...

  4. JS验证身份证是否符合规则

    调用isIdCardNo(num)  验证通过返回true 错误返回false function isIdCardNo(num) { var factorArr = new Array(7, 9, 1 ...

  5. 【LeetCode】448. Find All Numbers Disappeared in an Array 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 方法一:暴力求解 方法二:原地变负做标记 方法三:使用set ...

  6. 【LeetCode】359. Logger Rate Limiter 解题报告(C++)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 字典 日期 题目地址:https://leetcode ...

  7. 【LeetCode】945. Minimum Increment to Make Array Unique 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 暴力求解,TLE 一次遍历 日期 题目地址:http ...

  8. 【LeetCode】906. Super Palindromes 解题报告(Python)

    作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 BFS解法 相似题目 参考资料 日期 题目地址:ht ...

  9. 重重封锁,让你一条数据都拿不到《死磕MySQL系列 十三》

    在开发中有遇到很简单的SQL却执行的非常慢,甚至只查询一行数据. 咔咔遇到的只有两种情况,一种是MySQL服务器CPU占用率很高,所有的SQL都执行的很慢直到超时,程序也直接502,另一种情况是行锁造 ...

  10. sql-labs 1-14

    less-1: 1.采用二分法进行猜列: http://192.236.147.191:30000/Less-1/?id=1' order by 10--+ Welcome    Dhakkan Un ...