【问题标题】:Query results taking too long on 200K database, speed up tips?200K数据库查询结果耗时过长,提速技巧?
【发布时间】:2011-02-09 01:17:57
【问题描述】:

我有一个 sql 语句,我将在其中加入大约 4 个表,每个表有 200K 行。查询运行,但一直冻结。当我对 3 个表进行连接时,它会返回行(大约需要 10 秒)。任何建议为什么?加快速度的建议?

谢谢!

代码

SELECT *
FROM equipment, tiremap, workreference, tirework
WHERE equipment.tiremap = tiremap.`TireID` AND 
      tiremap.`WorkMap` = workreference.`aMap` AND
      workreference.`bMap` = tirework.workmap
LIMIT 5

附言

如果有帮助的话,我正在使用 sql alchemy 生成此代码,用于此的 sqlalchemy 代码是

query = session.query(equipment, tiremap, workreference, tirework)
query = query.filter(equipment.c.tiremap == tiremap.c.TireID)
query = query.filter(tiremap.c.WorkMap==workreference.c.aMap)
query = query.filter(workreference.c.bMap == tirework.c.workmap)
query = query.limit(5)
query.all()

【问题讨论】:

  • 为了让它工作而排除的表是否总是同一个表?换句话说,你能让它与 4 个可能的表中的任何 3 个一起工作吗?还是总是有问题的 1 个特定表?
  • 您是否通过 EXPLAIN 运行它以查看它在做什么?这应该是你的第一步。
  • @MusiGenesis ,我能够以不错的速度运行前 3 个表的 sql,但是一旦我开始加入疲劳工作,它似乎就冻结了,没有响应,你认为是什么问题?

标签: sql mysql postgresql sqlalchemy


【解决方案1】:

可能是您要加入的第四张桌子比其他桌子大得多。也可能是您要加入的列上没有索引。

