首先,我將描述問題域的簡化版本。爲什麼使用CHAR/VARCHAR索引時,MySQL查詢的性能如此糟糕?
有表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
)時,查詢與預期的一樣快。
所以,問題是爲什麼使用CHAR
/VARCHAR
索引時,MySQL查詢的性能如此糟糕?
我有強烈的感覺是,MySQL執行按索引鍵相匹配的所有值完全線性掃描。但是,爲什麼它只能返回匹配行的數量呢?我錯過了什麼,這真的需要嗎?或者是MySQL優化器的一個可悲的缺點?
@dnoeth。 。 。 OP做出聲明(我希望他/他會證實)整數列的性能非常相似。問題是爲什麼一個索引掃描字符,但其他一些整數方法。 –