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";

propertyKeypropertyValue 上没有任何索引,该查询运行非常缓慢,大约需要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

propertyKeypropertyValue 的唯一区别是前者是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 的所有行现在将“彼此相邻”,从而最大限度地减少磁盘读取。 idAUTO_INCREMENT 吗?如果是这样,INDEX(id) 就足够了;不需要PK。否则...为什么有id?你能摆脱它吗?并不真地;您仍然需要以某种方式使 PK 独一无二。

表的ROW_FORMAT 是什么? property_value 通常需要多长时间?这些会影响“大”字段(例如 TEXT)是与行一起保存还是单独存储。

(底线:键值模式很烂。)

【讨论】:

以上是关于TEXT 列上的 MySQL 索引无效的主要内容,如果未能解决你的问题,请参考以下文章

使用 Laravel 4 Eloquent 连接列上的列名“”无效?

计算列上的 T-SQL 列别名 - 列名无效

添加唯一约束时出错:表中的列的类型对于用作索引中的键列无效[重复]

主索引列上的 MySQL 表外键索引

时间戳列上的 MySQL 索引不用于大日期范围

MariaDB/MySQL 复合唯一索引无效