我正在构建一个连接到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表。
欢迎任何高级建议! 提前谢谢。
答案 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 TABLE
,SHOW TABLE STATUS
,innodb_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
除外);对需要更复杂索引的事物使用汇总表。