引言
今天遇到一个问题,我们在设计一个User表时,用户名需要保证唯一,且不区分大小写。开始设计是两个字段user_name(原始值),user_name_upcase(转大写值),这两个字段上都唯一索引。直到有位同学告诉我们可通过一个字段搞定,学习了,学习了。
字符集&校对规则
MySQL提供了字符集(character set)支持,可以使用各种字符集存储数据并根据各种校对规则(collations)进行比较。可以在server, database, table, column 指定字符集和校对规则。
什么是字符集?
简单来说,字符集就是符号和编码的集合。举个例子,假设我们有四个字母A,B,a,b。我们给每一个字符一个数值:A=0,B=1,a=2,b=3。A就是符号,数值0就是A的编码,字符和数字的编码规则的合集就是字符集。
什么是校对规则?
简单来说,是字符之间的比较规则,决定字符相等和排序等。接着上面的例子,比较符号A,B的大小,我们可以简单的认为A < B,因为0 < 1,我们称这种规则为binary collation。但是,当我们需要认为A=a,B=b 即忽略大小写时,我们成为case-insensitive collation。
读懂字符集
查看当前数据库支持哪些字符集(数据库版本8.0)
mysql> SHOW CHARACTER SET;
+----------+---------------------------------+---------------------+--------+
| Charset | Description | Default collation | Maxlen |
+----------+---------------------------------+---------------------+--------+
| big5 | Big5 Traditional Chinese | big5_chinese_ci | 2 |
| binary | Binary pseudo charset | binary | 1 |
...
| latin1 | cp1252 West European | latin1_swedish_ci | 1 |
...
| ucs2 | UCS-2 Unicode | ucs2_general_ci | 2 |
...
| utf8 | UTF-8 Unicode | utf8_general_ci | 3 |
| utf8mb4 | UTF-8 Unicode | utf8mb4_0900_ai_ci | 4 |
...
utf8 这个是mysql历史遗留的一个utf8编码字符集,最大长度是3个字节,而真正的utf8编码是最长4个字节。
utf8mb4 真正的utf8编码字符集,可以完美支持所有的utf8编码,推荐使用。
查看utf8mb4支持哪些校对规则
mysql> SHOW COLLATION WHERE Charset = 'utf8mb4';
+----------------------------+---------+-----+---------+----------+---------+---------------+
| Collation | Charset | Id | Default | Compiled | Sortlen | Pad_attribute |
+----------------------------+---------+-----+---------+----------+---------+---------------+
| utf8mb4_0900_ai_ci | utf8mb4 | 255 | Yes | Yes | 0 | NO PAD |
| utf8mb4_bin | utf8mb4 | 46 | | Yes | 1 | PAD SPACE |
| utf8mb4_general_ci | utf8mb4 | 45 | | Yes | 1 | PAD SPACE |
....
校对规则名称后缀含义表
后缀 | 含义 | 说明 |
---|---|---|
_ai | Accent-insensitive | 重音不敏感,多见于德语 |
_as | Accent-sensitive | 重音敏感 |
_ci | Case-insensitive | 大小写不敏感 |
_cs | Case-sensitive | 大小写敏感 |
_ks | Kana-sensitive | |
_bin | Binary | 二进制比较 |
使用字符集
例如,我们创建如下表
CREATE TABLE `test_charset` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`text_bin` VARCHAR(45) COLLATE UTF8MB4_BIN NOT NULL,
`text_ci` VARCHAR(45) COLLATE UTF8MB4_GENERAL_CI DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_text_ci` (`text_ci`)
) ENGINE=INNODB AUTO_INCREMENT=10 DEFAULT CHARSET=UTF8MB4 COLLATE = UTF8MB4_BIN;
实际上,可以在服务器,数据库,表,字段上使用不同的字符集和校对规则,针对我们这种忽略大小写的需求,可以单独指定该列的校对规则为UTF8MB4_GENERAL_CI。
但是,没有特殊情况,建议还是一个数据库统一用一套配置,多了你自己不心慌吗?
UTF8MB4_GENERAL_CI 实现的源码分析
很好奇mysql里面到底是怎么处理忽略大小写这件事情的,这个就得看下mysql源码了,mysql源码地址:https://github.com/mysql/mysql-server,直接到最后ctype-utf8.cc这个源码文件。
通过mysql的源码,我们可以看到mysql服务内部会都转成小写进行比较。
总结
mysql包含的东西太多,多学习吧。
参考文档
https://github.com/mysql/mysql-server/blob/8.0/strings/ctype-utf8.cc
https://dev.mysql.com/doc/refman/8.0/en/charset.html
https://dev.mysql.com/doc/refman/8.0/en/charset-collation-names.html
https://dev.mysql.com/doc/refman/8.0/en/charset-mysql.html