MySQL / MariaDB InnoDB 索引停止工作

Posted

技术标签:

【中文标题】MySQL / MariaDB InnoDB 索引停止工作【英文标题】:MySQL / MariaDB InnoDB indexes stops working 【发布时间】:2013-01-26 10:17:08 【问题描述】:

我的 mysql/MariaDB InnoDB/XtraDB 出现异常行为。最近切换到 MariaDB 5.5。切换使服务器整体性能更高,但我仍然有这个问题。

一个特定的表索引似乎不时中断。一段时间后,它会自行修复。

SHOW CREATE TABLE article_inventory; 给了

CREATE TABLE `article_inventory` (
    `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
    `article_variant_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
    `season_id` BIGINT(20) UNSIGNED NOT NULL,
    `warehouse_id` BIGINT(20) UNSIGNED NOT NULL,
    `quantity` BIGINT(20) NOT NULL DEFAULT '0',
    PRIMARY KEY (`id`),
    UNIQUE INDEX `unique_inventory_idx` (`article_variant_id`, `season_id`, `warehouse_id`),
    INDEX `article_variant_id_idx` (`article_variant_id`),
    INDEX `article_inventory_season_id_idx` (`season_id`),
    INDEX `article_inventory_warehouse_id_idx` (`warehouse_id`),
    CONSTRAINT `article_inventory_article_variant_id_article_variant_id` FOREIGN KEY (`article_variant_id`) REFERENCES `article_variant` (`id`),
    CONSTRAINT `article_inventory_season_id_season_id` FOREIGN KEY (`season_id`) REFERENCES `season` (`id`),
    CONSTRAINT `article_inventory_warehouse_id_warehouse_id` FOREIGN KEY (`warehouse_id`) REFERENCES `warehouse` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=3827622858;

编辑:大多数 SELECT 查询都是针对此表进行的。每小时都会进行一次重大更新。有时非常大的更新。

运行此查询:

SELECT a.id
FROM article a
    INNER JOIN article_variant a2 
        ON a.style_id = a2.style_id
    INNER JOIN article_block a3 
        ON a2.po = a3.po
    INNER JOIN color c 
        ON a2.color_id = c.id
    INNER JOIN size s 
        ON a2.size_id = s.id
    INNER JOIN article_group a4 
        ON a2.id = a4.article_variant_id AND (a4.season_id = 6)
    INNER JOIN article_inventory a5 
        ON a2.id = a5.article_variant_id AND (((a5.warehouse_id = 5 OR a5.warehouse_id = 1) AND a5.season_id = 6))
    INNER JOIN article_date a6 
        ON a.style_id = a6.style_id AND ((a6.pricelist_id = 5 AND a6.season_id = 6))
    INNER JOIN article_price a7 
        ON a.style_id = a7.style_id AND ((a7.pricelist_id = 5 AND a7.season_id = 6))
    INNER JOIN pricelist p 
        ON a7.pricelist_id = p.id
    INNER JOIN concept c2 
        ON a4.concept_id = c2.id
    INNER JOIN category c3 
        ON a4.category_id = c3.id
    LEFT JOIN order_cart_row o 
        ON a2.id = o.article_variant_id AND (o.order_id = 17035)
    LEFT JOIN shortlist s2 
        ON a.id = s2.article_id AND (s2.order_id = 17035)
WHERE ((a2.is_canceled <> 1 AND a4.is_canceled <> 1) OR o.quantity IS NOT NULL) AND c2.id = 2
GROUP BY a.id

...应该在大约 0.5-1.0 秒内执行,并给我一个类似这样的解释:

id select_type table  type     possible_keys                                                                                               key                                 key_len    ref                                  rowsExtra
1  SIMPLE      p      const    PRIMARY                                                                                                     PRIMARY                             8          const                                1   Using index; Using temporary; Using filesort
1  SIMPLE      c2     const    PRIMARY                                                                                                     PRIMARY                             8          const                                1   Using index                            
1  SIMPLE      a3     index    PRIMARY                                                                                                     PRIMARY                             98         NULL                                 1031Using where                            
1  SIMPLE      a2     ref      PRIMARY,unique_variant_idx,color_id_idx,style_id_idx,size_id_idx,article_variant_po_idx                     article_variant_po_idx              98         wsp_stage.a3.po                      14  Using where                            
1  SIMPLE      s      eq_ref   PRIMARY                                                                                                     PRIMARY                             11         wsp_stage.a2.size_id                 1   Using index                            
1  SIMPLE      c      eq_ref   PRIMARY                                                                                                     PRIMARY                             11         wsp_stage.a2.color_id                1                                          
1  SIMPLE      o      eq_ref   unique_rows_idx,article_variant_id_idx,order_id_idx                                                         unique_rows_idx                     16         const,wsp_stage.a2.id                1   Using index                            
1  SIMPLE      a      eq_ref   unique_style_idx                                                                                            unique_style_idx                    767        wsp_stage.a2.style_id                1   Using index                            
1  SIMPLE      a6     ref      article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx                             article_season_pricelist_unique_idx 784        wsp_stage.a2.style_id,const,const    1   Using index                            
1  SIMPLE      a7     ref      article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx                             article_season_pricelist_unique_idx 784        wsp_stage.a2.style_id,const,const    1   Using index                            
1  SIMPLE      a4     eq_ref   unique_group_idx,one_per_season_idx,category_id_idx,concept_id_idx,season_id_idx,article_variant_id_idx     one_per_season_idx                  16         wsp_stage.a2.id,const                1   Using index                            
1  SIMPLE      c3     eq_ref   PRIMARY                                                                                                     PRIMARY                             8          wsp_stage.a4.category_id             1   Using index                            
1  SIMPLE      s2     ref      shortlist_article_id_idx                                                                                    shortlist_article_id_idx            8          wsp_stage.a.id                       10  Using where                            
1  SIMPLE      a5     ref      unique_inventory_idx,article_variant_id_idx,article_inventory_season_id_idx,article_inventory_warehouse_id_iunique_inventory_idx                17         wsp_stage.a2.id,const                8   Using where 

当一切正常时,article_inventory(别名 a5)使用unique_inventory_idxarticle_variant_id_idx。两者都应该给我大约 5-100 行检查。

但时不时会发生一些事情,同样的查询需要大约 30 秒,并给我这样的解释:

id select_type table  type     possible_keys                                                                                               key                                 key_len  ref                                  rows    Extra
1  SIMPLE      p      const    PRIMARY                                                                                                     PRIMARY                             8        const                                1       Using index; Using temporary; Using filesort
1  SIMPLE      c2     const    PRIMARY                                                                                                     PRIMARY                             8        const                                1       Using index
1  SIMPLE      a5     ref      unique_inventory_idx,article_variant_id_idx,article_inventory_season_id_idx,article_inventory_warehouse_id_iarticle_inventory_season_id_idx     8        const                                6718732 Using where
1  SIMPLE      a4     eq_ref   unique_group_idx,one_per_season_idx,category_id_idx,concept_id_idx,season_id_idx,article_variant_id_idx     one_per_season_idx                  16       wsp_stage.a5.article_variant_id,const1       Using where
1  SIMPLE      c3     eq_ref   PRIMARY                                                                                                     PRIMARY                             8        wsp_stage.a4.category_id             1       Using index
1  SIMPLE      a2     eq_ref   PRIMARY,unique_variant_idx,color_id_idx,style_id_idx,size_id_idx,article_variant_po_idx                     PRIMARY                             8        wsp_stage.a5.article_variant_id      1       
1  SIMPLE      c      eq_ref   PRIMARY                                                                                                     PRIMARY                             11       wsp_stage.a2.color_id                1       Using index
1  SIMPLE      a      eq_ref   unique_style_idx                                                                                            unique_style_idx                    767      wsp_stage.a2.style_id                1       Using index
1  SIMPLE      a6     ref      article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx                             article_season_pricelist_unique_idx 784      wsp_stage.a2.style_id,const,const    1       Using index
1  SIMPLE      a7     ref      article_season_pricelist_unique_idx,season_id_idx,pricelist_id_idx,style_id_idx                             article_season_pricelist_unique_idx 784      wsp_stage.a2.style_id,const,const    1       Using index
1  SIMPLE      s      eq_ref   PRIMARY                                                                                                     PRIMARY                             11       wsp_stage.a2.size_id                 1       Using index
1  SIMPLE      a3     eq_ref   PRIMARY                                                                                                     PRIMARY                             98       wsp_stage.a2.po                      1       Using index
1  SIMPLE      o      eq_ref   unique_rows_idx,article_variant_id_idx,order_id_idx                                                         unique_rows_idx                     16       const,wsp_stage.a5.article_variant_id1       Using where
1  SIMPLE      s2     ref      shortlist_article_id_idx                                                                                    shortlist_article_id_idx            8        wsp_stage.a.id                       7       Using where

article_inventory (a5) 现在使用article_inventory_season_id_idx。一个非常非常糟糕的索引,因为它是所有索引中第二不具体的。给了我 6718732 个检查的行。

my.ini:

[mysqld]
datadir="W:/mariadb/data/"
port=3306
sql_mode="STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION"
default_storage_engine=innodb
innodb_buffer_pool_size=5000M
innodb_log_file_size=52428800
innodb_file_per_table
innodb_file_format=Barracuda

[client]
port=3307

【问题讨论】:

这张表是否有大量更新? 是的,我正要更新问题。大量阅读和大型期刊INSERT ... ON DUPLICATE KEY UPDATEs. 您能否将article_inventory a5 ON a2.id = a5.article_variant_id AND (((a5.warehouse_id = 5 OR a5.warehouse_id = 1) AND a5.season_id = 6)) 替换为(select list_of_cols from article_inventory a5 ON a2.id = a5.article_variant_id AND a5.warehouse_id = 5 and a5.season_id = 6 union all select list_of_cols from article_inventory a5 ON a2.id = a5.article_variant_id AND a5.warehouse_id = 1 and a5.season_id = 6) as a5。我会尝试调查以下内容:1)在 (warehouse_id + season_id) 上创建另一个索引 2)使用 where 和 only 对表进行预过滤,而不是与其他 tbl 连接。 【参考方案1】:

