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

dav*_*vej 8 mysql innodb locking sql-delete

我试图从大约4000万中删除267条记录.查询看起来像:

delete from pricedata
where
pricedate > '20120413'
Run Code Online (Sandbox Code Playgroud)

定价是一个char(8)领域.

我知道调整innodb_buffer_pool_size,但如果我能做到

select from pricedata
where
pricedate > '20120413'
Run Code Online (Sandbox Code Playgroud)

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

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

Qua*_*noi 8

您似乎没有索引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
    
    Run Code Online (Sandbox Code Playgroud)

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

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


小智 7

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

无需更改 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
Run Code Online (Sandbox Code Playgroud)


dav*_*vej 6

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