【讨论】:

    【解决方案2】:

    确保您有以下索引:

    • 设备(轮胎地图)
    • 轮胎图 (TireID)
    • 轮胎地图(工作地图)
    • 工作参考(地图)
    • 工作参考(bMap)
    • 轮胎(工作图)

    编辑:为了完整起见,我想我应该提供一些上下文。

    SQL 优化器查看一条语句,对其进行解析,然后根据查询、引用的表和可用索引为其确定执行计划。如果您执行SELECT * FROM tab1,那么它将对 tab1 进行全表扫描,因为没有其他方法可以执行该操作。

    如果您使用SELECT * FROM person WHERE lastname LIKE 'V%' 并且拥有一百万条记录,则查询每一行会很慢,但如果将lastname 编入索引,则效率会高得多。

    对于像您这样的查询,其中一个表将是驱动表,无论索引如何,都可以简单地作为全表扫描完成。这没有什么问题。一张表必须驱动查询。如果有 WHERE 子句(对于连接条件以外的其他内容),这可能会改变,但否则它通常是正确的。

    然后,MySQL 将从该驱动表开始将连接附加到执行计划。这些连接将需要另一侧的索引才能有效地工作。

    因此,对于三个表,您可能有一个未编入索引的表,但这并不重要,因为它驱动查询。对于第四个表,可能有两个未索引的表,这现在是一个问题,因为对于一个中的每一行,MySQL 都必须对另一个进行全表扫描。

    所以基本上你在每个外键和连接列上创建一个索引,这样 MySQL 就可以使用可用的东西为你给它的查询制定最佳执行计划。

    最后,大多数工具会告诉您有关数据库架构的信息。 PHPMyAdmin 是一种流行的托管数据库。就我个人而言,我实际上喜欢这种事情的桌面应用程序。 Navicat Lite 是一个不错的免费工具。

    【讨论】:

    • 另外,有没有办法知道表是否被索引?好的,nvm,我刚刚用谷歌搜索了它。谢谢。
    • @colorfulgrayscale 为我的声明添加了一些上下文。
    【解决方案3】:

    大多数 SQL 数据库都有一些“EXPLAIN PLAN”或“EXPLAIN”的变体,您可以使用它们来查看它是如何解析查询的。寻找全表扫描作为您需要索引的地方。

    【讨论】:

      【解决方案4】:

      您正在对 4 个表进行自然连接。此外,在您的“WHERE”语句中,没有特殊条件。

      数据库引擎会做以下事情:

      它将首先对每个表中的所有数据进行递归乘积。

      考虑表 A、B 和 C 中的以下行:

      A = rowA1
          rowA2
          rowA3;
      B = rowB1
          rowB2
          rowB3;
      C = rowC1
          rowC2
          rowC3;
      

      基本上,如果您对这 3 个表进行自然连接,引擎将在内存中拥有:

      rowA1 - rowB1 - rowC1
      rowA1 - rowB1 - rowC2
      rowA1 - rowB1 - rowC3
      rowA1 - rowB2 - rowC1
      rowA1 - rowB2 - rowC2
      rowA1 - rowB2 - rowC3
      rowA1 - rowB3 - rowC1
      rowA1 - rowB3 - rowC2
      rowA1 - rowB3 - rowC3
      ...
      ...
      ...
      rowA3 - rowB3 - rowC1
      rowA3 - rowB3 - rowC2
      rowA3 - rowB3 - rowC3
      

      总共有 27 行放入内存中。但是,我们只需要 3 行:

      rowA1 - rowB1 - rowC1
      rowA2 - rowB2 - rowC2
      rowA3 - rowB3 - rowC3
      

      如果您的数据库引擎不自己进行优化,那么 3 表的自然连接是非常昂贵的。对于 4 个表,即使是有限的行数也是不可想象的。

      现在,我们怎样才能得到更好的东西?

      首先,通过查看代码,我们知道我们只需要 5 个值。另外,在数据库优化中,据说应该让 SELECT 尽可能早。

      这里有一些未经测试的代码可以帮助你。您可能需要修改它,具体取决于您使用的数据库引擎:

      SELECT *
      FROM (SELECT * FROM equipment LIMIT 5) e, tiremap, workreference, tirework
      WHERE e.tiremap = tiremap.TireID AND
            tiremap.WorkMap = workreference.`aMap` AND
            workreference.`bMap` = tirework.workmap
      

      仅通过这样做,就应该感觉我们只有 3 张桌子,而不是 4 张桌子。不过,这并不是您真正想要的。如果其他表中没有引用“设备”的一行,那么最后您将获得少于 5 行。但是,这是一个向您展示我们可能并不真正需要所有表中的所有行的示例。

      现在,我认为你想要的可能是这样的:

      SELECT * FROM equipment 
      INNER JOIN tiremap ON equipment.tiremap = tiremap.TireID
      INNER JOIN workreference ON tiremap.WorkMap = workreference.aMap
      INNER JOIN tirework ON workreference.bMap = tirework.workmap
      LIMIT 5
      

      您可能会遇到问题:如果您的引擎不是那么好(mySQL,抱歉),这可能需要很长时间。

      如果你真的想自己做优化:

      SELECT * FROM tirework, 
         (SELECT * FROM workreference, 
             (SELECT * FROM tiremap,
                 (SELECT * FROM equipment) e
              WHERE e.tiremap = tiremap.TireID) t
          WHERE t.WorkMap = workreference.aMap) w
      WHERE w.bMap = tirework.workmap
      LIMIT 5
      

      瞧!即使您的引擎优化器不存在,该查询也不应该花费太长时间。您的引擎不会将所有内容都制作成大产品,而是一次做一个产品,并在将坏行加入新表之前将其删除。

      试试看。

      【讨论】:

      • zomg,非常感谢。这正是我一直在寻找的。和平。
      猜你喜欢
      • 2019-08-02
      • 2011-11-03
      • 2015-10-23
      • 2012-11-04
      • 2013-07-24
      • 2015-09-08
      • 2016-11-04
      • 2021-10-07
      相关资源
      最近更新 更多