TEXT 列上的 MySQL 索引无效
Posted
技术标签:
【中文标题】TEXT 列上的 MySQL 索引无效【英文标题】:MySQL Index on TEXT Column not Effective 【发布时间】:2015-12-03 07:14:01 【问题描述】:我有一个相当简单的表来存储键:值对,设置如下:
CREATE TABLE `assetProperties` (
`propertyKey` varchar(255) NOT NULL,
`propertyValue` text NOT NULL,
`id` bigint(20) NOT NULL,
`assetInstance_id` bigint(20) NOT NULL,
PRIMARY KEY (`id`),
KEY `FK67F768435C68E1C0` (`id`),
KEY `FK67F76843FBDFC83F` (`assetInstance_id`),
KEY `keyIndex` (`propertyKey`),
KEY `valIndex` (`propertyValue`(255)),
CONSTRAINT `FK67F76843FBDFC83F` FOREIGN KEY (`assetInstance_id`) REFERENCES `assets` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
...并且此表中有合理数量的条目:
mysql> select count(*) from assetProperties;
+----------+
| count(*) |
+----------+
| 19931305 |
+----------+
...我想做的是找到与给定键值对完全匹配的条目。例如,使用如下查询:
SELECT count(*) FROM assetProperties WHERE propertyKey = "Wood Species"
AND propertyValue = "Jarrah";
在propertyKey
和propertyValue
上没有任何索引,该查询运行非常缓慢,大约需要10 多秒。这是可以预料的。问题是,即使在两个字段上都添加了索引,查询时间也没有改善。
奇怪的是,propertyKey
上的索引似乎工作正常:
-- Before adding index
mysql> SELECT count(*) FROM assetProperties WHERE propertyKey = "Wood Species";
+----------+
| count(*) |
+----------+
| 339395 |
+----------+
1 row in set (9.37 sec) <-- bad
-- After adding index
mysql> SELECT count(*) FROM assetProperties WHERE propertyKey = "Wood Species";
+----------+
| count(*) |
+----------+
| 339395 |
+----------+
1 row in set (0.16 sec) <-- reasonable
...虽然propertyValue
上的索引似乎没有什么不同:
-- Before adding index
mysql> SELECT count(*) FROM assetProperties WHERE propertyValue = "Jarrah";
+----------+
| count(*) |
+----------+
| 219099 |
+----------+
1 row in set (12.51 sec) <-- bad
-- After adding index
mysql> SELECT count(*) FROM assetProperties WHERE propertyValue = "Jarrah";
+----------+
| count(*) |
+----------+
| 219099 |
+----------+
1 row in set (9.75 sec) <-- still garbage
propertyKey
和propertyValue
的唯一区别是前者是varchar(255)
列,后者是text
。是否有什么东西阻止 MySQL 有效地使用 text
列上的索引,或者我可以做些什么来加快查询速度?
编辑
还尝试在两个字段中添加多列索引。没有区别。
此外,如果我添加一个 varchar(255)
类型的新列(例如 propertyValueShort
)并将 propertyValue
中的值复制到新列中并设置相关索引,它会正常工作:
mysql> SELECT count(*) FROM assetProperties WHERE propertyKey = "Wood Species" AND propertyValueShort = "Jarrah";
+----------+
| count(*) |
+----------+
| 219099 |
+----------+
1 row in set (0.14 sec) <-- acceptable
所以这可能是直接的答案(目前使用的最长 propertyValue
是 88 个字符,所以我真的不需要在那里使用 text
)。但是仍然没有解释为什么text
列上的索引表现如此糟糕。
【问题讨论】:
你能发布这些查询的 EXPLAIN 输出吗? 【参考方案1】:PRIMARY KEY (`id`),
KEY `keyIndex` (`propertyKey`),
-->
PRIMARY KEY(property_key, id),
INDEX(id),
为什么?
InnoDB 将 PK 与数据“聚集”在一起。这意味着具有相同property_key
的所有行现在将“彼此相邻”,从而最大限度地减少磁盘读取。
id
是 AUTO_INCREMENT
吗?如果是这样,INDEX(id)
就足够了;不需要PK。否则...为什么有id
?你能摆脱它吗?并不真地;您仍然需要以某种方式使 PK 独一无二。
表的ROW_FORMAT
是什么? property_value
通常需要多长时间?这些会影响“大”字段(例如 TEXT)是与行一起保存还是单独存储。
(底线:键值模式很烂。)
【讨论】:
以上是关于TEXT 列上的 MySQL 索引无效的主要内容,如果未能解决你的问题,请参考以下文章
使用 Laravel 4 Eloquent 连接列上的列名“”无效?