【问题标题】:MySQL performance boost after create & drop index创建和删除索引后 MySQL 性能提升
【发布时间】:2012-09-03 11:09:48
【问题描述】:

我有一个大型 MySQL、MyISAM 表,大约有 400 万行,运行在一个核心 2 双核、8G RAM 笔记本电脑上。

此表共有 30 列,包括 varchar、decimal 和 int 类型。

我在 varchar(16) 上有一个索引。让我们将此列称为:“indexed_varchar_column”。

我的查询是

SELECT 9 columns FROM the_table WHERE indexed_varchar_column = 'something';

它总是为我查询的每个“东西”返回大约 5000 行。

查询的解释返回:

+----+-------------+-------------+------+----------------------------------------------------+--------------------------------------------+---------+-------+------+-------------+
| id | select_type | table       | type | possible_keys                                      | key                                        | key_len | ref   | rows | Extra       |
+----+-------------+-------------+------+----------------------------------------------------+--------------------------------------------+---------+-------+------+-------------+
|  1 | SIMPLE      | the_table   | ref  | many indexes including indexed_varchar_column      | another_index NOT: indexed_varchar_column! | 19      | const | 5247 | Using where |
+----+-------------+-------------+------+----------------------------------------------------+--------------------------------------------+---------+-------+------+-------------+

首先我不确定为什么选择 another_index。实际上,它选择了一个索引,该索引是 indexed_varchar_column 和另外 2 列(构成所选列的一部分)的复合索引。也许这是有道理的,因为不必读取查询中的 2 列可能会使事情变得更快。真正的问题是以下一个

对于我匹配的每个“东西”,查询需要 5 秒。第二次我查询“某事”需要 0.15 秒(我猜是因为查询正在被缓存)。当我对“something_new”运行另一个查询时,又需要 5 秒。所以,它是一致的。

问题是:我发现创建一个索引(另一个复合索引,包括我的 indexed_varchar_column)并再次删除它会产生针对新“something_other”的所有进一步查询只需 0.15 秒。请注意 1) 我创建了一个索引 2) 再次删除它。所以一切都处于相同的状态。

我猜想构建和删除索引所需的所有操作都会使 SQL 引擎缓存某些东西,然后再重用。当我在所有这些之后对查询运行 EXPLAIN 时,我得到的结果与以前完全相同。

如何继续了解创建-删除索引过程中缓存的内容,以便在不操作索引的情况下对其进行缓存?

更新:

根据 Marc B 的评论,建议当 mySQL 创建索引时,它会在内部执行 SELECT... 我尝试了以下操作:

SELECT * FROM my_table;

花费了 30 秒并返回了 400 万行。好消息是所有进一步的查询再次非常快(直到我重新启动系统)。请注意,重新启动后查询又变慢了。我猜这是因为 mySQL 正在使用某种操作系统缓存。

有什么想法吗?如何显式缓存我猜的表?

更新 2: 或许我应该提到这张表可能严重碎片化。它有 400 万行,但我会定期删除很多旧字段。我还添加了新的。由于我每天的 ID (删除的行)有很大的差距,我删除了主索引 (ID) 并用连续的数字再次创建它。该表可能会非常碎片化,因此 IO 一定是个问题......不知道该怎么办。

【问题讨论】:

  • 在内部构建索引基本上是“选择字段,in,index from ...”来获取构建索引所需的数据,因此只需构建索引即可为您缓存数据索引,即使您自己没有运行任何选择。
  • 感谢 Marc B。这给了我一个想法。我在 400 万行上运行了一个疯狂的“SELECT * FROM my_table”。我在控制台上做了这个并丢弃了所有结果。我花了一段时间。创建索引所需的相同时间,大约。所有进一步的查询都是超快的。有没有其他优雅的方式达到同样的效果?我会用这个发现更新问题。
  • 所以你有两个包含indexed_varchar_column的索引:一个是{indexed_varchar_column, other_column1, other_column2}上的复合索引。其他指数如何?它是复合的吗?其中列的顺序是什么?
  • 布兰科,你可以忘记索引。一个简单的 SELECT * FROM my_table 缓存所有内容。我相信我在硬盘上有一个 IO 瓶颈(我需要查询的列太多,覆盖索引太大)。当我运行 SELECT * 时,IO 瓶颈得到缓解。我想我需要明确地缓存表。

标签: mysql sql caching indices


【解决方案1】:

感谢大家的帮助。

最后我发现(感谢 Marc B 的提示)我的表在多次 INSERT 和 DELETE 后严重碎片化。几个小时前,我用这个信息更新了这个问题。有两件事有帮助:

1)

ALTER TABLE my_table ORDER BY indexed_varchar_column;

2) 运行:

myisamchk --sort-records=4 my_table.MYI  (where 4 corresponds to my index)

我相信这两个命令是等效的。即使在系统重新启动后查询也很快。 我已将此 A​​LTER TABLE ORDER BY 命令放在每天运行的 cron 上。这需要 2 分钟,但值得。

【讨论】:

    【解决方案2】:

    复合索引中列的顺序是什么。

    您必须(至少)使用查询中列的左关联子集

    如果你有一个关于 foo、bar 和 baz 的索引,那将被他们自己用作针对 bar 或 baz 的索引。只有 (foo)、(foo,bar) 和 (foo,bar,baz)。

    EXPLAIN 是你的朋友。它会告诉您查询正在使用哪个索引(如果有)。

    编辑这里是一个用于比较的简单左连接查询的 postgres 解释。

    Nested Loop Left Join  (cost=0.00..16.97 rows=13 width=103)
        Join Filter: (pagesets.id = pages.pageset_id)
          ->  Index Scan using ix_pages_pageset_id on pages  (cost=0.00..8.51 rows=13 width=80)
                  Index Cond: (pageset_id = 515)
          ->  Materialize  (cost=0.00..8.27 rows=1 width=23)
              ->  Index Scan using pagesets_pkey on pagesets  (cost=0.00..8.27 rows=1 width=23)
                    Index Cond: (id = 515)
    

    【讨论】:

    • 好吧,我不确定我听不懂泰勒。我有多个复合索引,但 indexed_varchar_column 有一个索引。然后,这个索引没有被使用,因为 mysql 决定使用另一个索引,正如我解释的那样。真正的问题在我帖子的底部。我不明白为什么创建和删除索引会使所有进一步的查询快 2000 倍以上
    • 好的,我知道你现在在哪里为问题添加了更多内容。坦白说,Mysql是真的。它的查询计划是出了名的糟糕。如果这种事情困扰您(而且应该如此),那么 PostgreSQL 是一个更好的选择。它的解释也好多了。
    【解决方案3】:

    您有多少个包含 indexed_varchar_column 的索引?您是否只有 indexed_varchar_column 的单个索引?

    您是否尝试过: SELECT 9 columns FROM USE INDEX (name_of_index) the_table WHERE indexed_varchar_column = 'something';?

    【讨论】:

    • 是的,我已经做到了。结果是一样的。我猜 mySQL 选择了另一个具有其他查询列的索引。这不是真正的问题。问题是为什么我在创建和删除索引后会获得如此高的性能提升。缓存了什么?关于有多少个索引包含 indexed_varchar_column:它在 3 个复合索引上。
    • 如何发布实际的、完整的、表和索引的声明?如有必要,请更改列名,但不要让我们玩通灵!
    • Tyler,这张桌子很大,我相信这会让整个事情变得更加混乱。我用另一个肯定指向缓存问题的发现更新了这个问题。
    猜你喜欢
    • 2011-02-24
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    • 2023-01-18
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多