为什么MySQL的information_schema.tables字段index_length显示为0?

 

为什么MySQL的information_schema.tables字段index_length显示为0?

 

测试版本为:MySQL社区版 8.0.36

 

分情况1:
innodb引擎的表是索引组织表,按照主键进行顺序存放。
则表是索引,索引是表,index_length不会有值。

如下,创建一个表,并追加主键,查看索引情况。

(root@localhost 11:02:20) [zkm](7994090)> create table test as select * from information_schema.tables;
Query OK, 691 rows affected (3.19 sec)
Records: 691  Duplicates: 0  Warnings: 0

(root@localhost 11:05:35) [zkm](7994090)> alter table test add primary key (table_schema,table_name);
Query OK, 0 rows affected (0.51 sec)
Records: 0  Duplicates: 0  Warnings: 0

(root@localhost 11:07:34) [zkm](7994090)> show indexes from test;
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| test  |          0 | PRIMARY  |            1 | TABLE_SCHEMA | A         |           9 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| test  |          0 | PRIMARY  |            2 | TABLE_NAME   | A         |         691 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
+-------+------------+----------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
2 rows in set (0.02 sec)

(root@localhost 11:08:04) [zkm](7994090)> SELECT CONCAT(table_schema, '.', table_name) 'owner.table_name',
    ->        table_rows AS 'Number of Rows',
    ->        format_bytes(data_length) AS 'dataSize',
    ->        format_bytes(index_length) AS 'indexSize',
    ->        format_bytes(data_length + index_length) AS 'totalSize'
    ->   FROM information_schema.tables
    ->  WHERE table_schema = DATABASE()
    ->    AND table_name = 'test';
+------------------+----------------+------------+------------+------------+
| owner.table_name | Number of Rows | dataSize   | indexSize  | totalSize  |
+------------------+----------------+------------+------------+------------+
| zkm.test         |            691 | 128.00 KiB |    0 bytes | 128.00 KiB |
+------------------+----------------+------------+------------+------------+
1 row in set (0.00 sec)

 

 

情况2:防。

如果此时在情况1的基础上,创建一个其他字段的索引,可以看到,index_length依然值为0。

(root@localhost 16:04:29) [zkm](8036866)> create index idx_t_checksum on test(checksum);
Query OK, 0 rows affected (0.39 sec)
Records: 0  Duplicates: 0  Warnings: 0

(root@localhost 16:07:36) [zkm](8036866)> show indexes from test;
+-------+------------+----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| Table | Non_unique | Key_name       | Seq_in_index | Column_name  | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
+-------+------------+----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
| test  |          0 | PRIMARY        |            1 | TABLE_SCHEMA | A         |          10 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| test  |          0 | PRIMARY        |            2 | TABLE_NAME   | A         |         692 |     NULL |   NULL |      | BTREE      |         |               | YES     | NULL       |
| test  |          1 | idx_t_checksum |            1 | CHECKSUM     | A         |           1 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL       |
+-------+------------+----------------+--------------+--------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
3 rows in set (0.05 sec)

(root@localhost 16:07:39) [zkm](8036866)> SELECT CONCAT(table_schema, '.', table_name) 'owner.table_name',
    ->        table_rows AS 'Number of Rows',
    ->        format_bytes(data_length) AS 'dataSize',
    ->        format_bytes(index_length) AS 'indexSize',
    ->        format_bytes(data_length + index_length) AS 'totalSize'
    ->   FROM information_schema.tables
    ->  WHERE table_schema = DATABASE()
    ->    AND table_name = 'test';
+------------------+----------------+------------+------------+------------+
| owner.table_name | Number of Rows | dataSize   | indexSize  | totalSize  |
+------------------+----------------+------------+------------+------------+
| zkm.test         |            692 | 128.00 KiB |    0 bytes | 128.00 KiB |
+------------------+----------------+------------+------------+------------+
1 row in set (0.05 sec)

https://www.cnblogs.com/PiscesCanon/p/18232608

 

首先从百度一通,没解决问题。

然后从官网看看information_schema.tables字段index_length的解释:

对于InnoDBINDEX_LENGTH 是分配给non-clustered indexes的近似空间量(以字节为单位)。具体来说,它是non-clustered indexes大小(以页为单位)的总和乘以 InnoDB页大小。

还是没解决问题,但实际上对于information_schema.tables官网开头的介绍可以看出端倪,再回去仔细瞧瞧。

查询tables显示的是目标表的统计信息的缓存值,而这个缓存值不一定准确可能是过期的,这就是原因了。

information_schema_stats_expiry定义了缓存表统计信息多久过期,以秒为单位(默认是1天)。

对参数的说明可以直到,默认情况下,当查询这些列时,MySQL 会从 mysql.index_stats 和 mysql.table_stats 字典表中检索这些列的缓存值,这比直接从存储引擎检索统计信息更有效。如果缓存统计信息不可用或已过期,MySQL 会从存储引擎中检索最新统计信息并将其缓存在 mysql.index_stats 和 mysql.table_stats 字典表中。后续查询将检索缓存统计信息,直到缓存统计信息过期。服务器重新启动或第一次打开 mysql.index_stats 和 mysql.table_stats 表不会自动更新缓存统计信息。

