【问题标题】:How to Optimize Queries in a Database - The Basics如何优化数据库中的查询 - 基础
【发布时间】:2011-03-12 14:54:45
【问题描述】:

似乎所有关于这个主题的问题都非常具体,虽然我重视具体的例子,但我对 SQL 优化的基础知识很感兴趣。我很擅长使用 SQL,并且有硬件/低级软件方面的背景。

我想要的是既是有形软件的工具,也是一种查看我定期查看的 mysql 数据库并了解 join 语句顺序和 where 语句之间的区别的方法。

我想知道为什么索引会有所帮助,例如,究竟是为什么。我想具体了解发生了什么不同,我想知道我如何才能真正看到正在发生的事情。我不需要一个工具来分解我的 SQL 的每一步,我只想能够四处寻找,如果有人不能告诉我要索引哪一列,我将能够拿出一张纸然后在一段时间内能够想出答案。

数据库很复杂,但它们并没有那么复杂,而且必须有一些很好的材料来学习基础知识,以便您知道如何找到遇到的优化问题的答案,即使可以找到确切的答案在论坛上回答。

请推荐一些简洁、直观且不怕深入到低层次的具体细节的阅读材料。我更喜欢在线免费资源,但如果一本书的推荐能打破它击中的钉头,我会考虑接受它。

