为什么MySQL的查询性能使用CHAR / VARCHAR索引时是如此糟糕?(Why perform

2019-10-23 05:18发布

首先,我将描述该问题域的简化版本。

有表strings

CREATE TABLE strings (
  value CHAR(3) COLLATE utf8_unicode_ci NOT NULL,
  INDEX(value)
) ENGINE=InnoDB;

正如你所看到的,它有一个非唯一索引CHAR(3)列。

该表是使用下面的脚本填充:

CREATE TABLE a_variants (
  letter CHAR(1) COLLATE utf8_unicode_ci  NOT NULL
) ENGINE=MEMORY;

INSERT INTO a_variants VALUES -- 60 variants of letter 'A'
  ('A'),('a'),('À'),('Á'),('Â'),('Ã'),('Ä'),('Å'),('à'),('á'),('â'),('ã'),
  ('ä'),('å'),('Ā'),('ā'),('Ă'),('ă'),('Ą'),('ą'),('Ǎ'),('ǎ'),('Ǟ'),('ǟ'),
  ('Ǡ'),('ǡ'),('Ǻ'),('ǻ'),('Ȁ'),('ȁ'),('Ȃ'),('ȃ'),('Ȧ'),('ȧ'),('Ḁ'),('ḁ'),
  ('Ạ'),('ạ'),('Ả'),('ả'),('Ấ'),('ấ'),('Ầ'),('ầ'),('Ẩ'),('ẩ'),('Ẫ'),('ẫ'),
  ('Ậ'),('ậ'),('Ắ'),('ắ'),('Ằ'),('ằ'),('Ẳ'),('ẳ'),('Ẵ'),('ẵ'),('Ặ'),('ặ');

INSERT INTO strings
  SELECT CONCAT(a.letter, b.letter, c.letter) -- 60^3 variants of string 'AAA'
    FROM a_variants a, a_variants b, a_variants c
  UNION ALL SELECT 'BBB'; -- one variant of string 'BBB'

因此,它包含了216000没有区别(在方面utf8_unicode_ci整理)字符串“AAA”和字符串“BBB”一个变体的变种:

SELECT value, COUNT(*) FROM strings GROUP BY value;
+-------+----------+
| value | COUNT(*) |
+-------+----------+
| AAA   |   216000 |
| BBB   |        1 |
+-------+----------+

由于value被索引,我希望以下两个查询到有类似的表现:

SELECT SQL_NO_CACHE COUNT(*) FROM strings WHERE value = 'AAA';
SELECT SQL_NO_CACHE COUNT(*) FROM strings WHERE value = 'BBB';

但在实践中的第一个比第二慢的超过300个倍 ! 看到:

+----------+------------+---------------------------------------------------------------+
| Query_ID | Duration   | Query                                                         |
+----------+------------+---------------------------------------------------------------+
|        1 | 0.11749275 | SELECT SQL_NO_CACHE COUNT(*) FROM strings WHERE value = 'AAA' |
|        2 | 0.00033325 | SELECT SQL_NO_CACHE COUNT(*) FROM strings WHERE value = 'BBB' |
|        3 | 0.11718050 | SELECT SQL_NO_CACHE COUNT(*) FROM strings WHERE value = 'AAA' |
+----------+------------+---------------------------------------------------------------+

- 我跑了“AAA”查询两次在这里只是要确定。

如果我改变索引列的大小或改变其类型为VARCHAR ,具有性能问题依然表现。 同时,在类似的情况,但在非唯一索引不CHAR / VARCHAR (如INT ),查询是预期的快。

所以,问题是为什么MySQL的查询的性能,使用时是如此糟糕CHAR / VARCHAR指数?

我有一个MySQL的执行按索引键相匹配的所有值完全线性扫描强烈的感觉。 但它为什么这样做时,它可以只返回匹配的行数? 我失去了什么,而真正需要的? 或者是,MySQL优化的可悲缺点?

Answer 1:

显然,问题是,查询做一个索引扫描。 另一种方法是做两个索引查找,对于那些相同的第一和最后一个值,然后在该指数中使用的元信息。 根据你的观察,MySQL不会把两者。

这个答案的其余部分是投机。

究其原因,表现“仅仅” 300慢倍,而不是慢20万次,是因为开销在读取索引。 事实上扫描的条目是相当快相比,还需要其它的操作。

有数字和字符串之间的根本区别,当谈到比较。 该引擎可以只看两个数位表示,并承认他们是否相同或不同。 不幸的是,对于字符串,你需要采取编码/整理进去。 我想,这就是为什么它需要看值。

这是可能的,如果你有完全相同的字符串21.6万份,那么MySQL将能够做索引使用元数据计数。 换句话说,该索引是足够聪明的使用元数据进行精确的相等比较。 但是,这是没有足够的智慧采取编码考虑。



Answer 2:

一个你可能要检查的事情是每个查询的逻辑I / O。 我敢肯定你会看到完全不同。 要计算的“BBB在表中的数字,大概只需要3或4 LIOS(视之类的东西桶的大小)。 要计算的“AAA的,基本上是整个表必须进行扫描,索引或数量不限。 随着216k行,可加起来显著更LIOS - 更不用说物理I / O。 逻辑I / O的速度比物理I / O,但任何I / O是一个性能杀手。

至于文字VS数字,它始终是软件( 任何软件,而不仅仅是数据库引擎)不是文字比较数字更容易和更快。



文章来源: Why performance of MySQL queries are so bad when using a CHAR/VARCHAR index?