我最近在我正在研究的站点中发现并修复了一个错误,该网站导致表中有数百万个重复的数据,即使没有它们,也会很大(仍在数百万人中)。我可以轻松地找到这些重复的行,并且可以运行单个删除查询以杀死它们。问题在于,试图一杆删除这么多行将桌子上锁定很长时间,如果可能的话,我想避免。我看到的唯一可以摆脱这些行的方法,而无需删除网站(通过锁定桌子)是:

  1. 编写一个将在循环中执行数千个较小删除查询的脚本。从理论上讲,这将围绕锁定的表问题,因为其他查询将能够将其纳入队列并在删除之间运行。但是它仍然会在数据库上飙升很多,并且需要很长时间才能运行。
  2. 重命名表并重新创建现有表(现在它将为空)。然后在更名的桌子上进行清理。重命名新桌子,将旧桌子命名,然后将新行合并到更名的桌子中。这是要采取更多步骤,但应以最小的中断完成工作。这里唯一棘手的部分是,所讨论的桌子是一张报告表,因此,一旦将其更名为一旦将其置于障碍,而一个空的桌子放在其位置上,所有历史性报告都消失了,直到我将其放回原处。再加上合并过程可能有点痛苦,因为存储的数据类型。总的来说,这是我现在的可能选择。

我只是想知道其他人是否以前遇到过这个问题,如果是这样,您如何处理它而无需删除网站,并希望用户对用户有任何干扰吗?如果我使用2号或其他类似的方法,我可以安排这些东西在深夜运行,并在第二天清晨进行合并,然后让用户提前知道,所以这没什么大不了的。我只是想看看是否有人对清理工作有更好的或更容易的方法。

有帮助吗?

解决方案

DELETE FROM `table`
WHERE (whatever criteria)
ORDER BY `id`
LIMIT 1000

洗涤,冲洗,重复直到零行影响。也许在迭代之间睡一秒钟或三个脚本中。

其他提示

我还建议您在表格中添加一些约束,以确保您不会再次发生。一百万行,每次射击1000行,将需要1000个脚本重复。如果脚本每3.6秒运行一次,您将在一个小时内完成。不用担心。您的客户不太可能注意到。

以下删除了1,000,000个记录,一次删除。

 for i in `seq 1 1000`; do 
     mysql  -e "select id from table_name where (condition) order by id desc limit 1000 " | sed 's;/|;;g' | awk '{if(NR>1)print "delete from table_name where id = ",$1,";" }' | mysql; 
 done

您可以将它们分组在一起,并在(ID1,ID2,.. IDN)中删除table_name,我肯定也没有很多困难

我有一个用例,在MySQL中的25m+行表中删除了1M+行。尝试了不同的方法,例如批处理删除(如上所述)。
我发现最快的方法(必需记录的副本到新表):

  1. 创建只有ID的临时表。

创建表ID_TEMP_TABLE(temp_id int);

  1. 插入应删除的ID:

插入id_temp_table(temp_id)选择.....

  1. 创建新的table_new

  2. 插入从表格到table_new的所有记录,而没有不必要的行,

插入table_new ....其中table_id不在id_temp_table中的(选择独特的(temp_id));

  1. 重命名表

整个过程大约需要1小时。 在我的用例中,100个记录上的批次简单删除花了10分钟。

我会用 MK-Arachiver 来自优秀 Maatkit 实用程序软件包(用于MySQL管理的一堆Perl脚本)Maatkit来自O'Reilly“高性能MySQL”书的作者Baron Schwartz。

目的是一项低影响,仅远前的工作,可以将旧数据从桌上淘汰而不影响OLTP查询。您可以将数据插入另一个表中,这不必在同一服务器上。您也可以以适合加载数据填充的格式写入文件。或者,您既不能做,在这种情况下,这只是一个增量删除。

它已经构建了用于将不需要的行分成小批量的额定值,作为奖励,它可以将删除的行保存到文件中,以防您拧紧查询以选择要删除的行的查询。

无需安装,只需抓取 http://www.maatkit.org/get/mk-archiver 并在其上运行perldoc(或阅读网站)以进行文档。

我面临类似的问题。我们有一张非常大的表格,大约500 GB的大小没有分区,而主列列只有一个索引。我们的主人是一台机器的绿巨人,128个内核和512次RAM,我们也有多个奴隶。我们尝试了一些技术来解决行的大规模删除。我将在这里列出所有这些,从我们发现的最糟糕到最佳 -

  1. 一次获取和删除一行。这是您可以做的绝对最糟糕的情况。因此,我们甚至没有尝试过。
  2. 使用primary_key列上的限制查询从数据库中获取第一个'x'行,然后检查以在应用程序中删除的行ID,并使用primary_key ID列表触发单个删除查询。因此,每行2个查询。现在,这种方法很好,但是使用批处理作业在10分钟左右的时间内删除了约500万行,因此,我们的MySQL DB的奴隶滞后105秒。 10分钟活性中的105秒滞后。所以,我们不得不停下来。
  3. 在此技术中,我们在随后的批次获取和每个大小“ x”的删除之间引入了50 ms的滞后。这解决了滞后问题,但我们现在删除每10分钟1.2-130万行,而技术#2则删除了500万行。
  4. 分区数据库表,然后在不需要时删除整个分区。这是我们拥有的最好的解决方案,但需要一张分区的表。我们遵循步骤3,因为我们有一个非常旧的表,仅在主列列上索引。创建分区会花费太多时间,我们处于危机模式。以下是与分区有关的一些链接,我发现有用 - MySQL官方参考, Oracle DB每日分区.

