为什么索引比MySQL表中的数据占用更多的空间?

时间:2017-12-15 09:45:40

标签: mysql database database-design

我在Google Cloud SQL上托管了超过10亿行的MySQL表。

>> SHOW CREATE TABLE depth

CREATE TABLE `depth` (
  `date` date DEFAULT NULL,
  `receive_time` datetime(3) DEFAULT NULL,
  `instrument_token` bigint(20) unsigned DEFAULT NULL,
  `level` tinyint(3) unsigned DEFAULT NULL,
  `bid_count` smallint(5) unsigned DEFAULT NULL,
  `bid_size` bigint(20) unsigned DEFAULT NULL,
  `bid_price` float DEFAULT NULL,
  `ask_price` float DEFAULT NULL,
  `ask_size` bigint(20) unsigned DEFAULT NULL,
  `ask_count` smallint(5) unsigned DEFAULT NULL,
   KEY `date_time_sym` (`date`,`receive_time`,`instrument_token`),
   KEY `date_sym_time` (`date`,`instrument_token`,`receive_time`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8

要获取数据和索引大小,我运行查询

SHOW TABLE STATUS from mktdata where Name = "depth";

这里我得到一个包含一行的表输出,其值为几个重要字段:

Name: depth
Engine:InnoDB
Version:10
Row_format:Dynamic
Rows: 1,72,08,21,447 
Avg_row_length: 78
Index_length: 1,83,90,03,07,456
Data_length:  1,35,24,53,32,480 

问题:为什么Index_length大于Data_length?你可以在上面看到我的索引,为什么他们需要这么多空间来存储?我不太了解如何创建和存储索引,所以请从基础知识中解释。

2 个答案:

答案 0 :(得分:2)

这可能发生。

你有一个重度索引的表。这可能有用也可能没用。

以下是一些常见错误:

  • “我正在为所有列编制索引” - 通常没用。

  • “我为每个使用的列编制索引” - 但未能理解“复合”索引的重要性:INDEX(last,first)与INDEX(last),INDEX(first)

  • INDEX(a),INDEX(a,b) - 没有意识到第一个是多余的。

  • PRIMARY KEY(id),INDEX(id) - 没有意识到PRIMARY KEY是一个INDEX(和UNIQUE)。

显示创建表 并描述主要的SELECTS。然后我们可以讨论哪些INDEX是最优的,哪些可以删除。

进一步说明:INDEX包含表中每行的行,并包含索引列以及指向DATA中行的指针。如果您有多个索引,它们都包含在INDEX_LENGTH中(InnoDB的PRIMARY KEY除外)。

或者,它可能是您的最佳索引集,索引大小比数据大。

参考链接: https://forums.mysql.com/read.php?10,390235,390352

答案 1 :(得分:0)

拥有Index_length> Data_length很少见,但不是“坏”或“错误”。

你没有明确的PRIMARY KEY,所以它是一个隐藏的6字节字段,有点像auto_increment。

每个辅助密钥都包含PK的副本。

Avg_row_length: 78 - 这是根据Date_length / Rows计算得出的。 但是Rows估算值。我已经看到它被关闭了2倍或更多。

您声明的每一列都是NULLable;这是故意的吗?最不应该是NOT NULL?以下计算没有考虑列可以是NULL

但是,如果很多值都是NULL,则可能有78个字节/行有效。例如,BIGINT通常需要8个字节(加上开销),但如果NULL,则需要0个字节(加上开销)。

一个索引大小:

  • DATE的3个字节
  • DATETIME(3)
  • 的7个字节
  • BIGINT的8个字节((20)无关紧要)
  • 隐藏PK的6个字节

总计为24个字节。

  • 24字节
  • 每行添加20(?)字节开销
  • 次1.45 - BTree块拆分的开销

等于每行64个字节。 * 1.72M行= 110GB。

由于2个索引加倍 - 220GB。 `SHOW TABLE STATUS表示184GB。这两个数字足够接近。 (我使用的一些数字只是近似值。)

没有明确的PRIMARY KEY是顽皮的。使用AUTO_INCREMENT导致4字节INT或使用巨大的8字节BIGINT的空间不足。如果某些列的组合是唯一的,则它们可能是PK。这样做可能会缩小数据大小(通过gettring摆脱6字节的PK)并可能缩小索引(如果3个索引列中的任何一个是PK的一部分)。

有关索引的更多信息:http://mysql.rjweb.org/doc.php/index_cookbook_mysql请注意它对“范围”和索引的说法。如果您使用datereceive_time上的范围,则您拥有的索引将不是最佳。