0

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

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

我在以下字段上有索引:

  • 访问ID
  • 日期
  • 其他一些领域

这是解释的样子:

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索引对查询时间没有影响。

4

6 回答 6

5

上的索引(date,accessid) 可能会有所帮助。但是,在调整索引之前,我建议您检查accessid列的类型。EXPLAIN说密钥长 257 个字节,这对于 ID 列来说听起来很多。你在使用VARCHAR(256)foraccessid吗?如果是这样,您不能使用更紧凑的类型吗?如果它是一个数字,它应该由INT( SMALLINT, BIGINT,任何适合您的需要),如果它是一个字母数字 ID,它真的可以是 256 个字符吗?如果它的长度是固定的,你不能用CHARCHAR(32)例如)代替吗?

于 2009-09-11T06:08:07.687 回答
2

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

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

您可以尝试在 (date,accessid) 上创建一个索引,希望它将使用它来覆盖查询(因此您不需要访问任何表),但我不抱太大希望。你能做的不多。

编辑:

我的回答是由High Performance MySQL - Second Edition提供的,如果您必须进行认真的 MySQL 开发,那么它是金子般的重量。

于 2009-09-11T06:00:20.760 回答
0

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

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

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

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

于 2009-09-11T05:53:02.660 回答
0

该查询使用“日期”索引,因为这就是您在 where 子句中使用的内容。

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

如果这是一个非常大的表,则日期和 accessid 的复合索引可能会有所帮助。

于 2009-09-11T05:56:17.303 回答
0

我无法对其进行测试,但我肯定会尝试添加一个涵盖 accessid 和 date 的索引

经常像炼金术一样的索引优化。不同的 DBMS 表现不同,有时您需要简单地尝试(并失败)各种组合。我不是说不可能推理。在很多情况下是这样,但在一定程度上是这样。通常,跟随你的直觉会更快、更容易。

于 2009-09-11T05:59:50.073 回答
0

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

因为使用日期索引可以让它忽略表中的很大一部分数据。有可能该表主要包含历史数据,其中很多是指比当前月初更早的日期,因此日期标准是有选择性的,并允许优化器忽略大多数数据,从而减少了优化器的工作量的数据。

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

是否有任何其他索引可以用来加快在某些日期范围内对不同 accessid 的查询?

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

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

于 2009-09-11T06:01:40.160 回答