可以使用哪些索引来改进此查询?

时间:2009-09-11 05:48:46

标签: mysql optimization indexing distinct

此查询选择特定日期范围内的所有唯一身份访问者会话:

select distinct(accessid) from accesslog where date > '2009-09-01'

我在以下字段中有索引:

  • ACCESSID
  • 日期
  • 其他一些领域

以下是解释:

mysql> explain select distinct(accessid) from accesslog where date > '2009-09-01';
+----+-------------+-----------+-------+----------------------+------+---------+------+-------+------------------------------+
| id | select_type | table     | type  | possible_keys        | key  | key_len | ref  | rows  | Extra                        |
+----+-------------+-----------+-------+----------------------+------+---------+------+-------+------------------------------+
|  1 | SIMPLE      | accesslog | range | date,dateurl,dateaff | date | 3       | NULL | 64623 | Using where; Using temporary |
+----+-------------+-----------+-------+----------------------+------+---------+------+-------+------------------------------+


mysql> explain select distinct(accessid) from accesslog;
+----+-------------+-----------+-------+---------------+----------+---------+------+---------+-------------+
| id | select_type | table     | type  | possible_keys | key      | key_len | ref  | rows    | Extra       |
+----+-------------+-----------+-------+---------------+----------+---------+------+---------+-------------+
|  1 | SIMPLE      | accesslog | index | NULL          | accessid | 257     | NULL | 1460253 | Using index |
+----+-------------+-----------+-------+---------------+----------+---------+------+---------+-------------+

为什么带有date子句的查询不使用accessid索引?

我是否可以使用其他索引来加快某些日期跨度中不同accessid的查询?

修改 - 解决方案

accessid上的列宽从varchar 255减少到char 32,将查询时间缩短了约75%。

添加date+accessid索引对查询时间没有影响。

6 个答案:

答案 0 :(得分:5)

(date,accessid) 上的索引可以帮助。但是,在调整索引之前,我建议您检查accessid列的类型。 EXPLAIN表示密钥长度为257字节,对于ID列来说听起来很多。您是否使用VARCHAR(256) accessid?如果是这样,你不能使用更紧凑的类型吗?如果它是一个数字,它应该是INTSMALLINTBIGINT,符合您的需要),如果它是一个字母数字ID,它真的是256 chars多长?如果其长度是固定的,您不能使用CHAR(例如CHAR(32))吗?

答案 1 :(得分:2)

您的问题是您的条件是范围子句(在日期列上)。

date-> accessid的多列索引可能无法帮助解决这种情况,因为MySQL在范围条件之后无法使用索引列。从理论上讲,它们应该能够用它来覆盖这种情况下的计算,但它似乎是MySQL的一个缺点,我从来没有在这种情况下成功地使用多列索引。

你可以尝试在(date,accessid)上创建一个索引,希望它能用它来覆盖查询(所以你不需要点击任何表格),但我没有太大的希望。你可以做的不是很多。

编辑:

我的回答是High Performance MySQL - Second Edition的礼貌,如果你不得不进行严肃的MySQL开发,那么它的重要性就是黄金。

答案 2 :(得分:0)

  

为什么带有date子句的查询不使用accessid索引?

因为使用日期索引更有效率。那是因为它可能会更快地削减搜索空间。

至少有一个DBMS(DB2 / z,我对MySQL不太了解)会受益于date + accessid上的索引,因为访问ID将在该索引的日期内排序。 DBMS将使用date + accessid键有效地使用where子句来缩小搜索空间,以在该空间内返回accessid的不同值。

MySQL是否聪明,我不知道。我的建议是尝试一下(这是大多数数据库优化问题的最佳答案)。

答案 3 :(得分:0)

查询使用'date'索引,因为这就是你在where子句中使用的内容。

这是唯一明智的选择,如果它使用了访问id索引,它需要读取所有accessid行,然后检查它之前的日期,然后才决定它是否是不同的。

如果这是一张非常大的表,那么日期和accessid上的复合索引可能有所帮助。

答案 4 :(得分:0)

我无法对其进行测试,但我绝对会尝试添加一个跨越accessid和date 的索引。

索引优化(如果经常喜欢炼金术)。不同的DBMS行为不同,有时您只需尝试(并失败)各种组合。我不是说不可能推理。在许多情况下,但达到某一点。通常,只需更快,更容易地遵循你的直觉。

答案 5 :(得分:0)

  

为什么带有date子句的查询不使用accessid索引?

因为使用日期索引允许它忽略表中的大部分数据。很可能该表主要包含历史数据,其中很多都是指比当前月初更早的日期,因此日期标准是选择性的,通过允许它忽略大多数来减少优化程序的工作量数据。

如果它使用了accessid索引,则还必须读取每一行(以及每个索引条目)以查看日期是否符合搜索条件。这意味着读取整个索引和整个表 - 实际上,在上下文中忽略索引会做得更好,但我开始使用“如果它使用了accessid索引”。

  

我是否可以使用其他索引来加快某些日期跨度中不同accessid的查询?

根据优化程序的复杂程度,(date,accessid)上的索引可能会改进。它可以在索引的前导列上进行范围搜索,尾随列表示它不必引用表中的数据来建立accessid - 信息在索引中。因此,这可能会将访问索引和表的查询转换为仅访问索引的查询 - 这将减少所需的I / O量,从而提高查询的性能。

如果您有其他标准需要来自其他列的数据,或者您需要返回的不仅仅是唯一的accessid值,那么您最终会读取部分表数据;与扫描整个表格相比,这可能仍然是一个胜利。