Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/68.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在MySQL中删除数百万行_Mysql_Query Performance_Maintenance_Sql Delete - Fatal编程技术网

在MySQL中删除数百万行

在MySQL中删除数百万行,mysql,query-performance,maintenance,sql-delete,Mysql,Query Performance,Maintenance,Sql Delete,我最近发现并修复了一个我正在工作的站点中的一个bug,该bug导致表中有数百万个重复的数据行,即使没有这些数据行,该表也会非常大(仍然有数百万)。我可以很容易地找到这些重复的行,并可以运行一个删除查询将它们全部删除。问题是,试图一次删除这么多行会将表锁定很长一段时间,如果可能的话,我希望避免这种情况。在不关闭站点(通过锁定表)的情况下,我能看到的摆脱这些行的唯一方法是: 编写一个脚本,在循环中执行数千个较小的删除查询。从理论上讲,这将绕过锁定表问题,因为其他查询将能够进入队列并在删除之间运行。但

我最近发现并修复了一个我正在工作的站点中的一个bug,该bug导致表中有数百万个重复的数据行,即使没有这些数据行,该表也会非常大(仍然有数百万)。我可以很容易地找到这些重复的行,并可以运行一个删除查询将它们全部删除。问题是,试图一次删除这么多行会将表锁定很长一段时间,如果可能的话,我希望避免这种情况。在不关闭站点(通过锁定表)的情况下,我能看到的摆脱这些行的唯一方法是:

  • 编写一个脚本,在循环中执行数千个较小的删除查询。从理论上讲,这将绕过锁定表问题,因为其他查询将能够进入队列并在删除之间运行。但是它仍然会大大增加数据库的负载,并且需要很长时间才能运行
  • 重命名该表并重新创建现有表(它现在将为空)。然后对重命名的表执行清理。重命名新表,重新命名旧表,并将新行合并到重命名的表中。这是一种需要更多步骤的方法,但应该以最小的中断完成工作。这里唯一棘手的部分是,所讨论的表是一个报告表,因此,一旦它被重命名,并且空的一个放回原位,所有历史报告就会消失,直到我将其放回原位。此外,由于存储的数据类型不同,合并过程可能会有点麻烦。总的来说,这是我现在可能的选择
  • 我只是想知道以前是否有人遇到过这个问题,如果是的话,你是如何在不关闭网站的情况下处理这个问题的,如果对用户的干扰最小的话,你希望这样做?如果我使用第二种方法,或者其他类似的方法,我可以安排这些东西在深夜运行,第二天一早进行合并,只是提前让用户知道,所以这不是什么大问题。我只是想看看是否有人有任何更好或更简单的清洁方法的想法

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

    清洗,漂洗,重复,直到零行受到影响。可能在一个脚本中,迭代之间会休眠一秒或三秒。

    一次可以成批执行2000行。在两者之间作出承诺。一百万行并没有那么多,而且这会很快,除非您的表上有很多索引。

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

    我从优秀的实用程序包(用于MySQL管理的一组Perl脚本)中使用的Maatkit来自Baron Schwartz,他是O'Reilly“高性能MySQL”一书的作者

    目标是低冲击,仅向前 将旧数据从数据库中删除的作业 不影响OLTP查询的表 很您可以将数据插入到另一个数据库中 表,不必在同一个表上 服务器。你也可以把它写到 文件的格式适合于加载 数据填充。或者你两个都不能做,在美国 在这种情况下,它只是一个增量 删除

    它已经为小批量归档不需要的行而构建,并且作为一个额外功能,它可以将删除的行保存到一个文件中,以防您在选择要删除的行的查询中出错

    无需安装,只需抓取并在其上运行perldoc(或阅读网站)即可获取文档。

    根据,
    截断表
    删除的快速替代方法。试试这个:

    TRUNCATE TABLE table_name 截断表名 我在50米的排上试过这个,两分钟内就完成了


    注意:截断操作不是事务安全的;在活动事务或活动表锁定过程中尝试一条记录时出错。以下操作每次删除1000000条记录

     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)中我肯定太难了

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

  • 创建只包含ID的临时表
  • 创建表id_temp_TABLE(temp_id int)

  • 插入应删除的ID:
  • 插入id\u临时表(临时id) 选择

  • 创建新表

  • 将所有记录从一个表插入到另一个表\u new,而不在id\u temp\u表中插入不必要的行

  • 插入到新的表格中。。。。其中表_id不在(选择 与id临时表不同的(临时id)

  • 重命名表

  • 整个过程耗时约1小时。在我的用例中,简单地删除100条记录的批次需要10分钟。

    对于我们来说,
    删除其中的%s ORDER BY%s LIMIT%d
    答案不是一个选项,因为WHERE条件很慢(非索引列),并且会击中master

    从读取副本中选择要删除的主键列表。以这种格式导出:

    00669163-4514-4B50-B6E9-50BA232CA5EB
    00679DE5-7659-4CD4-A919-6426A2831F35
    
    使用以下bash脚本获取此输入并将其分块到DELETE语句中[需要bash≥ 4由于
    mapfile
    内置]:

    sqlchunker.sh
    (记住
    chmod+x
    me,并将shebang更改为指向bash4可执行文件):

    #/usr/local/ceral/bash/4.4.12/bin/bash
    #预期输入格式:
    : 
    这样调用:

    /sql-chunker.sh 1000 ids.txt>批处理\u 1000.sql
    
    这将为您提供一个输出格式如下的文件(我使用的批处理大小为2):

    从my_cool_表中删除,其中id位于('006CC671-655A-432E-9164-D3C64191EDCE','006CD163-794A-4C3E-8206-D05D1A5EE01E');
    从我的“酷”表中删除,其中id位于('006CD837-F1AD-4CCA-82A4-74356580CEBC','006CDA35-F132-4F2C-80
    
    rows_affected = 0
    do {
     rows_affected = do_query(
       "DELETE FROM messages WHERE created < DATE_SUB(NOW(),INTERVAL 3 MONTH)
       LIMIT 10000"
     )
    } while rows_affected > 0
    
    #!/bin/bash
    
    #######################
    #
    i_size=1000
    max_delete_queries=10
    sleep_interval=15
    min_operations=8
    max_query_time=1000
    
    USER="user"
    PASS="super_secret_password"
    
    log_max_size=1000000
    log_file="/var/tmp/clean_up.log"
    #
    #######################
    
    touch $log_file
    log_file_size=`stat -c%s "$log_file"`
    if (( $log_file_size > $log_max_size ))
    then
        rm -f "$log_file"
    fi 
    
    delete_queries=`mysql -u user -p$PASS -e  "SELECT * FROM information_schema.processlist WHERE Command = 'Query' AND INFO LIKE 'DELETE FROM big.table WHERE result_timestamp %';"| grep Query|wc -l`
    
    ## -- here the hanging DELETE queries will be stopped
    mysql-u $USER -p$PASS -e "SELECT ID FROM information_schema.processlist WHERE Command = 'Query' AND INFO LIKE 'DELETE FROM big.table WHERE result_timestamp %'and TIME>$max_query_time;" |grep -v ID| while read -r id ; do
        echo "delete query stopped on `date`" >>  $log_file
        mysql -u $USER -p$PASS -e "KILL $id;"
    done
    
    if (( $delete_queries > $max_delete_queries ))
    then
      sleep $sleep_interval
    
      delete_queries=`mysql-u $USER -p$PASS -e  "SELECT * FROM information_schema.processlist WHERE Command = 'Query' AND INFO LIKE 'DELETE FROM big.table WHERE result_timestamp %';"| grep Query|wc -l`
    
      if (( $delete_queries > $max_delete_queries ))
      then
    
          sleep $sleep_interval
    
          delete_queries=`mysql -u $USER -p$PASS -e  "SELECT * FROM information_schema.processlist WHERE Command = 'Query' AND INFO LIKE 'DELETE FROM big.table WHERE result_timestamp %';"| grep Query|wc -l`
    
          # -- if there are too many delete queries after the second wait
          #  the table will be cleaned up by the next cron job
          if (( $delete_queries > $max_delete_queries ))
            then
                echo "clean-up skipped on `date`" >> $log_file
                exit 1
            fi
      fi
    
    fi
    
    running_operations=`mysql-u $USER -p$PASS -p -e "SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST WHERE COMMAND != 'Sleep';"| wc -l`
    
    if (( $running_operations < $min_operations ))
    then
        # -- if the database is not too busy this bigger batch can be processed
        batch_size=$(($i_size * 5))
    else 
        batch_size=$i_size
    fi
    
    echo "starting clean-up on `date`" >>  $log_file
    
    mysql-u $USER -p$PASS -e 'DELETE FROM big.table WHERE result_timestamp < UNIX_TIMESTAMP(DATE_SUB(NOW(), INTERVAL 31 DAY))*1000 limit '"$batch_size"';'
    
    if [ $? -eq 0 ]; then
        # -- if the sql command exited normally the exit code will be 0
        echo "delete finished successfully on `date`" >>  $log_file
    else
        echo "delete failed on `date`" >>  $log_file
    fi
    
    CREATE TABLE mytable_temp AS SELECT * FROM my_original_table WHERE my_condition;
    TRUNCATE TABLE my_original_table;
    INSERT INTO my_original_table  SELECT * FROM mytable_temp;