2013-10-15 19 views
0

优化查询我有一个InnoDB表levels在MySQL5.6

 
+--------------------+--------------+------+-----+---------+-------+ 
| 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  |  | 
+--------------------+--------------+------+-----+---------+-------+ 

共有约4万行。由于前端功能,我需要使用各种过滤器和排序来查询此表。他们在playable_character,rating,playsdate_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子句中存在相同的两个索引列。

我的问题是通过playsrating订购的查询。我知道我正在介绍第三列,但在我的生活中,我无法得到一个效果良好的索引,尽管尝试了我能想到的每一个变化:每个订单中三到四个组合索引,以及等等。也许查询可能写入不同?

我要补充一点,ratingplays总是质疑为DESC。只有date_published可能是DESCASC

任何建议非常感谢。 TIA。

+0

你有没有尝试覆盖索引,像'ON(date_sub ASC,playable_character ASC,rating DESC)''?请注意,第一列按升序排列,最后一列以降序排列,因为您的查询具有'ORDER BY rating DESC' + limit ... – a1ex07

+0

对于第一个查询,'(playable_character,date_published)'上的索引会更好。对于第二和第三,它更复杂,不容易优化。 –

+0

@ypercube - 我本可以发誓,昨天速度较慢,并且在EXPLAIN额外专栏中给了我一个'where'。但你是对的,我只是比较它们,它更快(并且是'使用索引条件')。 – Hal50000

回答

1

看来你会很好地利用这种方式来分类的每个查询的数据:

  1. playable_character,date_published
  2. playable_character,date_published,评级
  3. playable_character,date_published,扮演

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

还要注意,添加DESCASC到索引语法正确,但实际上并没有改变任何东西因为这种功能目前不支持(预计在未来支持所以这就是为什么它的存在)。所有索引都按升序存储。更多信息here

因此,这些都是你应该创建索引:

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

那里跑的比阿甘快这应该使3个查询。

+0

谢谢。有趣的事情正在发生。首先,这两个指标确实运作良好,但是有一种情况他们没有。考虑这个:'SELECT count(*)FROM levels WHERE playable_character = 0;/*'1724061'*/ SELECT count(*)FROM levels WHERE playable_character = 10;/*'6718'* /'当date_published是3年时,它基本上是每个记录。在EXPLAIN中,当行(在EXPLAIN中)大约700,000行时,会弹出'where where',并且查询性能会下降到极限。你知道发生了什么事吗? – Hal50000

+0

索引越“独特”,数据获取的速度越快。如果你有几十万条与查询相匹配的记录(在where子句中),那么你将不得不对数十万条数据进行排序。在这些情况下没有太多的事情要做,因为数据是如何分布的。下一步应该增加诸如'read_rnd_buffer_size'和'sort_buffer_size'的资源。请查看此[链接](http://dev.mysql.com/doc/refman/5.0/en/server-system-variables.html#sysvar_sort_buffer_size)以获取有关它们的信息。或者在http://dba.stackexchange.com提问。他们可以帮助你更多:) –

+0

@Mosty Mostacho read_rnd_buffer_size和sort_buffer_size设置可以非常危险的设置来调整..Hal50000现在问这里http://dba.stackexchange.com/questions/51771/how-to-optimize-indexes -on-mysql-query-with-various-sorts about the settings .. –

1

您的where子句和order by中使用的列应该是索引的一部分。我有你想要的

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

索引我会把可玩角色的原因首先是ID小学,那么所有这些日期内的问题。评分和比赛仅用于协助ORDER BY条款)。

想想像这样的索引。如果您按照Date_Published排序,那么Playable_Character,请考虑一个盒子的房间。每个盒子都有一个日期。在给定日期的那个盒子里,你按照字符顺序排列它们。所以,你需要3年的数据才能通过,你必须在过去的3年中打开所有的盒子,并找到你正在寻找的角色。

现在,想起它是这样的。每个盒子都是按字符排列的,其中的所有日期都是预先排序的。所以,你去一个盒子,打开它......移动到有问题的日期,并从你想要的X-Y范围内抓取记录。现在,您可以通过这些记录申请一个简单的订单。

+0

谢谢,解释有助于理解索引和查询的性质。 – Hal50000

1

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

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

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

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

您可以期待的最好的条件是减少结果集的大小,以便它可以在内存中进行排序,而不需要执行太多的操作sort merge passes。您可以通过增加sort_buffer_size来提供帮助,但请注意不要太多,因为它是按每个线程分配的。

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

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

相关问题