0

我试图了解表是否正在加载到 InnoDB 缓冲区。为此,我正在查询 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE 表。据我所见,桌子已满载。但是,加载到缓冲区的数据量 (MB) 与 INFORMATION_SCHEMA.TABLES 中显示的数字有很大不同。

例如:

SELECT TABLE_NAME, TABLE_ROWS
    , CAST(DATA_LENGTH/POWER(1024,2) AS DECIMAL(5, 0)) AS DATA_LENGTH_MB
    , CAST(DATA_FREE/POWER(1024,2) AS DECIMAL(5, 0)) AS DATA_FREE_MB
FROM INFORMATION_SCHEMA.TABLES 
    FROM INFORMATION_SCHEMA.TABLES 
    WHERE TABLE_SCHEMA = '<db_name>' 
        AND TABLE_NAME = '<table_name>';


| TABLE_NAME   | TABLE_ROWS | DATA_LENGTH_MB | DATA_FREE_MB |
|-----------------------------------------------------------|
| <table_name> | 39735968   | 10516          | 548          |

因此,根据 INFORMATION_SCHEMA.TABLES,大约有 3970 万条记录和 10.5 GB 的数据页

但是,当我运行这个时:

SELECT p.TABLE_NAME, p.INDEX_NAME
    , ROUND(SUM(DATA_SIZE)/POWER(1024,2)) AS DATA_SIZE_MB
    , SUM(NUMBER_RECORDS) AS NUMBER_RECORDS
FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE AS p 
WHERE p.TABLE_NAME LIKE '`<db_name>`.`<table_name>`' AND p.INDEX_NAME = 'PRIMARY' 
    AND p.PAGE_TYPE = 'INDEX' AND p.PAGE_STATE = 'FILE_PAGE'
ORDER BY p.TABLE_NAME, p.INDEX_NAME

我越来越:

| TABLE_NAME             | INDEX_NAME | DATA_SIZE_MB | NUMBER_RECORDS |
-----------------------------------------------------------------------
| <db_name>.<table_name> | PRIMARY    | 3505         | 45224835       |

最后,

SELECT COUNT(1) FROM <db_name>.<table_name>;
44947428

NUMBER_RECORDS 略大于 INFORMATION_SCHEMA.TABLES 中的 TABLE_ROWS。所以我假设该表已完全加载到内存中,并且 TABLE_ROWS 是近似的或不是最新的。但是为什么 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE 中的 DATA_SIZE 有很大不同(3.5 GB 与 10.5 GB)?我错过了什么?TABLES 中的数据大小是否完全不正确?

如果这很重要,数据库正在 Amazon RDS (Aurora MySQL 5.7) 上运行。

谢谢。

PS CREATE TABLE 语句(列名被混淆了,抱歉:)

CREATE TABLE `table_name` (
    `recid` BINARY(32) NOT NULL,
    `col1` INT(11) UNSIGNED NOT NULL,
    `col2` TINYINT(1) UNSIGNED NOT NULL,
    `col3` VARCHAR(250) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
    `col4` TINYINT(1) UNSIGNED NOT NULL,
    `col5` VARCHAR(250) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
    `col6` TINYINT(1) UNSIGNED NOT NULL,
    `col7` TINYINT(1) UNSIGNED NOT NULL,
    `col8` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
    `col9` TINYINT(1) UNSIGNED NOT NULL,
    `col10` TINYINT(1) UNSIGNED NOT NULL,
    `col11` VARCHAR(100) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
    `col12` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0',
    `col13` TINYINT(1) UNSIGNED NOT NULL DEFAULT '1',
    `col14` INT(11) UNSIGNED NULL DEFAULT NULL,
    `col15` BINARY(32) NULL DEFAULT NULL,
    `col16` CHAR(2) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
    `col17` TINYINT(1) NULL DEFAULT NULL,
    `col18` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
    `col19` TINYINT(1) NULL DEFAULT NULL,
    `col20` TINYINT(1) NULL DEFAULT NULL,
    PRIMARY KEY (`recid`) USING BTREE,
    UNIQUE INDEX `col3` (`col3`) USING BTREE,
    INDEX `col5` (`col5`) USING BTREE,
    INDEX `col8` (`col8`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
4

2 回答 2

0

Information Schema INNODB_BUFFER_PAGE 表包含有关缓冲池中页面的信息。

注意最后 4 个单词。

这表明 SUM fromINNODB_BUFFER_PAGE可能小于您从中获得的INFORMATION_SCHEMA.TABLES.

(我不知道所有细节,但这里有一些一般性陈述。)

buffer_pool 可能包含:

  • 表的部分或全部叶节点。
  • 表的部分或全部非叶节点。
  • 对于表的每个非 PRIMARY 索引,叶节点和非叶节点也是如此。
  • 更多表同上。
  • TEXT 和 BLOB(以及大的 VARCHAR)可以不记录存储。这大大增加了占用的磁盘空间。但我认为这不会发生在你的情况下。 但是,请参见下文
  • 25%(可调)的 buffer_pool 保留给“更改缓冲区”;这是一种用于更改二级索引的写缓存。
  • 其他的东西
  • 百分之几的 buffer_pool 被保留或因其他原因丢失。

块以 [大致] 最近最少使用的顺序被踢出 buffer_pool。

我不知道,但我希望如果该表是 buffer_pool 大小的一半,则可能无法在 buffer_pool 中保留一个表。

另一件需要注意的事情......每个表的 Data_free 指标只是表中许多“开销”类别之一。

  • 预分配的块(可能反映在 Data_free 中)
  • 未填充的块(可能没有数据或索引块 100% 已满)
  • 事务会导致额外的行副本——这些行来来去去,要么在撤消/重做空间中,要么在数据块中。
  • 块拆分
  • 等等
  • 经验法则是数据占用的磁盘空间(Data_length)是预测大小的 2x-3x。(“预测”= 将各个数据大小相加,例如每个 4 个字节INT。)

狂野的想法

是什么ROW_FORMAT

您的 3.5GB 计算可能是 on-record 空间,所有这些VARCHARs都存储在 off_record 中。数学几乎可以解决。

让我们一起追寻2个思路

SELECT count(*),
       AVG(LENGTH(col3)) AS avg3,
       AVG(LENGTH(col5)) AS avg5,
       ...   -- the rest of the VARCHARs
    FROM table_name;

(我特别想要LENGTH(),不是CHAR_LENGTH()。)

于 2020-09-26T00:00:02.743 回答
0

很抱歉耽搁了很长时间。我终于设法确认实际上对相关表执行了数据清理。大约 60% 或记录被删除。这应该可以解释mysql.innodb_index_stats表中sizen_leaf_pages值之间的差异。不确定这是否是正常行为。

所以回答我的问题。为了估计 InnoDB 池中需要多少表,我可能应该研究mysql.innodb_index_stats大小而不是INFORMATION_SCHEMA.TABLE

SELECT TABLE_NAME, ROUND((stat_value*@@innodb_page_size)/POWER(1024,2)) AS DATA_SIZE_MB 
FROM mysql.innodb_index_stats 
WHERE database_name = 'db_name' AND index_name = 'PRIMARY' AND table_name = 'table_name' 
    AND stat_name = 'n_leaf_pages';

感谢@Rick James 帮我解决了这个问题

于 2020-11-23T15:52:55.633 回答