首先索引没有损坏。这可能与建议查询优化器使用错误索引的表上的 MySQL 统计信息有关。现在,在我们转向可能的解决方案之前,让我们先了解是什么原因造成的。

当 MySQL 运行查询时,它会查看该表的统计信息以确定哪些索引适合查询,然后根据建议选择正确的索引。表统计信息包含索引基数和与使用索引相关的成本等信息。 MySQL 将在每次运行查询时查看这些统计信息以确定最佳执行路径。

现在,由于索引是存储在磁盘上的实际数据结构,当您更新、插入和删除这些索引时,它们的统计信息会发生变化。这可能是问题的根本原因。 InnoDB 通过对索引结构进行 8 次随机深入研究来动态更新统计信息。 MyISAM 的做法不同。有关更多信息,请参阅此链接:https://dba.stackexchange.com/questions/3398/from-where-does-the-mysql-query-optimizer-read-index-statistics

您提到您每隔一段时间就会使用重复更新进行大型插入。我怀疑无论是在插入期间还是在插入发生之后,都会有一小段时间表的 innodb 统计信息已过时或正在编译。这可能就是您看到从一个指数到感染指数的零星变化的原因。那时您的统计信息不正确,查询优化器做出了错误的选择。

去谷歌以下:

mysql statistics update

