[英]Optimizing query in MySQL5.6
我有一个INNODB表levels
:
+--------------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------------------+--------------+------+-----+---------+-------+ | id | int(9) | NO | PRI | NULL | | | level_name | varchar(20) | NO | | NULL | | | user_id | int(10) | NO | | NULL | | | user_name | varchar(45) | NO | | NULL | | | rating | decimal(5,4) | NO | | 0.0000 | | | votes | int(5) | NO | | 0 | | | plays | int(5) | NO | | 0 | | | date_published | date | NO | MUL | NULL | | | user_comment | varchar(255) | NO | | NULL | | | playable_character | int(2) | NO | | 1 | | | is_featured | tinyint(1) | NO | MUL | 0 | | +--------------------+--------------+------+-----+---------+-------+
有大约400万行。 由于具有前端功能,因此我需要使用各种过滤器和排序查询该表。 它们在playable_character
, rating
, plays
和date_published
。 可以过滤date_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
排序的date_published
。 使用EXPLAIN,我得到“使用索引条件”,这很好。 我想我理解索引为什么起作用的原因,因为WHERE和ORDER BY子句中存在相同的两个索引列。
我的问题是按plays
或rating
ORDER的查询。 我知道我要介绍第三列,但是就我的一生而言,尽管尝试了我可能想到的几乎所有变体,但我仍然无法获得运行良好的索引:每个顺序中所有三个或四个的复合索引,并且以此类推。 也许查询的书写方式可能不同?
我应该添加该rating
并且plays
总是以DESC
查询。 只有date_published
可以是DESC
或ASC
。
任何建议,不胜感激。 TIA。
where子句AND order by中使用的列应该是索引的一部分。 我会有一个索引
( playable_character, date_published DESC, rating DESC, plays DESC )
我将可玩角色设置为FIRST的原因是,您希望该ID为主要ID,然后是所有这些日期在问题之内。 评分和比赛就可以帮助ORDER BY子句。
这样考虑一下索引。 如果按Date_Published排序,然后按Playable_Character排序,则考虑一下盒子的空间。 每个框都有一个日期。在给定日期的框内,按字符顺序排列它们。 因此,您有3年的数据需要经过,您必须打开最近3年的所有框并找到所需的字符。
现在,这样想。 每个方框均按字符排列,其中所有日期均已预先排序。 因此,您转到一个框,将其打开...移动到有问题的日期,并从所需的XY范围中获取记录。 现在,您可以通过这些记录应用简单的订单。
看来您将充分利用以这种方式对每个查询排序的数据:
请记住,您在第一个查询中排序的数据恰好是第二个和第三个查询所需数据的子集,因此我们可以摆脱它。
还要注意,将DESC
或ASC
添加到索引在语法上是正确的,但实际上并不会更改任何内容,因为当前不支持该功能(预计将来会支持该功能,因此就是该功能所在)。 所有索引均以升序存储。 更多信息在这里 。
因此,这些是您应该创建的索引:
ALTER TABLE levels ADD INDEX (playable_character, date_published, rating)
ALTER TABLE levels ADD INDEX (playable_character, date_published, plays)
那应该使那里的3个查询比Forrest Gump运行得更快。
当查询包含像BETWEEN
这样的范围谓词时,索引中列的顺序很重要。
因此,您的第一个查询可以从(playable_character, date_published)
上的索引中受益。 排序应为空,因为优化器只会按索引顺序获取行。
第二个查询和第三个查询必然要进行文件排序,因为您有一个范围谓词,然后要按不同的列进行排序。 如果仅具有相等谓词,则可以使用第三列来避免文件排序,但是当您具有范围谓词时,这将不起作用。
您所希望的最好的结果是,条件会减小结果集的大小,以使结果集可以在内存中进行排序,而无需进行过多的排序合并遍历 。 您可以通过增加sort_buffer_size来帮助解决此问题,但请注意不要增加太多,因为它是按线程分配的。
索引定义中的ASC
/ DESC
关键字在MySQL中没有区别。
参见http://dev.mysql.com/doc/refman/5.6/en/create-index.html :
这些关键字允许将来用于指定升序或降序索引值存储的扩展。 目前,它们已被解析但被忽略; 索引值始终按升序存储。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.