12

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

delete from pricedata
where
pricedate > '20120413'

pricedate 是一个char(8)字段。

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

select from pricedata
where
pricedate > '20120413'

并获得 267 条记录(仅此而已),没有错误,为什么它会在删除时阻塞?

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

4

3 回答 3

10

似乎您没有索引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立即从记录中生成提升锁。如果您在语句模式下使用二进制日志并且不允许二进制日志不安全查询(这是默认设置),这将不起作用。

于 2012-04-20T21:24:55.983 回答
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
于 2014-05-20T05:54:14.303 回答
6

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

于 2012-04-21T21:24:28.327 回答