有一整套链接,其中包含有关此内容的更多详细信息,值得阅读。

我以前在数据库上看到过这种情况,这不是 BUG,只是需要注意。

可能的解决方案:

    在批量插入后使用重复更新语句显式调用相关表上的 ANALYZE TABLE。在更新后直接运行此命令可能会将您的统计信息转换为正确的形式,从而建议正确的索引。不利的一面是,您的系统实际上可能会重新编译两次统计信息,这有点浪费资源。请记住,我不确定这个问题是在插入语句之后还是期间发生。 在您的选择语句中强制使用正确的索引。您可以强制 MySQL 始终使用正确的索引。然而,这是一个坏主意。在某些时候,另一个索引可能会针对您的查询变得更加优化,并且由于您现在正在有效地将索引硬编码以用于查询,因此以后会成为一个问题。 保持原样,这听起来可能很奇怪,但运行 30 秒的查询是否是一场灾难?这可能取决于您的要求,但如果 30 秒可以让查询运行,为什么要尝试修复它?请记住,如果它没有损坏,请不要修复它的理念。

如果您需要更多说明,我希望发表评论是有意义的.....

【讨论】:

读起来很有趣。它引导我做我所做的事情来平息问题。 我的情况是这样的,我的桌子非常不稳定。读取负载和插入/更新负载。快速增长。但这些数据在历史上并不重要。我通过完全截断它并从我们的主源重新创建数据来让它平静下来。它导致表格缩小了 80%。 (我这边的维护不好,因为之前没有这样做)。实际上,这导致任一索引的表现大致相同。甚至全表扫描也是可以接受的(使用 USE INDEX() 禁用索引)。非常感谢! :D "保持原样,这可能听起来很奇怪,但运行 30 秒的查询是否是一场灾难?这可能取决于您的要求,但如果 30 秒可以让查询运行,为什么要尝试修复它? 记住,如果它没有坏,就不要修复它的哲学。严重地?我希望你在写这篇文章时心情很有趣 @Dominique 因此,如果我查询一个包含 100 000 000 行的 1TB 数据,我将加入该报告,以获取每月运行一次的报告,供 CEO 看 5 分钟(如果需要 30 秒)问题是什么?有些查询只是不需要高度优化,它是对工程的调用。请记住,添加新索引会导致新的维护。为什么你会为了没人会注意到的收益而承担更多的维护?

以上是关于MySQL / MariaDB InnoDB 索引停止工作的主要内容,如果未能解决你的问题,请参考以下文章

在 MariaDB/MySQL 中不加锁地删除?`(InnoDB)

如何在 MariaDB 10 中启用大索引?

MySQL 在索引中使用自定义字符串比较运算符

MariaDB · 性能优化 · Extended Keys

Mariadb 配置文件参数浅谈

详解MariaDB数据库的索引