我有一个数据库,其中包含一个跟踪用户状态的表。当我完成处理行时,不再需要将其保留在数据库中并可以删除。
现在假设我想跟踪行而不是删除它(用于历史目的,分析等)。会不会更好:
将数据保留在同一个表格中,并将该行标记为“已使用”(带有额外的列或类似内容)
从表中删除行并将其插入到仅为历史目的创建的单独表中
对于选择#1,我想知道在数据库中留下不必要的行是否会开始影响查询性能。 (我的所有查询都在索引列上,所以这可能没关系?)
对于选择#2,我想知道不断删除行是否会导致碎片等问题?
答案 0 :(得分:3)
从长远来看,查询效果会更好:
永久插入会发生什么:
表增长,索引增长,索引性能(查找)随表的大小而减小,特别是插入性能受损。
删除时发生了什么:
表页面碎片化,因此删除的空间不会像预期的那样100%重复使用,在MySQL中更接近50%。因此,该表仍然增长到您可能对数据量预期的大小的两倍。索引变得支离破碎并变为高边形:它包含新数据,但也包含旧数据的结构。这取决于您的数据结构有多糟糕。然而,这种情况稳定在一定的性能。这个性能点有两个好处:
1)该表的大小更受限制,因此潜在的全表扫描更快
2)你的表现是可以预测的。
由于碎片,但是这个性能点不等于数据量的两倍左右,它往往会更糟糕(基准测试看你自己)。但是,删除方案的好处是因为您拥有较小的数据集,您可以在每个合理的时间段内重建索引一次,从而提高性能。
<强>替代强>
您可以通过两种方法来提高绩效:
切换到MariaDB:在大型数据集上获得约8%的性能(我的观察,数据集只有大约200GB的压缩数据)
查看分区:如果您有一个方便的分区参数,您可以为您创建一系列“小表”,并防止删除,重建和历史数据管理的逻辑。这可能会为您提供最佳性能配置文件。
答案 1 :(得分:1)
如果该表的大部分标记为已删除,则在您查找未删除的记录时,您将会遇到这些问题。将is_deleted
添加到许多索引可能会有所帮助。
如果您完全按年龄删除记录,那么PARTITION BY RANGE(TO_DAYS(...))
是构建表格的绝佳方式。 DROP TABLE
是即时的,创建新周(或月或...)分区的ALTER TABLE ... REORGANIZE ...
也是即时的。有关详细信息,请参阅my blog。
如果您“移动”记录到另一个表,那么由于碎片,该表不会很快收缩。如果你有足够的磁盘空间,这不是一个错误的交易。如果某些查询需要同时查看当前和已归档的记录,请使用UNION ALL
;它非常简单有效。