淘客熙熙

主题:【求助】mysql 里面邪门的 utf8mb4 问题 -- 铁手

共:💬15 🌺27 新:
全看树展主题 · 分页
家园 【求助】mysql 里面邪门的 utf8mb4 问题

网站数据库里原来是用的GBK,一直也没什么问题。现在为了支持 emoji,比如 😀 😁 😂 🤣 😃 😄 😅 😆 😉,所以要改为 utf8mb4。

大部分还算顺利,但是在这个过程中碰到一个很邪门的问题。

主要是中文里的全角字符,比如这个全角的数字:203和普通数字 203,在GBK中算是不同的东西,但是在 utf8mb4 里面变成了一样的东西。至少是在作 index 的时候,是同等看待,于是就造成了原来是 unique 的,变得重复了。

试验了一下 utf8 编码,也是同样的问题。

难道说在 utf8 中全角的概念?还是说 mysql 在处理字符集的时候有错误?

关键词(Tags): #utf8mb4#mysql
家园 这个供你参考,也许有用

https://www.v2ex.com/t/662096

家园 国内都是用utf8genic的
家园 应该指定collation为utf8mb4_bin

alter table t_unicode convert to character set utf8mb4 collate utf8mb4_bin

除了编码之外,collation决定如何比较字符串和顺序,有多个变种。用错了会造成不能区分大小写全半角,只考虑语意一致性。

https://zudoh.com/mysql/should-use-collation-utf8mb4_bin-as-default

家园 假设转换没出问题,就是collation的问题

现在的比较的没开width sensitive

https://dev.mysql.com/doc/refman/8.0/en/charset-asian-sets.html

改成

gbk_bin试试?

不行了再上

utf8mb4_bin

家园 多谢各位帮忙,有所改进,但问题依旧存在。

这里汇报一下测试结果,看看大家有没有什么想法。@西电鲁丁 @葡萄干 @骆筱 @shinji

首先需要确认的是,内容保存没有问题。输入什么,保存的也是什么。毛病出在索引上。

Collation最早是用 gbk_chinese_ci

以下是直接在库结构上改(不知道 DUMP 出来再 IMPORT 进去会不会有区别)的情况。

如果改成 utf8mb4_general_ci 则全角和半角字符不再被同等看待,解决部分问题。但是出现 e 等同于 é 的情况。以及两个历史遗留的乱码变成等同。

_ci 的意思是 case insensitive,所以这可能就是看似不同,但是被当作相同的原因。

如果改成 utf8mb4_bin 则上述 全角字符问题,e 等同于 é 这两种情况不再存在。那两个乱码问题依旧存在。

现在看来,大概就是只能用 utf8mb4_bin,然后手工修改那两个乱码。

邪门的是,这两个乱码我怎么看都是一样的,不知道在 gbk_chinese_ci 的时候,为什么会被当作不一样。

家园 感觉已经踏进了mysql quirk的领域

https://dev.mysql.com/doc/refman/8.0/en/faqs-cjk.html#faq-cjk-how-cjk-sort-unicode-1

建议用utf8mb4_ja_0900_as_cs能解决老版本遗留的问题

不过怎么看都是针对日语的,有兴趣可以试试utf8mb4_zh_0900_as_cs

家园 为什么不换成postgresql ?

postgresql 性能强大的多,而且稳如死狗

家园 手痒,试了一下,好像gbk 可以

脚本在这,跑了一下,没有乱码,唯一索引也可以的:

drop table if exists zzIronHand;

create table if not exists zzIronHand (

`id` int(11) NOT NULL AUTO_INCREMENT,

`name` varchar(25) not null COMMENT '测试铁手问题的字段',

PRIMARY KEY (`id`) USING BTREE

) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=gbk ROW_FORMAT=COMPACT COMMENT='测试铁手问题';

insert into zzIronHand(`name`) values ('203');

insert into zzIronHand(`name`) values ('203');

insert into zzIronHand(`name`) values ('e');

insert into zzIronHand(`name`) values ('é');

select * from zzIronHand;

create unique index idxIronHand on zzIronHand(`name`);

我这版本老了点,是MySQL 5.6的。

家园 GBK是没问题的

我就是想从原来的GBK转到 utf8mb4 的其中一种,然后在 unique index上碰到了一些数据错误。

眼下看来,最靠谱大概就是用 utf8mb4_bin 了。

家园 这个方便的话给多说说?

我查来查去的结果,也是有很多人抱怨 mysql 这种字符支持的各种怪异。比如直觉上用 utf8 的,实际上要用 utf8mb4 才行。也有不少人热赞 postgresql 的。

不知道这个学起来难度大不大?我现在用的是 PDO,估计转换起来也不会太多问题。关键是数据库的管理,比如备份之类的,怎么样?

家园 我现在是保存没问题,就是做唯一性索引的时候有问题

你的这个帖中提到的问题,看来是显示上全角半角分不清。出现这种问题,还真是只能哭了,怎么会想的到呢。

家园 惭愧惭愧

是我看帖不仔细,没理解需求

家园 我又继续手痒

接上面的脚本,我加了这句,把字段的Charset改了:

ALTER TABLE zzIronHand

CHANGE COLUMN `name` `name` varchar(25) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

然后再: select * from zzIronHand;

发现没有码,一切正常,索引也没报错。

如果换了其他collate, 尤其是那些ci结尾的,就会报错,说之前创建的唯一索引不对,这个是正常的,因为ci就是Case Insensitive,把e é当成一样的了。

不过好像MySQL的Charset/Collate是有些历史遗留问题的,可以参考一下这里:https://stackoverflow.com/questions/43644218/why-is-table-charset-set-to-utf8mb4-and-collation-to-utf8mb4-unicode-520-ci

全看树展主题 · 分页


有趣有益,互惠互利;开阔视野,博采众长。
虚拟的网络,真实的人。天南地北客,相逢皆朋友

Copyright © cchere 西西河