要立即更新某张表在tables的缓存值,可以使用analyze table语句,而如果要绕过缓存值直接查村存储引擎兼做最新统计数据,则设置information_schema_stats_expiry=0。

更多细节看官网的说明吧。

按照官方的说法,设置information_schema_stats_expiry=0或者analyze table,都能查到值了。

不过还是遇到问题了。。。

当设置information_schema_stats_expiry=0,对于index_length的值还是0(直接给我整懵了),不过像update_time这个倒是准确了,额外提出来做个对比。如下:

(analyze table test,则没问题会更新index_langth的值,这里略了)

(root@localhost 09:49:30) [zkm](8157259)> insert into test (table_schema,table_name,create_time,TABLE_COLLATION) values ('g','g',now(),'g');
Query OK, 1 row affected (0.00 sec)

(root@localhost 09:49:31) [zkm](8157259)> commit;
Query OK, 0 rows affected (0.00 sec)

(root@localhost 09:49:38) [zkm](8157259)> SELECT INDEX_LENGTH,UPDATE_TIME from information_schema.tables WHERE TABLE_NAME = 'test';
+--------------+---------------------+
| INDEX_LENGTH | UPDATE_TIME         |
+--------------+---------------------+
|            0 | 2024-06-05 09:04:22 |
+--------------+---------------------+
1 row in set (0.01 sec)

(root@localhost 09:49:48) [zkm](8157259)> set session information_schema_stats_expiry = 0;
Query OK, 0 rows affected (0.00 sec)

(root@localhost 09:49:56) [zkm](8157259)> SELECT INDEX_LENGTH,UPDATE_TIME from information_schema.tables WHERE TABLE_NAME = 'test';
+--------------+---------------------+
| INDEX_LENGTH | UPDATE_TIME         |
+--------------+---------------------+
|            0 | 2024-06-05 09:49:31 |
+--------------+---------------------+
1 row in set (0.00 sec)

--重新设置回去,update_time时间变回去了,说明mysql.table_stats的值并没有被更新
(root@localhost 09:49:57) [zkm](8157259)> set session information_schema_stats_expiry = 86400;
Query OK, 0 rows affected (0.00 sec)

(root@localhost 09:50:05) [zkm](8157259)> SELECT INDEX_LENGTH,UPDATE_TIME from information_schema.tables WHERE TABLE_NAME = 'test';
+--------------+---------------------+
| INDEX_LENGTH | UPDATE_TIME         |
+--------------+---------------------+
|            0 | 2024-06-05 09:04:22 |
+--------------+---------------------+
1 row in set (0.01 sec)

 

 

 

在mos文档“The Data and Index Length in the TABLES Information Schema Table Are Not Updating (文档 ID 2567751.1)”也提到了跟information_schema_stats_expiry有关,,,,,,

he information in information_schema.TABLES for InnoDB tables is only updated when the index statistics for the table are updated. In MySQL 8.0, there is additionally the information_schema_stats_expiry option that defines how long time the Information Schema will cache data on its own.

This means that the DATA_LENGTH and INDEX_LENGTH columns will only be updated when there either has been enough changes to the table to trigger an automatic update, or an explicit update has been triggered through ANALYZE TABLE or similar. 

InnoDB 表的 information_schema.TABLES 中的信息仅在表的索引统计信息更新时才会更新。在 MySQL 8.0 中,还有 information_schema_stats_expiry 选项,该选项定义信息模式将自行缓存数据的时间。

这意味着,只有当表发生足够多的更改以触发自动更新,或者通过 ANALYZE TABLE 或类似操作触发显式更新时,DATA_LENGTH 和 INDEX_LENGTH 列才会更新。

而在mos文档“When Does InnoDB Update the Index Statistics? (文档 ID 1463718.1)”详细提到了innodb的索引统计信息的更新启动条件,可以看看潇湘隐者的:MySQL InnoDB什么时候更新索引的统计信息?,知识来源也是这个mos文档,但是中文更易读。

也就是说索引统计信息更新的情况除了“analyze table”和“optimize table(本质是触发analyze table)”外,对于持久统计信息的表,那就是当 1/10 (10%) 行发生更改时,索引统计信息将更新,限制为每次更新之间必须至少经过 10 秒。

这篇mos文档倒是没提到information_schema_stats_expiry,乱的雅痞。

另外在“Why Is the Database Size Returned by "du -ch" Different From the One From information_schema.TABLES? (文档 ID 2422575.1)”则提到:

    • Bug 19811005 - ALTER TABLE ADD INDEX DOES NOT UPDATE INDEX_LENGTH IN I_S TABLES
      Fixed in 5.6.44, 5.7.26, 8.0.16 and later.
    • Bug 22778072 - INDEX_LENGTH IS 0 (SHOW TABLE STATUS) AFTER CREATE INDEX

不过Bug链接已经无法访问,可能没有权限或者没这个Bug了,不确定。

所以也只能通过“analyze table"方式了。

 

目前觉得information_schema_stats_expiry=0不生效是Bug。

 

posted @ 2024-06-05 11:08  PiscesCanon  阅读(15)  评论(0编辑  收藏  举报