我的数据库模式包括以下两个表:
CREATE TABLE `categories` (
`id` bigint(20) NOT NULL auto_increment,
`title` varchar(128) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
并且
CREATE TABLE `articles` (
`id` bigint(20) NOT NULL auto_increment,
`title` varchar(512) NOT NULL,
`body` longtext,
`state` varchar(7) NOT NULL,
`type` varchar(6) NOT NULL,
`category` bigint(20) default NULL,
`publishedAt` datetime default NULL,
PRIMARY KEY (`id`),
KEY `FK_category_to_article_category` (`category`),
CONSTRAINT `FK_category_to_article_category` FOREIGN KEY (`category`) REFERENCES `categories` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
对于文章表格,state
列有"PUBLISHED"或"UNPUBLISHED"等值,type
列有"NEWS"、"GOSSIP"和"OPINION"等值。
我的应用程序执行很多这样的查询:
select * from articles where state="PUBLISHED" and type in ("NEWS","GOSSIP")
and category in (4) and publishedAt<=now() order by publishedAt desc;
我有大约10K篇文章,我正在尝试确定上述查询在使用默认的分类外键时是否表现更好,还是应该使用多列索引。
没有索引(使用“explain extended”):
+----+-------------+-------+------+---------------------------------+---------------------------------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------------------------+---------------------------------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | this_ | ref | FK_category_to_article_category | FK_category_to_article_category | 9 | const | 630 | Using where; Using filesort |
+----+-------------+-------+------+---------------------------------+---------------------------------+---------+-------+------+-----------------------------+
如果我创建了多列索引并再次解释(强制使用特定的索引):
create index I_s_t_c_p on articles (state, type, category, publishedAt);
+----+-------------+-------+-------+---------------+-----------+---------+------+------+------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+------------------------------------------+
| 1 | SIMPLE | this_ | range | I_s_t_c_p | I_s_t_c_p | 61 | NULL | 1216 | Using where; Using index; Using filesort |
+----+-------------+-------+-------+---------------+-----------+---------+------+------+------------------------------------------+
查询实际返回的行数为630。在我看来,多列索引应该比FK表现更好,因为所有索引列都被使用了,但是当使用索引时检查了约1200行的事实使我感到困惑。我知道这些数字只是估计值,但两个键之间的差异相当大;使用组合索引时,我们检查的行数是使用FK的两倍。
所以我的问题如下:
- 为什么使用多列索引会检查那么多行?
- 由于使用FK我们有一个连接类型“ref”,而使用组合索引我们有一个连接类型“range”,这是否意味着使用FK的查询比另一个查询更好/更快?
- 我应该将检查行数的估计作为决定索引是否良好/最优的标准吗?
- 在这种情况下,多列索引比FK更好吗?我应该基于什么来做出决策?
一些额外的信息:
- 在没有强制查询上的索引的情况下,优化器选择了FK。当我对文章执行了
analyze table
操作后,选择了多列索引。 - 我正在使用MySql 5.0.15
- 索引信息
+----------+------------+---------------------------------+--------------+-------------+-------------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Cardinality | Index_type |
+----------+------------+---------------------------------+--------------+-------------+-------------+------------+
| articles | 0 | PRIMARY | 1 | id | 12561 | BTREE |
| articles | 1 | FK_category_to_article_category | 1 | category | 37 | BTREE |
| articles | 1 | I_s_t_c_p | 1 | state | 8 | BTREE |
| articles | 1 | I_s_t_c_p | 2 | type | 32 | BTREE |
| articles | 1 | I_s_t_c_p | 3 | category | 163 | BTREE |
| articles | 1 | I_s_t_c_p | 4 | publishedAt | 12561 | BTREE |
+----------+------------+---------------------------------+--------------+-------------+-------------+------------+
谢谢您的提前参与。
publishedAt
似乎是不必要的,但是以分类列为首的索引顺序与以状态为首的索引顺序相比,结果更差。这可能与文章数量有关吗?也许在约50K篇文章的情况下,一个以类别为首的索引可以展示更好的结果? - Argyro Kazaki