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

Sid*_*rma 5 mysql database database-design

我有一个托管在 Google Cloud SQL 上的 MySQL 表,其中包含超过 10 亿行。

>> 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
Run Code Online (Sandbox Code Playgroud)

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

SHOW TABLE STATUS from mktdata where Name = "depth";
Run Code Online (Sandbox Code Playgroud)

在这里,我得到一个包含一行的表输出,其中一些重要字段的值如下:

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 
Run Code Online (Sandbox Code Playgroud)

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

小智 8

这可能会发生。

您有一个索引较多的表。这可能有用也可能没用。

以下是一些常见错误:

  • “我正在对所有列建立索引”——通常没有用。

  • “我对我使用的每一列都建立了索引”——但未能理解“复合”索引的重要性:INDEX(last,first) 与 INDEX(last), INDEX(first)

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

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

显示创建表并描述主要选择。然后我们可以讨论哪些索引是最佳的,哪些可以删除。

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

或者,可能是您的最佳索引集,并且索引大小大于数据。

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


Ric*_*mes 2

Index_length > Data_length 的情况很少见,但也不是“坏”或“错误”。

您没有显式的PRIMARY KEY,因此它是一个隐藏的 6 字节字段,其工作原理有点像 auto_increment。

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

Avg_row_length: 78Date_length-- 由/计算得出Rows不过Rows只是一个估计。我发现它相差两倍或更多。

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

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

1 索引大小:

  • 3 个字节表示日期
  • DATETIME(3) 为 7 个字节
  • BIGINT 为 8 个字节((20)无关紧要)
  • 6字节用于隐藏PK

总共 24 个字节。

  • 24字节
  • 每行增加 20(?) 字节开销
  • 乘以 1.45 - BTree 块分割的开销

这相当于每行 64 字节。*172 万行 = 110GB。

由于有 2 个索引,所以要加倍——220GB。`显示表状态显示 184GB。这两个数字足够接近。(我使用的一些数字只是近似值。)

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

有关索引的更多信息: http://mysql.rjweb.org/doc.php/index_cookbook_mysql 请注意它关于“范围”和索引的内容。date如果您在或上使用范围receive_time,则您拥有的索引将不是最佳的。