如何调试 Etherpad Lite 数据库中 MySQL utf8mb4 列中的无效数据
Posted
技术标签:
【中文标题】如何调试 Etherpad Lite 数据库中 MySQL utf8mb4 列中的无效数据【英文标题】:How to debug invalid data in MySQL utf8mb4 column in Etherpad Lite database 【发布时间】:2018-02-14 22:32:00 【问题描述】:我们正在运行 Etherpad Lite,并且正在尝试将数据库从 mysql 迁移到 PostgreSQL。
MySQL 数据库“值”列的类型为 utf8mb4。但是,大约 10% 的行包含实际上以 Windows-1252 或 ISO-8859-15 而不是 UTF-8 编码的值。这怎么可能? MySQL 在输入列之前不验证 UTF-8 吗?
PostgreSQL 在迁移期间不能接受无效值,因为它确实验证了数据并命中例如原始字节 0xE4 (ISO-8859-15: ä
) 应编码为 UTF-8 中的字节序列 0xC3 0xA4。
这是 MySQL 的一个已知“特性”吗?有什么方法可以始终从utf8mb4
列获取真正的 UTF-8?
【问题讨论】:
【参考方案1】:如果
你说客户端正在使用latin1
(等等),并且
您说列是utf8(或utf8mb4),并且
你提供十六进制E4
那么一切都很好。 E4
将在INSERT
期间转换为C3A4
,这就是存储的内容。做SELECT HEX(...) ...
验证。
如果
你说客户端正在使用utf8(或utf8mb4),并且 您说列是utf8(或utf8mb4),并且 你提供十六进制C3A4
再次,一切都很好。 C3A4
直接进入表格。
这是一个混乱的案例:
如果
你说客户端正在使用latin1
,并且
您说列是utf8(或utf8mb4),并且
但是你提供十六进制C3A4
然后,MySQL 有义务将 两个 字符(C3 和 A4)转换为 utf8,产生C383C2A4
。我称之为“双重编码”。
遵循Trouble with UTF-8 characters; what I see is not what I stored 中的最佳实践并使用其建议的方法来测试数据。然后回来提供更多细节。
可能导致 10% 的数据被错误解释的唯一方法是对 10% 的数据进行不同的编码。因此,请为 10% 的示例和 90% 的示例提供十六进制。并在插入前在客户端提供十六进制,插入后在表格中提供。
【讨论】:
我有一个案例,客户端说set names utf8
并且数据库列的类型为utf8mb4
和SELECT
仍然返回包含原始字节\xE4
的字符串,而不是ä
或U+00E4 .请注意,UTF-8 应该将该字符编码为\xC3\xA4
。数据最初由 node.js 服务输入,以防万一。
那么你已经“撒谎”了字节是 utf8。十六进制 E4
是 latin1。 U+00E4
可能看起来很相似,但那是 unicode,不是 latin1,也不是 utf8。说 set names utf8
是说 client 将具有十六进制 C3A4
用于 ä
。
我同意纯 0xE4
不应出现在 UTF-8 字符串中。但是,Etherpad Lite(在 node.js 上运行)有时会在 UTF-8 字符串中间发出原始 Windows-1252 字符(我认为这是 node.js 或 Etherpad Lite 中的一个错误),MySQL 会很乐意将这样的字符串存储在MyISAM 表。如果 MySQL 确实抛出异常而不是接受这样的无效字符串,我就不会问这个问题。
UTF-8 字符串可能包含字节 0xE4
,如果它后面跟着设置了第 8 位的其他字节。比如U+4494就是字节序列0xE4 0x92 0x94
。
我不知道如何重现问题(将数据插入数据库),但事实是 MySQL 正在使用 Windows-1252 编码从数据库返回部分数据,即使连接设置为UTF-8 和数据库列类型为utf8mb4
。【参考方案2】:
没有已知的解决方案。这可能是 MySQL 中的一个错误,它应该禁止存储非 UTF-8 数据以防 client 连接和 列类型 都是 utf8mb4。
我不再将 MySQL 用于任何事情,所以我不再费心去尝试解决这个错误。现在,我用 PostgreSQL 代替一切。
【讨论】:
以上是关于如何调试 Etherpad Lite 数据库中 MySQL utf8mb4 列中的无效数据的主要内容,如果未能解决你的问题,请参考以下文章
如何在 cPanel 上安装 etherpad-lite(共享主机)
如何在 Sandstorm 上安装 Etherpad Lite 插件?