我在mysql 5.6.10实例上有一个非常大的表(大约4.8亿行)。 存储引擎是InnoDB。 (表和数据库默认值)。 该表通过merchantId(bigint:一种客户端标识符)的散列进行分区,该查询在与单个商家相关的查询时提供帮助。由于查询跨越多个商家时性能显着下降,我决定在ACTION_DATE(活动发生的DATE)按范围对表进行重新分区。认为我很聪明,我决定添加一些(5)新字段供将来使用(unused_varchar1 varchar(200)等),因为表格太大,添加新字段本质上需要重建,所以为什么不呢? ...
我将新表结构创建为_new,使用mysql dump将现有文件转储到辅助服务器。然后我使用awk脚本来熟化名称和一些其他细节以适应新表(将tableName更改为tableName_new),并开始加载。
现有表约为430 GB。文本文件类似地约为403 GB。因此我感到惊讶的是,新桌子最终占用了大约840 GB! (基于.ibd文件的linux fize大小)
所以,我有2个基本问题,这些问题实际上就是为什么以及现在......
我认为新表更大,因为转储文件是前一个分区(merchantId)的顺序,而负载插入到新分区(活动日期)中,创建了一个半随机的插入顺序。随机性导致mysql在页面中留下足够的空间(大约50%)以供将来插入。 (我对这里的术语有点模糊,在我的职业生涯中花了更多的时间在Sql Server DB而不是MySql Dbs ......)我无法在mysql中找到任何内部统计信息,每页空间无空间。 INFORMATION_SCHEMA.TABLES DATA_FREE统计数据令人难以置信68MB。
如果有帮助,这些是来自I_S.TABLES的相关统计数据:
TABLE_TYPE:BASE TABLE
发动机:InnoDB
版本:10
ROW_FORMAT:紧凑型
TABLE_ROWS:488,094,271
AVG_ROW_LENGTH:1,564
DATA_LENGTH:763,509,358,592(711 GB)
INDEX_LENGTH:100,065,574,912(93.19 GB)
DATA_FREE:68,157,440(0.06 GB)
我意识到这不会增加到840 GB,但正如我所说,这是.ibd文件的大小,似乎与I_S.TABLES统计数据略有不同。无论哪种方式,它都远远超过文本转储文件。 我离题了......
我的问题是我的理论是否重新解释是否解释了大致翻了一倍的规模。还是有另一种解释?我认为额外的列(2 Bigint,2 Varchar(200),1 Date)不是罪魁祸首,因为它们都是空的。我的餐巾纸计算是附加列将添加< 9 GB。同样,UID上的一个附加索引应该是一个相对较小的附加值。
后续问题是如果我想尝试压缩表格,我现在可以做什么。 (服务器现在只有大约385 GB免费...) 如果我重复这个过程,转储到文件,重新加载,这次是按照当前的分区顺序,我最终会得到一个更像原始表大小的表~430 GB?
以下是DDL的相关部分。
旧表:
CREATE TABLE table_name (
`AUTO_SEQ` bigint(20) NOT NULL,
`MERCHANT_ID` bigint(20) NOT NULL,
`AFFILIATE_ID` bigint(20) DEFAULT NULL,
`PROGRAM_ID` bigint(20) NOT NULL,
`ACTION_DATE` date DEFAULT NULL,
`UID` varchar(128) DEFAULT NULL,
... additional columns ...
PRIMARY KEY (`AUTO_SEQ`,`MERCHANT_ID`,`PROGRAM_ID`),
KEY `oc_rpt_mpad_idx` (`MERCHANT_ID`,`PROGRAM_ID`,`ACTION_DATE`,`AFFILIATE_ID`),
KEY `oc_rpt_mapd` (`MERCHANT_ID`,`ACTION_DATE`),
KEY `oc_rpt_apda_idx` (`AFFILIATE_ID`,`PROGRAM_ID`,`ACTION_DATE`,`MERCHANT_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50100 PARTITION BY HASH (merchant_id)
PARTITIONS 16 */
新表:
CREATE TABLE `tableName_new` (
`AUTO_SEQ` bigint(20) NOT NULL,
`MERCHANT_ID` bigint(20) NOT NULL,
`AFFILIATE_ID` bigint(20) DEFAULT NULL,
`PROGRAM_ID` bigint(20) NOT NULL,
`ACTION_DATE` date NOT NULL DEFAULT '0000-00-00',
`UID` varchar(128) DEFAULT NULL,
... additional columns...
# NEW COLUMNS (ALL NULL)
`UNUSED_BIGINT1` bigint(20) DEFAULT NULL,
`UNUSED_BIGINT2` bigint(20) DEFAULT NULL,
`UNUSED_VARCHAR1` varchar(200) DEFAULT NULL,
`UNUSED_VARCHAR2` varchar(200) DEFAULT NULL,
`UNUSED_DATE1` date DEFAULT NULL,
PRIMARY KEY (`AUTO_SEQ`,`ACTION_DATE`),
KEY `oc_rpt_mpad_idx` (`MERCHANT_ID`,`PROGRAM_ID`,`ACTION_DATE`,`AFFILIATE_ID`),
KEY `oc_rpt_mapd` (`ACTION_DATE`),
KEY `oc_rpt_apda_idx` (`AFFILIATE_ID`,`PROGRAM_ID`,`ACTION_DATE`,`MERCHANT_ID`),
KEY `oc_uid` (`UID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
/*!50500 PARTITION BY RANGE COLUMNS(ACTION_DATE)
(PARTITION p01 VALUES LESS THAN ('2012-01-01') ENGINE = InnoDB,
PARTITION p02 VALUES LESS THAN ('2012-04-01') ENGINE = InnoDB,
PARTITION p03 VALUES LESS THAN ('2012-07-01') ENGINE = InnoDB,
PARTITION p04 VALUES LESS THAN ('2012-10-01') ENGINE = InnoDB,
PARTITION p05 VALUES LESS THAN ('2013-01-01') ENGINE = InnoDB,
PARTITION p06 VALUES LESS THAN ('2013-04-01') ENGINE = InnoDB,
PARTITION p07 VALUES LESS THAN ('2013-07-01') ENGINE = InnoDB,
PARTITION p08 VALUES LESS THAN ('2013-10-01') ENGINE = InnoDB,
PARTITION p09 VALUES LESS THAN ('2014-01-01') ENGINE = InnoDB,
PARTITION p10 VALUES LESS THAN ('2014-04-01') ENGINE = InnoDB,
PARTITION p11 VALUES LESS THAN ('2014-07-01') ENGINE = InnoDB,
PARTITION p12 VALUES LESS THAN ('2014-10-01') ENGINE = InnoDB,
PARTITION p13 VALUES LESS THAN ('2015-01-01') ENGINE = InnoDB,
PARTITION p14 VALUES LESS THAN ('2015-04-01') ENGINE = InnoDB,
PARTITION p15 VALUES LESS THAN ('2015-07-01') ENGINE = InnoDB,
PARTITION p16 VALUES LESS THAN ('2015-10-01') ENGINE = InnoDB,
PARTITION p17 VALUES LESS THAN ('2016-01-01') ENGINE = InnoDB,
PARTITION p18 VALUES LESS THAN ('2016-04-01') ENGINE = InnoDB,
PARTITION p19 VALUES LESS THAN ('2016-07-01') ENGINE = InnoDB,
PARTITION p20 VALUES LESS THAN ('2016-10-01') ENGINE = InnoDB,
PARTITION p21 VALUES LESS THAN ('2017-01-01') ENGINE = InnoDB,
PARTITION p22 VALUES LESS THAN ('2017-04-01') ENGINE = InnoDB,
PARTITION p23 VALUES LESS THAN ('2017-07-01') ENGINE = InnoDB,
PARTITION p24 VALUES LESS THAN ('2017-10-01') ENGINE = InnoDB,
PARTITION p25 VALUES LESS THAN ('2018-01-01') ENGINE = InnoDB,
PARTITION p26 VALUES LESS THAN ('2018-04-01') ENGINE = InnoDB,
PARTITION p27 VALUES LESS THAN ('2018-07-01') ENGINE = InnoDB,
PARTITION p28 VALUES LESS THAN ('2018-10-01') ENGINE = InnoDB,
PARTITION p29 VALUES LESS THAN ('2019-01-01') ENGINE = InnoDB,
PARTITION p30 VALUES LESS THAN (MAXVALUE) ENGINE = InnoDB) */
答案 0 :(得分:0)
添加新字段本质上需要重建,所以为什么不
我预测你会后悔的。
现有表约为430 GB。
根据.ibd的大小?还是SHOW TABLE STATUS
?或转储大小,这将是假的(见下文)。
它远远超过文本转储文件
TABLE STATUS
中的长度包括几种开销(BTree,可用空间,额外扩展等)以及索引(不在转储文件中)。
另外,考虑一个包含1234的BIGINT
.ibd将是8个字节加上一些开销;转储将有5个(' 1234',加上逗号)。这导致了我的下一个观点......
真的超过4个亿商家吗? merchant_id
是BIGINT
(8个字节); INT UNSIGNED
只有4个字节,允许0..4亿。
uid
中的内容是什么?如果它是某种UUID,它似乎非常长。
你碰巧有来自I_S.TABLES"的"统计数据从旧桌子?
到目前为止,我还没有解决#34;重新解释是否解释了大致翻了一倍的尺寸"。
额外列(2 Bigint,2 Varchar(200),1 Date)
那个约每行29个字节(15GB的Data_length),可能更少,因为它们是NULL
。
您好像使用默认ROW_FORMAT
。我怀疑这在转换中并没有改变。
使用"分区键&#34> 启动索引通常是不明智的。 (merchant_id
或action_date
)。这是因为你已经"修剪"在那把钥匙上;你最好用其他东西开始索引。 (警告:有例外。)
检查"其他列"的CHARACTER SET
和数据类型。如果发生了变化,那可能会很重要。
唉,在我们弄清楚它为什么会成长之前,我无法回答这个问题。我最终会得到一张更像原始桌子大小的桌子~430 GB?
我对随机插入与分区(ACTION_DATE)是否会导致浪费的空间/半空页更感兴趣。
我建议您尝试以下实验。 不使用优化分区;见http://bugs.mysql.com/bug.php?id=42822。而是执行此操作来对一个分区(例如p02)进行碎片整理:
ALTER TABLE table_name REBUILD PARTITION p02;
您可以在SELECT
之前和之后执行此操作,以查看PARTITIONs
的更改:
SELECT *
FROM information_schema.PARTITIONS
WHERE TABLE_SCHEMA = 'dbname' -- change as needed
AND TABLE_NAME = 'table_name' -- change as needed
ORDER BY PARTITION_ORDINAL_POSITION,
SUBPARTITION_ORDINAL_POSITION;
它是一个通用查询,用于获取一个表的分区的表状态信息。
如果REBUILD
将分区削减了大约50%,那么我们就有了答案。
通常情况下,随机插入BTree应该会给你带来大约69%(不是50%)的"完整"尺寸。因此,我并不期待'这是解决方案/答案。