InnoDB使用DBI下的批处理事务DELETE来锁定耗尽

时间:2015-03-17 20:11:07

标签: mysql perl innodb

我正在使用perl和DBI在一个非常大的mysql表上以1000块的形式执行删除。但是我收到了这个错误:DBD::mysql::db do failed: The total number of locks exceeds the lock table size

以下是带有执行删除的sql语句的perl代码

my $q = q{
DELETE FROM table
WHERE date_format(date, '%Y-%m') > '2015-01' LIMIT 1000 
};

my $rc = '';
until ($rc eq '0E0') {
      $rc = $dbh->do($q);
      $dbh->commit();
}

根据我的经验,只有在尝试使用一个语句一次删除或插入大量记录时才会出现此错误。事实上,我能找到的可行解决方案是:

  1. 使用innodb_buffer_pool_size全局变量增加innodb缓冲池大小。

  2. 以块的形式执行删除。

  3. 我没有尝试解决方案1.有两个原因。首先,它似乎在我的特定情况下,它只会增加缓冲区最终填充之前的时间,虽然我不确定,第二,因为我们不确定它可能对使用数据库的应用程序产生什么影响。 / p>

    我想知道:

    *即使我正在以块的形式删除,为什么会出现此错误?

    * perl和/或DBI是否有针对此问题的快速高级解决方案?

    *任何其他可能导致消息的信息。

2 个答案:

答案 0 :(得分:2)

为什么即使我在块中删除也会发生此错误?

InnoDB使用row-level locking

  

14.5.8由InnoDB中的不同SQL语句设置的锁

     

锁定读取,UPDATEDELETE通常设置记录锁定在处理SQL语句时扫描的每个索引记录。它不会是否在语句中有WHERE条件排除该行。 InnoDB不记得确切的WHERE条件,但只知道扫描了哪个索引范围。锁通常是下一键锁,它也会在记录之前阻止插入“间隙”。

     

[...]

     

DELETE FROM ... WHERE ...在搜索遇到的每条记录上设置一个独占的下一键锁定。

(强调补充)

这意味着您的查询将锁定它扫描的每一行,甚至是与WHERE子句中的条件不匹配的行。

我不知道您的查询的确切执行细节,但我想用一个大表,不会超出innodb_buffer_pool_size的{​​{3}}(我相信是由所有会话共享;其他会话可以与查询同时锁定行)。特别是如果您的查询不使用索引并触发表扫描。

这个问题有快速的高级解决方案吗?

default 128 MB描述了针对这种情况的简单解决方法:

  

如果要从大表中删除多行,则可能会超出InnoDB表的锁表大小。要避免此问题,或者只是为了最小化表保持锁定的时间,以下策略(根本不使用DELETE)可能会有所帮助:

     
      
  1. 选择不要删除的行到与原始表具有相同结构的空表中:
  2.         

    INSERT INTO t_copy SELECT * FROM t WHERE ... ;
    
         
        
    1. 使用RENAME TABLE以原子方式移动原始表格并将副本重命名为原始名称:
    2.         

      RENAME TABLE t TO t_old, t_copy TO t;
      
           
          
      1. 删除原始表:
      2.         

        DROP TABLE t_old;
        
             

        执行RENAME TABLE时,没有其他会话可以访问所涉及的表,因此重命名操作不会受到并发问题的影响。请参见第13.1.20节“RENAME TABLE语法”。

答案 1 :(得分:0)

  1. INDEX(date)
  2. date的类型为DATETIMEDATETIMESTAMP
  3. 以这种方式执行查询:

    DELETE FROM table     在哪里date> “2015年1月31日”     ORDER BY date DESC     限制1000

  4. 当DELETE具有rows_affected == 0

  5. 时停止