INFORMATION_SCHEMA.INNODB_BUFFER_PAGE 和 INFORMATION_SCHEMA.TABLES 中的不同数据大小

Different data sizes in INFORMATION_SCHEMA.INNODB_BUFFER_PAGE and INFORMATION_SCHEMA.TABLES

我试图了解 table 是否正在加载到 InnoDB 缓冲区。为此,我正在查询 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE table。 据我所见,table 已满载。但是,加载到缓冲区的数据量 (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 已完全加载到内存中,并且 TABLE_ROWS 是近似值或不是最新的。 但为什么 INFORMATION_SCHEMA.INNODB_BUFFER_PAGE 中的 DATA_SIZE 如此不同(3.5 GB 与 10.5 GB)? 我错过了什么? TABLES 中的数据大小是否完全不正确?

数据库在 Amazon RDS (Aurora MySQL 5.7) 上 运行 如果重要的话。

谢谢。

P.S。 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
;

The Information Schema INNODB_BUFFER_PAGE table contains information about pages in the buffer pool.

记下最后 4 个字。

这表明来自 INNODB_BUFFER_PAGE 的 SUM 可能小于您从 INFORMATION_SCHEMA.TABLES 获得的 SUM。

(我不知道所有细节,但这里有一些笼统的说法。)

buffer_pool可能包含:

  • table.
  • 的部分或全部叶节点
  • table.
  • 的部分或全部 non-leaf 个节点
  • 同上叶和 non-leaf 每个 non-PRIMARY 索引的节点 table。
  • 同上 tables。
  • TEXT 和 BLOB( 大型 VARCHAR)可以存储 off-record。这大大增加了磁盘占用space。但我认为你的情况不会发生这种情况。 然而,见下文
  • buffer_pool 的 25%(可调)保留用于“更改缓冲区”;这是一种用于更改二级索引的写入缓存。
  • 其他内容
  • 百分之几的 buffer_pool 被保留或因其他原因丢失。

块被踢出 buffer_pool [大致] least-recently-used 顺序。

我不知道,但我预计如果 table 的大小是 buffer_pool 的一半,则可能无法在 buffer_pool 中保留 table =91=].

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

  • Pre-allocated块(可能反映在Data_free)
  • 未填充的块(可能没有数据或索引块是 100% 满的)
  • 事务导致行的额外副本——这些副本来来去去,要么在 undo/redo space 中,要么在数据块中。
  • 区块分裂
  • 等等
  • 根据经验,数据 (Data_length) 占用的磁盘 space 是预测大小的 2-3 倍。 (“预测”= 将各个数据大小相加,例如每个 INT 4 个字节。)

天马行空

什么是ROW_FORMAT

你的3.5GB计算可能是on-recordspace,全部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()。)

抱歉耽搁了这么久。我终于设法确认确实对相关 table 执行了数据清理。大约 60% 或记录被删除。 这应该可以解释 sizen_leaf_pages 值之间的区别 mysql.innodb_index_stats table。不确定这是否是正常行为。

所以来回答我的问题。为了估计 table 将占用 InnoDB 池多少,我可能应该查看 mysql.innodb_index_stats.size 而不是 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 帮助我完成这个