MySQL 索引大于存储的数据

Posted

技术标签:

【中文标题】MySQL 索引大于存储的数据【英文标题】:MySQL Index is bigger than the data stored 【发布时间】:2011-08-08 11:20:37 【问题描述】:

我有一个包含以下统计信息的数据库

Tables     Data   Index   Total
11     579,6 MB  0,9 GB  1,5 GB

因此,您可以看到索引接近 2 倍大。有一张表有大约 700 万行,至少占了其中的 99%。

我也有两个非常相似的索引

a) UNIQUE KEY `idx_customer_invoice` (`customer_id`,`invoice_no`),
b) KEY `idx_customer_invoice_order` (`customer_id`,`invoice_no`,`order_no`)

更新:这是最大表的表定义(至少在结构上)

CREATE TABLE `invoices` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `customer_id` int(10) unsigned NOT NULL,
  `order_no` varchar(10) default NULL,
  `invoice_no` varchar(20) default NULL,
  `customer_no` varchar(20) default NULL,
  `name` varchar(45) NOT NULL default '',
  `archived` tinyint(4) default NULL,
  `invoiced` tinyint(4) default NULL,
  `time` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `group` int(11) default NULL,
  `customer_group` int(11) default NULL,
  PRIMARY KEY  (`id`),
  UNIQUE KEY `idx_customer_invoice` (`customer_id`,`invoice_no`),
  KEY `idx_time` (`time`),
  KEY `idx_order` (`order_no`),
  KEY `idx_customer_invoice_order` (`customer_id`,`invoice_no`,`order_no`)
) ENGINE=InnoDB AUTO_INCREMENT=9146048 DEFAULT CHARSET=latin1 |

更新 2

mysql> show indexes from invoices;
+----------+------------+----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table    | Non_unique | Key_name                   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+----------+------------+----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| invoices |          0 | PRIMARY                    |            1 | id          | A         |     7578066 |     NULL | NULL   |      | BTREE      |         |
| invoices |          0 | idx_customer_invoice       |            1 | customer_id | A         |          17 |     NULL | NULL   |      | BTREE      |         |
| invoices |          0 | idx_customer_invoice       |            2 | invoice_no  | A         |     7578066 |     NULL | NULL   | YES  | BTREE      |         |
| invoices |          1 | idx_time                   |            1 | time        | A         |      541290 |     NULL | NULL   |      | BTREE      |         |
| invoices |          1 | idx_order                  |            1 | order_no    | A         |        6091 |     NULL | NULL   | YES  | BTREE      |         |
| invoices |          1 | idx_customer_invoice_order |            1 | customer_id | A         |          17 |     NULL | NULL   |      | BTREE      |         |
| invoices |          1 | idx_customer_invoice_order |            2 | invoice_no  | A         |     7578066 |     NULL | NULL   | YES  | BTREE      |         |
| invoices |          1 | idx_customer_invoice_order |            3 | order_no    | A         |     7578066 |     NULL | NULL   | YES  | BTREE      |         |
+----------+------------+----------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+

我的问题是:

    有没有办法在 MySQL 中找到未使用的索引? 是否存在影响索引大小的常见错误? 可以安全地删除 indexA 吗? 如何衡量每个索引的大小?我得到的只是所有索引的总和。

【问题讨论】:

如果您可以为任何大表运行一些 show create tables 并发布输出,将会很有帮助。 【参考方案1】:

我可能错了,但是第一个索引 (idx_customer_invoice) 是唯一的,第二个 (idx_customer_invoice_order) 不是,所以当你删除它时你可能会失去唯一性约束。没有?

【讨论】:

【参考方案2】:
    indexA 可以删除,因为有一个 indexB 包括 indexA 您的索引长度有什么影响 您的列类型和列长

    使用:

    从 information_schema.tables 中选择 index_length 其中 table_name='your_table_name' 和 table_schema='your_db_name';

    获取您的表 index_length

【讨论】:

3.这给了我一个数字,1003831296,是什么意思? @Peter Lindqvist 索引长度为 1003831296B ;您还可以使用显示表状态,例如“your_table_name” 嗯,我想看看各个索引的大小。【参考方案3】:

您可以删除索引 A,因为正如您所指出的,它是另一个索引的子集。并且可以在不中断正常处理的情况下执行此操作。

索引文件的大​​小本身并不令人担忧,净收益是积极的很容易成为事实。换句话说,索引的有用性和价值不应该因为它导致一个大文件而被打折扣。

索引设计是一门复杂而微妙的艺术,涉及对查询优化器解释和广泛测试的深入理解。但是一个常见的错误是在索引中包含的字段太少以使其更小。另一种是测试数据不足或代表性不足的指标。

【讨论】:

我可以同意“微妙的艺术”。【参考方案4】:

使用

show indexes from table;

定义您在特定表中拥有哪些索引。基数会说明您的索引有多有用。

您可以安全地删除索引(它不会破坏表),但请注意:某些查询可能执行得较慢。首先你应该分析你的查询来决定你是否需要某个索引。

不过,我认为您无法找出特定索引的数据长度。

但是,我想你可能认为如果索引长度大于数据长度两倍是不正常的......好吧,你错了。您的所有索引都可能有用;)如果您有一个提供大量信息的表,并且您必须在大量列上进行搜索,那么该表的索引很容易成为该表的 2 倍大表格数据。

【讨论】:

您能具体说明基数与有用性的对应关系吗?索引的存在是有原因的,针对此表的查询性能对应用程序至关重要。 但是,我很高兴听到它至少没有异常......:P 官方文档中说:“基数越高,MySQL在做join时使用索引的机会就越大”。实际上,这意味着该列将在连接中使用的次数多于基数较低的其他列。基数评估基于统计数据。具体如何?嗯...我不知道 :) 如果基数很高,这个索引也会消耗更多的容量,因为“基数是对索引中唯一值数量的估计”。 @Peter Lindqvist,绝对不是。事实上,如果我们在我工作的站点(每天 600k+ 的用户)的主表上没有很多索引,我们的 MySQL 服务器将总是关闭。此表中的索引长度/数据长度为 2(索引为 2 倍)。存储引擎是 InnoDB。就像你的情况一样。 而且...是的。我同意同事的观点:indexA 可能会被删除。【参考方案5】:

有没有办法在 MySQL 中找到未使用的索引?

数据库引擎优化器会在尝试优化您的查询时选择合适的索引。根据您上次收集索引统计信息的时间,选择的索引会有所不同。由于新的数据重新分区,未使用的索引可能会突然被使用。

可以安全地删除 indexA 吗?

如果 indexA 和 indexB 是 B-Tree 索引,我会说是的。这是因为以相同顺序以相同列开头的索引将具有相同的结构。

【讨论】:

另外两个问题我不确定我能否正确回答。

以上是关于MySQL 索引大于存储的数据的主要内容,如果未能解决你的问题,请参考以下文章

MySQL优化系列2-索引原理和优化

mysql 数据库,表每天会插入30W条数据,该表数据千万级,查询效率很慢,建立索引是不是利大于弊?

查询大于某一时间段的的数据记录,mysql数据库的查询语句该如何写!

mysql 索引

mysql索引类型都有哪些

深入理解Mysql索引底层数据结构与算法