因此,IMO,如果您有能力在桌子上创建分区的奢侈品,请选择选项#4,否则,您将被选项#3所困扰。

一批可以说一次,一次。介于两者之间。一百万行不是那么多,除非您的桌子上有很多索引,否则这将是快速的。

根据 MySQL文档, TRUNCATE TABLE 是快速的替代品 DELETE FROM. 。尝试这个:

TRUNCATE TABLE table_name

我在50m行上尝试了此操作,并且在两分钟内完成。

注意:截短操作不是交易安全;在主动交易或活动表锁定过程中尝试一个错误时发生错误

对我们来说, DELETE WHERE %s ORDER BY %s LIMIT %d 答案不是一个选择,因为其中标准很慢(无索引的列),并且会击中主人。

从读取的replica中选择您希望删除的主要密钥列表。以这种格式导出:

00669163-4514-4B50-B6E9-50BA232CA5EB
00679DE5-7659-4CD4-A919-6426A2831F35

使用以下bash脚本抓住此输入并将其切成删除语句 需要bash≥4,因为 mapfile 内置]:

sql-chunker.sh (记得 chmod +x 我,然后更改Shebang指向您的Bash 4可执行文件):

#!/usr/local/Cellar/bash/4.4.12/bin/bash

# Expected input format:
: <<!
00669163-4514-4B50-B6E9-50BA232CA5EB
00669DE5-7659-4CD4-A919-6426A2831F35
!

if [ -z "$1" ]
  then
    echo "No chunk size supplied. Invoke: ./sql-chunker.sh 1000 ids.txt"
fi

if [ -z "$2" ]
  then
    echo "No file supplied. Invoke: ./sql-chunker.sh 1000 ids.txt"
fi

function join_by {
    local d=$1
    shift
    echo -n "$1"
    shift
    printf "%s" "${@/#/$d}"
}

while mapfile -t -n "$1" ary && ((${#ary[@]})); do
    printf "DELETE FROM my_cool_table WHERE id IN ('%s');\n" `join_by "','" "${ary[@]}"`
done < "$2"

像这样说:

./sql-chunker.sh 1000 ids.txt > batch_1000.sql

这将为您提供一个像这样的输出格式的文件(我使用的批次大小为2):

DELETE FROM my_cool_table WHERE id IN ('006CC671-655A-432E-9164-D3C64191EDCE','006CD163-794A-4C3E-8206-D05D1A5EE01E');
DELETE FROM my_cool_table WHERE id IN ('006CD837-F1AD-4CCA-82A4-74356580CEBC','006CDA35-F132-4F2C-8054-0F1D6709388A');

然后执行这样的语句:

mysql --login-path=master billing < batch_1000.sql

对于那些不熟悉的人 login-path, ,这只是登录而不在命令行中键入密码的快捷方式。

我认为缓慢是由于MySQL的“群集索引”所致,其中实际记录存储在主密钥索引中 - 按主键索引的顺序。这意味着通过主键访问记录非常快,因为它仅需要一个磁盘获取,因为在该磁盘上的记录就在该磁盘上找到了索引中的正确主键。

在没有群集索引的其他数据库中,索引本身不保留记录,而只是“偏移”或“位置”,指示记录在表文件中的位置,然后在该文件中进行第二个提取以检索实际数据。

您可以想象,当在群集索引中删除记录时,必须向下移动所有记录,以避免在索引中创建巨大的漏洞(这是我至少在几年前回想起的 - 以后的版本可能已经改变了)。

知道上述我们发现真正加速在MySQL中删除的内容是以相反的顺序执行删除。这会产生最少的记录运动,因为您是从最终删除记录,这意味着后续删除的对象较少可重新定位。

我没有脚本脚本做到这一点,并且正确执行此操作绝对需要一个脚本,但是另一个选择是创建一个新的,重复的表并选择要保留在其中的所有行。在此过程完成时,请使用触发器将其保持最新。当它同步时(减去要丢弃的行),将两个表重命名为交易中,以便新的表替代了旧表。放下旧桌子,瞧!

这(显然)需要很多额外的磁盘空间,并且可能会对您的I/O资源征税,但是其他情况可能会更快。

根据数据的性质或紧急情况,您可以重命名旧桌子并在其位置创建一个新的空表格,然后在休闲时选择“保持”行进入新桌子...

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top