【问题标题】:Best indexes to optimize a large MySQL table?优化大型 MySQL 表的最佳索引?
【发布时间】:2022-01-04 15:35:27
【问题描述】:

所以我有一个超过 1000 万行的 MySQL 表。每次进行新下载时都会创建一个新行,并在再次下载文件时更新该行(更新下载计数)。要检查该行是否已经存在,我执行以下 MySQL 查询:

SELECT `id`, `download_count` 
FROM `product_files_downloads` 
WHERE (`user_id` = ? AND `variant_id` = ? AND `product_files_id` = ? AND `order_id` = ?) 
ORDER BY `id` DESC LIMIT 1;

我在user_id 上设置了一个简单的索引,如果用户(由user_id 定义)在此表中没有很多行,则查询非常快。但是,我有一些用户的 user_id 附加了超过 100k 行,在这种情况下,查询需要几秒钟才能执行。

我是否应该像这样在 user_idproduct_files_id 上添加新索引:

ALTER TABLE `product_files_downloads` ADD INDEX `user_id_product_files_id_idx` (`user_id`, `product_files_id`);

...或者是否有更好的索引来加快查询速度?

【问题讨论】:

  • 从建议的索引开始,看看会发生什么。 (我们不知道数据分布,所以只能猜测……)

标签: mysql sql database-design database-performance


【解决方案1】:

两列(user_id、product_filed_id)上的索引会部分缩小搜索范围,但为什么不将其缩小到您正在搜索的行呢?

ALTER TABLE `product_files_downloads` ADD INDEX `myindex` (
  `user_id`, `variant_id`, `product_files_id`, `order_id`
);

id 列,假设这是您的主键,被隐式附加为第五列,这很有帮助,因为它将使 ORDER BY 成为空操作。如果您按索引的前 4 列搜索并按第 5 列排序,MySQL 知道如何扫描行以避免排序。

您可能想查看我的演示文稿How to Design Indexes, Reallyvideo

【讨论】:

    【解决方案2】:

    首先,您需要索引中WHERE 子句中使用的许多列。最佳情况下,这应该从最有选择性到最无选择性。

    假设有 100 个用户、两个变体、10000 个产品文件和 1000 个订单。那么产品文件 ID 可能是最具选择性的。如果您查找一个产品文件,您将获得表中大约 1/10000 的行。因此,通过索引中的第一列,您已经缩小了要查找的行的高度。

    这会导致

    create index idx1 on product_files_downloads
      (product_files_id, order_id, user_id, variant_id);
    

    如果您不确定选择性,您可以提供尽可能多的索引,因为您有想要尝试的组合:

    create index idx2 on product_files_downloads
      (product_files_id, user_id, order_id, variant_id);
    create index idx3 on product_files_downloads
      (order_id, product_files_id,  user_id, variant_id);
    

    当您发现 DBMS 未使用索引时,您可以随时再次删除索引。 (你会发现EXPLAIN)。

    然后,您可能希望将排序键与索引一起提供,以便以正确的顺序获取条目,而无需 DBMS 执行额外的排序:

    create index idx1 on product_files_downloads
      (product_files_id, order_id, user_id, variant_id, id);
    

    最后,如果您愿意,您甚至可以提供一个包含要选择的列的覆盖索引。因此,仅查找索引就足够了,并且不必访问表:

    create index idx1 on product_files_downloads
      (product_files_id, order_id, user_id, variant_id, id, download_count);
    

    【讨论】:

      【解决方案3】:

      使用INSERT ... ON DUPLICATE UPDATE ...。这将替换 SELECTINSERT UPDATE。它的运行速度可能是您当前代码的两倍。

      但是,IODKU 需要UNIQUE 索引来确定是否已经存在条目。我怀疑您的LIMIT 1 是不必要的,并且表可能

      UNIQUE(`user_id`, `variant_id`, `product_files_id`, `order_id`)
      

      最好有

      PRIMARY KEY(`user_id`, `variant_id`, `product_files_id`, `order_id`)
      

      (该索引中的列顺序取决于“选择性”;这是老生常谈。)

      也许您可以摆脱您的AUTO_INCREMENT id 并更改ORDER BY?如果不是,则添加INDEX(id) 以满足AUTO_INCREMENT

      请提供SHOW CREATE TABLE

      【讨论】:

        猜你喜欢
        • 2019-02-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-03
        • 2016-05-18
        • 1970-01-01
        相关资源
        最近更新 更多