0

我有一个 INNODB 表levels

+--------------------+-------------+------+------+ ---------+--------+
| 领域 | 类型 | 空 | 钥匙 | 默认 | 额外 |
+--------------------+-------------+------+------+ ---------+--------+
| 编号 | 整数(9) | 否 | 优先级 | 空 | |
| 级别名称 | varchar(20) | 否 | | 空 | |
| 用户 ID | 整数(10) | 否 | | 空 | |
| 用户名 | varchar(45) | 否 | | 空 | |
| 评级 | 十进制(5,4) | 否 | | 0.0000 | |
| 票 | 整数(5) | 否 | | 0 | |
| 戏剧| 整数(5) | 否 | | 0 | |
| 发布日期 | 日期 | 否 | 穆尔 | 空 | |
| 用户评论 | varchar(255) | 否 | | 空 | |
| 可玩角色 | 整数(2) | 否 | | 1 | |
| is_featured | 小整数(1) | 否 | 穆尔 | 0 | |
+--------------------+-------------+------+------+ ---------+--------+

大约有 400 万行。由于前端功能的原因,我需要使用各种过滤器和排序来查询此表。它们在playable_characterratingplays和上date_publisheddate_published可以过滤以按最后一天、一周、一个月或任何时间(过去 3 年)显示。还有分页。因此,根据用户的选择,查询可能看起来像以下之一:

SELECT * FROM levels
WHERE playable_character = 0 AND
    date_published BETWEEN date_sub(now(), INTERVAL 3 YEAR) AND now()
ORDER BY date_published DESC
LIMIT 0, 1000;

SELECT * FROM levels
WHERE playable_character = 4 AND
    date_published BETWEEN date_sub(now(), INTERVAL 1 WEEK) AND now()
ORDER BY rating DESC
LIMIT 4000, 1000;

SELECT * FROM levels
WHERE playable_character = 5 AND
    date_published BETWEEN date_sub(now(), INTERVAL 1 MONTH) AND now()
ORDER BY plays DESC
LIMIT 1000, 1000;

我从一个索引开始,该索引在此处的第一个示例查询idx_date_char(date_published, playable_character)中运行良好——基本上任何按date_published. 使用 EXPLAIN,我得到“使用索引条件”,这很好。我想我理解索引为什么起作用,因为在 WHERE 和 ORDER BY 子句中存在相同的两个索引列。

我的问题是 ORDER by playsor的查询rating。我知道我要介绍第三列,但在我的一生中,尽管尝试了我能想到的几乎所有变体,但我无法获得一个运行良好的索引:每个顺序中所有三个或四个的复合索引,以及很快。也许查询可以用不同的方式编写?

我应该补充一点rating,并且plays总是被查询为DESC. 只能date_publishedDESCASC

任何建议都非常感谢。TIA。

4

3 回答 3

1

where 子句 AND order by 中使用的列应该是索引的一部分。我会有一个索引

( playable_character, date_published DESC, rating DESC, plays DESC )

我将可玩角色放在首位的原因是您想要该 ID 主要,然后是所有这些日期。评级和戏剧只是为了协助 ORDER BY 子句)。

想想这样的索引。如果您是按 Date_Published 和 Playable_Character 订购的,请考虑一个盒子房间。每个框都有一个日期。在给定日期的那个框中,您按字符顺序排列它们。所以,你有 3 年的数据要查看,你必须打开过去 3 年的所有盒子,找到你要找的角色。

现在,这样想。每个盒子都是按字符排列的,在其中,它们的所有日期都是预先排序的。所以,你去一个盒子,打开它......移动到有问题的日期并从你想要的 XY 范围内获取记录。现在,您可以应用这些记录的简单排序。

于 2013-10-15T16:09:13.093 回答
1

似乎您会为每个查询充分利用以这种方式排序的数据:

  1. 可玩角色,发布日期
  2. 可玩角色、发布日期、评分
  3. playable_character, date_published, 播放

请记住,您需要在第一个查询中排序的数据恰好是第二个和第三个查询所需数据的子集,因此我们可以摆脱它。

另请注意,向索引添加DESCASC在语法上是正确的,但实际上并没有更改任何内容,因为当前不支持该功能(预计将来会支持它,这就是它存在的原因)。所有索引都按升序存储。更多信息在这里

所以这些是您应该创建的索引:

ALTER TABLE levels ADD INDEX (playable_character, date_published, rating)
ALTER TABLE levels ADD INDEX (playable_character, date_published, plays)

这应该会使上面的 3 个查询比阿甘正传运行得更快。

于 2013-10-15T16:20:13.567 回答
1

当您的查询包含范围谓词BETWEEN时,索引中列的顺序很重要。

  • 首先,包括一个或多个由相等谓词引用的列。
  • 接下来,包括一个由范围谓词引用的列。
  • 范围谓词引用的列之后的索引中的任何其他列都不能用于其他范围谓词或排序。
  • 如果您没有范围谓词,则可以为排序顺序添加一列。

因此,您的第一个查询可以从(playable_character, date_published). 排序应该是无操作的,因为优化器只会按索引顺序获取行。

第二个和第三个查询必然会进行文件排序,因为您有一个范围谓词,然后您将按不同的列进行排序。如果您只有相等谓词,则可以使用第三列来避免文件排序,但是当您有范围谓词时,这不起作用。

您可以期望的最好结果是条件减小了结果集的大小,以便它可以在内存中进行排序,而无需进行太多的排序合并传递。您可以通过增加sort_buffer_size来解决此问题,但请注意不要增加太多,因为它是按线程分配的。

索引定义中的ASC/DESC关键字在 MySQL 中没有任何区别。
请参阅http://dev.mysql.com/doc/refman/5.6/en/create-index.html

这些关键字被允许用于指定升序或降序索引值存储的未来扩展。目前,它们被解析但被忽略;索引值始终按升序存储。

于 2013-10-15T20:30:43.597 回答