每天在巨大的桌子上添加和删除索引是一个好习惯吗?

时间:2016-11-23 13:33:46

标签: mysql sql database indexing

我正在构建一个连接到MySQL数据库的Web应用程序。 我现在有两张巨大的桌子,每张桌子大约有4千万行,每天都会收到新的行(每天增加约500 000-1000 000行)。

添加新行的过程在夜间运行,而没有人可以使用该应用程序,并且新行' content取决于当前数据库上一些基本SELECT查询的结果。 为了足够快地得到那些SELECT语句的结果,我在每个列上使用简单索引(每个索引一列),在WHERE子句中至少出现一次。

问题是,在白天,对这些表运行一些完全不同的查询,包括一些"范围WHERE子句" (SELECT * FROM t1 WHERE a = a1 AND b = b1 AND (date BETWEEN d1 AND d2))。 我在堆栈上发现了这个非常有用的迷你食谱,根据查询数据库的方式,建议你应该使用哪些索引:http://mysql.rjweb.org/doc.php/index_cookbook_mysql 他们建议使用复合索引:在上面的示例查询中,它将给出INDEX(a,b,date)。

确实提高了白天查询的速度(从1分钟到8秒,所以我真的很开心)。

但是,使用这些复合索引,在夜间添加新行所需的时间完全爆炸(添加每日内容需要一天以上)。

这是我的问题:是否可以每晚删除所有索引,添加新内容,并设置备份每日索引? 或者这是危险的,因为索引不是每天都要重建,特别是在如此大的桌子上? 我知道这样的操作总共需要大约两个小时(丢弃并重新创建INDEX)。

我知道ALTER TABLE table_name DISABLE KEYS;的存在,但我使用的是InnoDB,我相信它不适用于InnoDB表。

欢迎任何高级建议! 提前谢谢。

2 个答案:

答案 0 :(得分:2)

我相信你已回答了自己的问题:你需要白天的指数,而不是晚上。根据您的描述,您应该在晚上删除批量插入的索引,然后重新创建它们。删除数据加载的索引并非闻所未闻,在您的情况下似乎是合适的。

我会问 你是如何插入新数据的。一种方法是一次插入一行值。另一种方法是将值放入临时表(没有索引)并执行批量插入:

insert into bigtable( . . .)
    select . . .
    from smalltable;

这些具有不同的性能特征。您可能会发现使用单个insert(如果您还没有这样做)的速度足够快。

答案 1 :(得分:2)

按日期排序...... PARTITIONing对你来说非常有用,因为你在一年前删除了一些东西。我建议PARTITION BY RANGE(TO_DAYS(...))并将其分为14或54个分区(数月或数周,加上一些开销)。这将消除删除旧行所需的时间,因为DROP PARTITION几乎是即时的。

更多详情请见my partition blog。您的情况听起来像是用例#1和用例#3。

但回到你关于删除和重建索引的聪明想法。对于其他人,我指出了一个警告,即你有足够的时间来触摸桌面以进行重建。

使用PARTITIONing,所有插入的行都会进入“最新”行。分区,对吗?此分区比整个表小很多,因此索引更有可能适合RAM,因此更新速度快10倍(无需重建索引)。如果您提供SHOW CREATE TABLESHOW TABLE STATUSinnodb_buffer_pool_size和RAM大小,我可以帮助您进行算术运算,看看您的上次使用时间是否过长。分区将适合RAM。

关于InnoDB中索引更新的说明 - 它们已被延迟'通过坐在"更改缓冲区&#34 ;,这是buffer_pool的一部分。见innodb_change_buffer_size_max,自5.6以来可用。您使用的是该版本还是更新版本? (如果没有,你应该升级,原因有很多。)

该设置的默认值为25,这意味着25%的buffer_pool被预留用于索引的挂起更新,由INSERT等引起。这类似于"缓存" ,这样就可以对那个索引块进行多次更新,直到它们被淘汰出局。更高的设置应该使索引更新更少地访问磁盘,因此更快完成。

我的目标是...通过增加此设置,您可以使插入(直接,而不是重建)更有效。我认为这可能会加速它:

在每晚INSERTs

之前
innodb_change_buffer_size_max = 70
innodb_old_blocks_pct = 10

每晚INSERTs之后不久:

innodb_change_buffer_size_max = 25
innodb_old_blocks_pct = 37

(我不确定其他设置,但将其推开似乎是合理的。)

同时,innodb_buffer_pool_size的设置是什么?通常,它应该是可用RAM的70%。

在类似的应用程序中,我有一个大的,每小时的转储加载到一个表中,并保留了90天。我通过每天90个分区和24个小时分区来扩展我的分区规则。每天晚上,我花了很多时间(但不到一个小时)做REORGANIZE PARTITION将24小时分区变成新的每日(并放弃了90天的分区)。在每个小时内,负载具有额外的优势,即没有其他任何东西触及1小时分区 - 我可以在7分钟内完成规范化,汇总和加载。整个90天适合400GB。 (旁注:大量分区是性能杀手,直到8.0;所以不要考虑每日分区为你保留1年。)

汇总表使得50分钟的查询(在原型中)缩小到仅2秒。也许你需要一个PRIMARY KEY (a, b, date)的汇总表?这将让你摆脱“事实”这样的指数。表。糟糕,这消除了原始问题的整个前提!查看我博客底部的链接;寻找"汇总表"。一般规则:事实表上没有任何索引(PRIMARY KEY除外);对需要更复杂索引的事物使用汇总表。