【问题标题】:Can we have performance issues when using mixed table charsets in MySQL or in Postgres?在 MySQL 或 Postgres 中使用混合表字符集时会出现性能问题吗?
【发布时间】:2021-07-13 16:46:45
【问题描述】:

我正在开发一个具有不同字符集的表的数据库。由于它是一个大型数据库,我想知道它是否会导致性能问题。是的,数据库通常进行的通常值比较是 JOIN 并比较整数,但是除了某些字符集占用的更大空间之外,我们还会遇到具有不同字符集的表的其他性能问题吗?

【问题讨论】:

  • 好吧,您可以优化列字符集,例如,邮政编码 utf8 会很糟糕,更好的选择是 latin1 或 ascii,甚至可以节省磁盘空间来制作更小的表。您是否需要存储网页内容 utf8 可能是当今的选择
  • 感谢您的评论!实际上,它更多的是关于速度而不是空间(我知道它们是相关的,但我想专注于不受占用空间影响的性能)

标签: mysql database postgresql character-encoding collate


【解决方案1】:

如果您使用不兼容的排序规则进行字符串比较,则这些比较不能使用字符串列上的索引。我已经看到在对字符串列执行 JOIN 时会发生这种情况,并且加入的表有不同的排序规则(当然,如果它们也有不同的字符集,它们也是不同的排序规则)。

但是您说您的联接是在整数列上,而不是在字符串列上。因此,在您的情况下,联接应该不是问题。


如果您的表字符集与会话字符集不匹配,则在对字符串列进行查找时也会遇到性能问题。

示例:我的表是用 utf8mb4 定义的,但我将会话设置为 utf8,所以字符串文字将是 utf8。看起来像一个无害的变化,对吧?

mysql> set names utf8;

mysql> explain select * from mytable where text = 'abc123';
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | mytable | NULL       | ref  | text          | text | 83      | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+

我猜想将 utf8 字符串 'abc123' 提升为 utf8mb4 以匹配它所比较的​​列。

但是,如果我强制使用 utf8mb4 不支持的特定排序规则,我发现它必须进行表扫描并逐行比较,而不是索引查找:

mysql> explain select * from mytable where text = 'abc123' collate utf8_general_ci;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | mytable | NULL       | ALL  | NULL          | NULL | NULL    | NULL |   10 |   100.00 | Using where |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------------+

隐式排序规则和显式排序规则之间存在差异。假设我将会话设置为使用没有明确的 utf8mb4 路径的东西:

mysql> set names latin1;

mysql> explain select * from mytable where text = 'abc123';
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
| id | select_type | table   | partitions | type | possible_keys | key  | key_len | ref   | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | mytable | NULL       | ref  | text          | text | 83      | const |    1 |   100.00 | NULL  |
+----+-------------+---------+------------+------+---------------+------+---------+-------+------+----------+-------+

到目前为止一切顺利,但如果我明确说明排序规则:

mysql> explain select * from mytable where text = 'abc123' collate latin1_general_ci;
ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and (latin1_general_ci,EXPLICIT) for operation '='

最重要的是,您应该使用相同的字符集和排序规则,以使您的生活更轻松。将它用于所有表格和会话。

在当今时代,很难找到使用 utf8mb4 以外的任何东西的理由。


附:空间应该不是问题。 UTF-8 字符集允许 多字节字符,但它们不会扩展适合单个字节的字符的大小。 UTF-8 是一种可变宽度字符编码。因此,ASCII 范围 (0-127) 中的字符无论如何都存储在一个字节中。阅读UTF-8 on wikipedia了解详细信息,它有一个很好的解释。

【讨论】:

  • 感谢您的努力和完整的回答!
【解决方案2】:

MySQL:

对于存储为字符串(CHARVARCHAR)的 zip_code (postal_code),大多数字符集同样适用。但是,当JOINing 在这样的列上时,排序规则必须相同。

  • 如果相同,则可以使用该列上的索引。
  • 如果不是,那么索引就没用了,查询必须扫描整个表。

由于排序规则包含字符集,因此也强制字符集相同。

排序规则的选择相当少。但是,如果字符串中可以包含字母(postal_code、country_code 等),您需要决定是否强制表(和用户查询)使用特定大小写。

  • 排序规则..._bin 将大小写视为不同:“de”与“DE”不匹配(对于德国)。
  • 排序规则..._ci 是“不区分大小写”,因此它们会匹配。

【讨论】:

    猜你喜欢
    • 2017-01-15
    • 1970-01-01
    • 2019-11-11
    • 1970-01-01
    • 2017-02-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-04
    相关资源
    最近更新 更多