"锁的总数超过锁表大小"删除267条记录

时间:2012-04-20 20:59:56

标签: mysql locking innodb sql-delete

我正在尝试删除大约4000万条记录中的267条记录。查询如下所示:

delete from pricedata
where
pricedate > '20120413'

pricingate是char(8)字段。

我知道如何调整innodb_buffer_pool_size,但如果可以的话

select from pricedata
where
pricedate > '20120413'

并获得267条记录(这就是全部),没有错误,为什么它会删除?

如果调整innodb_buffer_pool_size不起作用,我该怎么办?

3 个答案:

答案 0 :(得分:7)

您似乎没有pricedate上的索引(或MySQL由于某种原因不使用此索引。)

使用REPEATABLE READ(默认事务隔离级别),InnoDB会在查询读取和过滤掉的记录上放置共享锁,而40M似乎没有足够的空间}锁。

要解决此问题,请使用以下任一解决方案:

  1. pricedate上创建索引,如果它不存在(可能需要时间)

  2. 将您的查询分成更小的块:

    DELETE
    FROM    pricedata
    WHERE   pricedate > '20120413'
            AND id BETWEEN 1 AND 1000000
    
    DELETE
    FROM    pricedata
    WHERE   pricedate > '20120413'
            AND id BETWEEN 1000001 AND 2000000
    

    等。 (根据需要更改id范围)。请注意,每个语句都应该在自己的事务中运行(如果AUTOCOMMIT关闭,请不要忘记在每个语句后提交。)

  3. 使用DELETE事务隔离级别运行READ COMMITTED查询。一旦读取,它将使InnoDB从记录中解除锁定。如果您使用二进制日志语句模式并且不允许binlog-unsafe查询(这是默认设置),则无效。

答案 1 :(得分:4)

(一个迟到的答案,但是当人们在谷歌中发现这个问题时,alwayx很好用)

无需更改innodb_buffer_pool_size或创建索引的解决方案可以限制要删除的行数。

因此,在您的情况下DELETE from pricedata where pricedata > '20120413' limit 100;例如。 这将删除100行并留下167。因此,您可以再次运行相同的查询并删除另一个100。 对于最后的67,它很棘手......当数据库中剩余的行数小于给定的限制时,你将再次得到关于锁数的错误。可能是因为服务器将搜索更多匹配的行以填充100。 在这种情况下,请使用limit 67删除最后一部分。 (当然你也可以在开头使用limit 267

对于那些喜欢编写脚本的人...我在bash脚本中使用的一个很好的例子来清理旧数据:

   # Count the number of rows left to be deleted
   QUERY="select count(*) from pricedata where pricedata > '20120413';"
   AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1`
   ERROR=0
   while [ ${AMOUNT} -gt 0 -a ${ERROR} -eq 0 ]
   do
      ${LOGGER} "   ${AMOUNT} rows left to delete"
      if [ ${AMOUNT} -lt 1000 ]
      then
         LIMIT=${AMOUNT}
      else
         LIMIT=1000
      fi
      QUERY="delete low_priority from pricedata where pricedata > '20120413' limit ${LIMIT};"
      ${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB}
      STATUS=$?
      if [ ${STATUS} -ne 0 ]
      then
         ${LOGGER} "Cleanup failed for ${TABLE}"
         ERROR=1
      fi
      QUERY="select count(*) from pricedata where pricedata > '20120413';"
      AMOUNT=`${MYSQL} -u ${MYSQL_USER} -p${MYSQL_PWD} -e "${QUERY}" ${DB} | tail -1`
   done

答案 2 :(得分:2)

什么有效:将innodb_buffer_pool_size更改为256M(请参阅Quassnoi原始评论下的评论)。