【问题讨论】:

    标签: sql mysql database optimization query-optimization


    【解决方案1】:

    您必须查找每个 where 条件和每个 join...on 条件。两者的工作方式相同。

    假设我们写

    select name
    from customer
    where customerid=37;
    

    DBMS 必须以某种方式找到 customerid=37 的记录。如果没有索引,唯一的方法是读取表中的每条记录,将 customerid 与 37 进行比较。即使找到一条,它也无法知道只有一条,所以它必须不断寻找其他人。

    如果您在 customerid 上创建索引,DBMS 可以快速搜索索引。这不是顺序搜索,而是根据数据库,二分搜索或其他一些有效方法。究竟如何并不重要,接受它比顺序快得多。然后索引将其直接带到适当的记录或记录。此外,如果您指定索引是“唯一的”,那么数据库知道只能有一个,因此它不会浪费时间寻找第二个。 (并且 DBMS 会阻止您添加第二个。)

    现在考虑这个查询:

    select name
    from customer
    where city='Albany' and state='NY';
    

    现在我们有两个条件。如果您只有其中一个字段的索引,则 DBMS 将使用该索引来查找记录的子集,然后按顺序搜索这些记录。例如,如果您有一个关于 state 的索引,DBMS 将快速找到 NY 的第一条记录,然后依次搜索 city='Albany',并在到达 NY 的最后一条记录时停止查找。

    如果您有一个包含两个字段的索引,即“在客户(州、城市)上创建索引”,那么 DBMS 可以立即放大到正确的记录。

    如果您有两个单独的索引,每个字段上都有一个,DBMS 将有各种规则来决定使用哪个索引。同样,具体如何完成这取决于您使用的特定 DBMS,但基本上它会尝试保留有关记录总数、不同值的数量和值的分布的统计信息。然后它将按顺序搜索这些记录,以寻找满足另一个条件的记录。在这种情况下,DBMS 可能会观察到城市比州多得多,因此通过使用城市索引,它可以快速放大到“奥尔巴尼”记录。然后它将依次搜索这些,检查每个与“NY”的状态。如果您有加利福尼亚州奥尔巴尼的记录,这些将被跳过。

    每个连接都需要某种查找。

    假设我们写

    select customer.name
    from transaction
    join customer on transaction.customerid=customer.customerid
    where transaction.transactiondate='2010-07-04' and customer.type='Q';
    

    现在 DBMS 必须决定首先读取哪个表,从那里选择适当的记录,然后在另一个表中找到匹配的记录。

    如果您有关于 transaction.transactiondate 和 customer.customerid 的索引,那么最好的计划可能是查找具有此日期的所有交易,然后为每个交易找到具有匹配 customerid 的客户,然后验证客户有正确的类型。

    如果您在 customer.customerid 上没有索引,那么 DBMS 可以快速找到该交易,但是对于每个交易,它必须按顺序搜索客户表以寻找匹配的 customerid。 (这可能会很慢。)

    假设您拥有的唯一索引位于 transaction.customerid 和 customer.type 上。然后 DBMS 可能会使用完全不同的计划。它可能会扫描客户表中所有类型正确的客户,然后为每个客户查找该客户的所有交易,并按顺序搜索它们以找到正确的日期。

    优化最重要的关键是找出真正有帮助的索引并创建这些索引。额外的,未使用的索引是数据库的负担,因为维护它们需要工作,如果它们从未使用过,这就是浪费。

    您可以使用 EXPLAIN 命令确定 DBMS 将使用哪些索引来处理任何给定的查询。我一直使用它来确定我的查询是否得到了很好的优化,或者我是否应该创建额外的索引。 (阅读有关此命令的文档以了解其输出的说明。)

    警告:请记住,我说过 DBMS 会统计每个表中的记录数和不同值的数量等。如果数据发生变化,EXPLAIN 今天可能会给你一个与昨天完全不同的计划。例如,如果您有一个连接两个表的查询,其中一个非常小而另一个很大,那么它会偏向于先读取小表,然后在大表中查找匹配的记录。向表中添加记录可以改变哪个更大,从而导致 DBMS 改变其计划。因此,您应该尝试对具有真实数据的数据库执行 EXPLAINS。针对每个表中包含 5 条记录的测试数据库运行比针对实时数据库运行的价值要小得多。

    嗯,还有很多话要说,但我不想在这里写书。

    【讨论】:

    • 哇,有很多信息,谢谢,我从阅读中学到了一些可以立即使用的东西
    【解决方案2】:

    假设您正在另一个城市寻找朋友。一种方法是挨家挨户询问这是否是您要找的房子。另一种方法是看地图。

    索引是表的映射。它可以准确地告诉数据库引擎你要找的东西在哪里。因此,您为您认为必须搜索的每一列编制索引,而忽略那些您只是从中读取数据而从不搜索的列。

    良好的技术阅读 about indicesabout ORDER BY optimization。如果你想看看到底发生了什么,你需要EXPLAIN 声明。

    【讨论】:

    • 另外,mysql的慢日志值得观察。 dev.mysql.com/doc/refman/5.0/en/slow-query-log.html
    • 我对索引如何影响连接特别感兴趣,我经常使用连接,但并不真正了解它们在低级别是如何工作的。例如,如果您有两个可能非常大的索引列相互连接,这是否重要?连接空间如何分配和遍历?如果它们都被索引了怎么办?如果两者都没有被索引呢?
    • 基本上,MySQL 手册的整个第 7.2 章都很有趣。如果一列没有被索引,您最多需要 n 次比较才能找到一些东西。如果是,您最多需要 log(n) 比较。基准的长度肯定是一个因素,但索引更重要。但是,我发现我几乎从不加入非整数字段。我的策略是,如果它有很大的重复机会,它应该有一个表和一个主键。像您这样的“假设”问题最好通过构建模型并在示例查询上运行 EXPLAIN 来回答。
    • 扩大一点,连接通常是双重搜索;与搜索有关的一切都与连接有关;因此,无论您加入什么,都最好将其编入索引。
    • 我还没有机会查看您提供的文档,我会在查看时更新
    【解决方案3】:

    不要考虑优化数据库。考虑优化查询。

    通常,您会以牺牲其他情况为代价来优化一种情况。您只需要决定您感兴趣的案例。

    【讨论】:

      【解决方案4】:

      “我对索引如何影响连接特别感兴趣”

      我以等值连接为例(SELECT FROM A,B WHERE A.x = B.y)。

      如果根本没有索引(这在理论上是可能的,但我认为在 SQL 中不行),那么基本上计算连接的唯一方法是获取整个表 A 并将其分区到 x 上,获取整个表y 并在 y 上对其进行分区,然后匹配分区,最后为每对匹配的分区计算结果行。除了最小的表之外,这对于所有表来说都是昂贵的(甚至由于内存限制而完全不可能)。

      如果在 A 和/或 B 上确实存在索引,但没有任何索引具有 x ,则情况相同。 y 作为它的第一个属性。

      如果在 x 上确实存在索引,但在 y 上不存在(或相反),那么另一种可能性打开了:扫描表 B,为每一行选择值 y,在索引中查找该值并获取相应的 A 行计算连接。请注意,如果没有其他进一步的限制适用(AND z = ...),这仍然不会为您赢得太多 - 除了 x 和 y 值之间只有很少匹配的情况。

      如果 x 和 y 上都存在有序索引(基于哈希的索引不是有序的),那么第三种可能性就会出现:对索引本身进行匹配扫描(索引本身可能小于表本身,因此扫描索引本身将花费更短的时间),并且对于匹配的 x/y 值,计算相应行的连接。

      这是基线。 x>y 等上的连接会出现变化。

      【讨论】:

        【解决方案5】:

        我不了解 MySql 工具,但在 MS SqlServer 中,您有一个工具可以显示查询将执行的所有操作以及整个查询的处理时间。

        使用这个工具帮助我理解了查询优化器是如何优化查询的,这比我认为任何书都更能帮助我,因为优化器所做的事情通常不容易理解。通过调整查询和可能的下划线数据库,我可以看到每个更改如何影响查询计划。编写查询有一些关键点,但在我看来,您似乎已经对这些有所了解,因此在您的案例中进行优化比任何一般规则都更重要。经过几年的数据库开发,我确实看过一些专门针对 SQL Server 上的数据库优化的书籍,但发现有用的信息很少。

        快速谷歌搜索想出了这个:http://www.mysql.com/products/enterprise/query.html 这听起来像一个类似的工具。

        这当然是在查询级别,数据库级别的优化又是另一回事,但是您正在查看诸如数据库在硬盘驱动器上的划分方式等参数。至少在 SqlServer 中您可以选择将表划分为不同的硬盘甚至磁盘板,这可能会产生很大的影响,因为驱动器和驱动器磁头可以并行工作。另一个是您如何构建查询,以便数据库可以在多个线程和处理器中并行运行它们,但这两个问题再次取决于您使用的数据库引擎甚至版本。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-02-27
          • 1970-01-01
          • 2016-05-02
          • 1970-01-01
          • 2019-03-30
          • 2017-01-11
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多