【问题标题】:MySQL query optimisation when using index使用索引时的 MySQL 查询优化
【发布时间】:2017-10-02 21:47:28
【问题描述】:

我正忙于优化和理解 MySQL 中的按功能分组。从我从戈登那里学到的关于 SO 的答案:

选择中的所有列都应该是 group by 中的列或 使用聚合函数(sum()、avg() 等)。

我要查表和查询

表格

+-----------------+-----------+------------+-------------+
|Id (primary key) | ip(index) | lastattack | create_date |
+-----------------+-----------+------------+-------------+

查询

  SELECT ip,
         lastattack
    FROM blacklist
   WHERE ip = 'xxx.xxx.xxx.xxx'
GROUP BY ip

当我执行上述查询时,我从 EXPLAIN

收到以下信息
+----+-------------+-------+------+---------------+-----+---------+-----+------+----------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------+---------------+-----+---------+-----+------+----------+-------+
| 1  | SIMPLE      | ipall | ref  | idx           | idx | 257     |const| 2    | 100.00   | Using index condition |
+----+-------------+-------+------+---------------+-----+---------+-----+------+----------+-------+

当我像 Gordon 告诉我的那样执行查询时,我会额外收到以下内容

  SELECT ip,
         lastattack
    FROM blacklist
   WHERE ip = 'xxx.xxx.xxx.xxx'
GROUP BY ip, lastattack

使用索引条件;使用哪里;使用临时的;使用文件排序

人们告诉我要避免临时或文件排序。

【问题讨论】:

  • 您遇到问题的重写查询是什么?
  • @EdmCoff 在帖子中添加了查询忘记了
  • 有什么问题?
  • 如果我必须将选定的列放在组中,为什么 mysql 解释给我使用临时和文件排序。从答案中我读到了它的说法,以避免在您的查询中临时解释

标签: mysql database indexing database-design


【解决方案1】:

就目前而言,您的查询不正确 - 或者更确切地说,它是模棱两可的。假设你有:

192.168.0.1    Attack1    2017-10-01 23:30
192.168.0.1    Attack2    2017-10-01 23:35

lastattack 应该输出哪个值?您没有向服务器提供足够的数据,服务器无法阅读您的想法并推测如果该字段称为“lastattack”,您可能想要具有最大时间戳的那个。

这就是 Gordon Linoff 所说的——“选择中的所有列都应该是 GROUP BY 中的列,或者使用聚合函数”;在这里,lastattack 两者都不是,因为您不 GROUP BY lastattack(但仅按 IP),并且您不聚合它(您选择 lastattack,而不是 AVG(lastattack) 或 SOME_AGGREGATE_FUNCTION(lastattack))。

可能仍会获得正确的值 - 但您可能不会。在实践中,将以确定的顺序检索记录,这很可能是您想要的顺序。但其他 DB 实现可能会获取它们遇到的 first 值,并让您接受第一次攻击而不是最后一次攻击。

要获得你想要的结果,你首先需要确定最后一次攻击的日期:

SELECT ip, MAX(attackdate) AS maxdate FROM blacklist GROUP BY ip;

这会为您提供一个带有正确时间戳的表格。要获得最后一次攻击,您需要一个 JOIN(如果两个攻击在同一秒内出现,则可能会出现重复,因此您无法确定 哪个 是最后一个):

SELECT a.ip, a.maxdate, b.lastattack
    FROM (
        SELECT ip, MAX(attackdate) AS maxdate FROM blacklist GROUP BY ip
    ) AS a
JOIN blacklist AS b ON (a.ip = b.ip AND a.maxdate = b.lastattack)

对于内部查询,您需要在 ipattackdate 上建立一个索引,这也应该适用于外部查询。您可能希望按此顺序在ipattackdatelastattack 上创建一个索引,看看这是否会发生任何变化。

【讨论】:

  • 为什么lastattack都没有?我怎么知道这个? lastattack 将始终更新为最新的攻击日期,因此不会有新记录仅更新
  • 既不是因为它不在 GROUP BY 中(你只能按 ip 分组)也不在一个组函数中(你不使用,比如说,MAX(lastattack) 或类似的东西那)。但是wait...如果总是更新lastattack,这是否意味着每个IP只有一行?如果是这样,为什么要按 IP 分组?
  • 我正在尝试检查是否需要在第一个查询中将所有选定的列添加到 group by 中,其中我只将 ip 分组为它的工作。但是在第二个查询中,我使用 ip 和 lastattack 分组,当我这样做时,我收到临时文件,并且文件排序也被 extra 使用,但为什么?如果我只遵循其他数据库管理员的指示
  • “临时”警告是因为数据库需要收集更多数据。然后需要对其进行排序。问题是您需要这样做,因为您的不完整分组更简单、更快,但只是偶然。使用 每个 IP 单行 并在更新时触发是一种解决方案,它允许您完全删除 GROUP BY,使用 IP 上的唯一索引和 ON DUPLICATE KEY UPDATE 来存储其他字段.但这一切都取决于您的情况。
  • 哦,但你这样做不是为了优化查询!您组织组以使查询工作。然后你设计索引(和/或重组查询,使用视图,使用辅助表......)来优化它。你可以有一个没有另一个,或者两者都有,或者两者都没有。
猜你喜欢
  • 2012-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-01
  • 2018-11-30
相关资源
最近更新 更多