MySQL 5.7.25 [innoDB],索引基数突然reset/change变回1

MySQL 5.7.25 [innoDB], index cardinality suddenly reset/change back to 1

我正在使用 MySql v 5.7.25 和 innoDB。

我有一个 table inventories 有 100 个 mio 数据

这是 SHOW CREATE TABLE inventories;

时的样子

注意:删除了不相关的字段。

CREATE TABLE `inventories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) DEFAULT NULL,
  `quantity` decimal(50,6) DEFAULT NULL,
  `line_number` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_inventories_on_line_number` (`line_number`),
  KEY `idx_pr_ln` (`product_id`,`line_number`),
) ENGINE=InnoDB AUTO_INCREMENT=39905 DEFAULT CHARSET=latin1

问题是索引 idx_pr_ln 上的索引基数突然在字段 line_number 上重置为 1。

mysql > show index from inventories;

| Table       | Non_unique | Key_name                              | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
|-------------|------------|---------------------------------------|--------------|-------------|-----------|-------------|----------|--------|------|------------|---------|---------------|
| inventories |          1 | index_inventories_on_line_number      |            1 | line_number | A         |     3025563 |          |        | YES  | BTREE      |         |               |
| inventories |          1 | idx_pr_ln                             |            1 | product_id  | A         |     4337902 |          |        | YES  | BTREE      |         |               |
| inventories |          1 | idx_pr_ln                             |            2 | line_number | A         |           1 |          |        | YES  | BTREE      |         |               |

每个使用索引 idx_pr_lnSELECT 查询现在都进行 FULL table 扫描,因为 line_number.

上的基数 1

在我执行 ANALYZE TABLE inventories 后,它会回到 'correct' 值大约 100 mio。 但是过几天又出现了(又重新设置为1)

我的问题是

为什么这个基数突然 reset/change 到 1?

你们有没有遇到过这种情况?

这是 MySql 上的错误吗?

如有任何解释/建议,我们将不胜感激。 谢谢。

注意:table 或索引没有变化, 没有改变 table,没有添加/删除索引。 只有可能改变的数据 (CUD)

编辑: select 语句是这样的

SELECT product_id, line_number FROM inventories WHERE product_id = 123 AND line_number < 321

SHOW INDEXES 中的 1 完全正常。它们表示当前值。

"index_inventories_on_line_number" 有一列,序号 1 of 1
"idx_pr_ln" 有 2 列,编号为 1 和 2。

第二个索引的两列的"cardinality"是第一列的“4337902”,也就是说有"a lot of different values"。对于其中的每一个,第二列都是唯一的或接近唯一的(“1”)。

MySQL/InnoDB 绝不会操纵除 PRIMARY KEY 列以外的任何其他列的值,即 id。它会递增,除了 TRUNCATE TABLE 会重置它。

every SELECT query that use index idx_pr_ln now do FULL table scan because of cardinality 1 on line_number.

视情况而定。

对于 SELECT ... WHERE line_number = 432,是的,它将进行完整的 table 扫描。这是因为line_number在复合索引中不是第一个

对于SELECT ... WHERE product_id = 987,它将使用索引。

如需进一步讨论,请提供示例 SELECTs

cardinality on index idx_pr_ln suddenly reset to 1 on field line_number

InnoDB 将进行一些随机探测以确定列的统计信息(基数)。如果碰巧看到 line_number 的唯一值,则基数设置为 1。如果没有,则使用某个更大的值。

统计信息用于决定优化查询。让我们看看一个 运行 比您认为应该慢的查询。 是我们应该关注的地方。

我发现了问题所在,导致索引基数为 1。

如@Rick James 所述

InnoDB will make a few random probes to determine statistics (cardinality) of columns. If it happens to see unique values for line_number, then the cardinality is set to 1. If not, some larger value is used.

在阅读了这篇 mysql 文档之后 https://dev.mysql.com/doc/refman/5.7/en/innodb-persistent-stats.html

这是由于 mysql 的统计更新造成的,当时有 10% 的数据(在本例中大约为 10 mio)发生了变化。

STATS_AUTO_RECALC specifies whether to automatically recalculate persistent statistics. The value DEFAULT causes the persistent statistics setting for the table to be determined by the innodb_stats_auto_recalc setting. A value of 1 causes statistics to be recalculated when 10% of table data has changed. A value 0 prevents automatic recalculation for the table. When using a value of 0, use ANALYZE TABLE to recalculate statistics after making substantial changes to the table.

STATS_SAMPLE_PAGES specifies the number of index pages to sample when cardinality and other statistics are calculated for an indexed column, by an ANALYZE TABLE operation

使用默认设置 STATS_AUTO_RECALC=1 和 STATS_SAMPLE_PAGES=20,mysql 有可能扫描包含不同值的示例页面(20 上只有 1 个不同值页样本)。

我更新后 STATS_SAMPLE_PAGES=30,它不太可能有基数 1。