mysql重新分区表大得多

时间:2015-05-25 15:27:29

标签: mysql database partitioning

我在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) */

1 个答案:

答案 0 :(得分:0)

  

添加新字段本质上需要重建,所以为什么不

我预测你会后悔的。

  

现有表约为430 GB。

根据.ibd的大小?还是SHOW TABLE STATUS?或转储大小,这将是假的(见下文)。

  

它远远超过文本转储文件

TABLE STATUS中的长度包括几种开销(BTree,可用空间,额外扩展等)以及索引(不在转储文件中)。

另外,考虑一个包含1234的BIGINT .ibd将是8个字节加上一些开销;转储将有5个(' 1234',加上逗号)。这导致了我的下一个观点......

真的超过4个亿商家吗? merchant_idBIGINT(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_idaction_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%)的"完整"尺寸。因此,我并不期待'这是解决方案/答案。