MariaDB/MySQL 中无锁删除?`(InnoDB)

问题描述 投票:0回答:4

据我所知,

DELETE FROM ... WHERE
在桌子上发出了锁。

现在,我在 MariaDB 中有一个巨大的 InnoDB 表,大小为 1TB+,正在积极使用,并且在整个数据库搜索完要删除的行之前将其锁定是不可能的。

有什么方法可以删除符合特定条件的行而不在删除发生时锁定它?

以下是此案例的更多具体信息:

  • 服务器正在运行 Debian Stretch 存储库中的 MariaDB 10.1.22-3
  • 服务器有 32 GB 内存和
    innodb_buffer_pool_size = 20G
  • 数据库大小为 1TB+,始终有大量活跃的
    INSERT
    SELECT
  • 整个数据库只包含2张表:
    • 实际数据的一个表(基本上具有类似
      data (BIGINT id, LONGTEXT data)
      的结构(其中
      data
      是一大块 JSON。我知道这不是一个完美的关系数据库模型,但 JSON 来自第三方,它非常复杂,并且可能包含第三方随时进行的结构更改,恕不另行通知)
    • 还有一张表,用于某种“索引”以满足
      SELECT
      s。 (简化的示例,这可以具有类似
      data_index (BIGINT id, INT userId, INT itemId, BIGINT timestamp)
      的结构,因此我可以在 userId 和 itemId 上使用
      SELECT
      ,并加入实际数据。(时间戳是以毫秒为单位的 unix 时间戳)
  • 就像我说的,数据只能在有限的时间内存储。所以基本上现在我想创建一个每天运行一次的 cronjob 来删除超过 7 天的行。

为了完成任务,我自然会想出这个简单的查询:

DELETE `data`, `data_index`
FROM `data_index`
LEFT JOIN `data` ON `data`.`id` = `data_index`.`id`
WHERE `timestamp` > (NOW() * 1000) - (7 * 24 * 60 * 60 * 1000)

但这可能会锁定桌子相当长的时间。我怎样才能完成相同的任务而不锁定表,以便数据库对其他

SELECT
INSERT
查询保持功能?

mysql database mariadb
4个回答
6
投票

不,如果不锁定所检查的行,则无法删除。

但是您可以通过在您正在搜索的

timestamp
列上创建索引来最大限度地减少检查的行数。

这还将针对您可能尝试在表末尾插入的潜在行创建间隙锁,以确保新行不会影响 DELETE。

在InnoDB中,像DELETE创建的普通写锁不会阻塞读。并发事务仍然可以读取行,甚至是您正在删除的行。

普通写锁不会锁定整个表。嗯,它使用“意向锁”锁定表,这只会阻止其他表锁,例如 ALTER TABLE 或 DROP TABLE 所需的锁。换句话说,当表正在进行任何读取或写入时,您无法更改/删除表。 您可能会喜欢我的演示:

用简笔画解释 InnoDB 锁定


5
投票
https://dev.mysql.com/doc/refman/5.7/en/innodb-locks-set.html

我读到它会产生行锁,而不是表锁。 无论如何你都可以尝试

删除...从...哪里...限制x

并根据需要执行多次。在执行之间,其他查询可以进入并最大限度地减少影响。当然,请在低负载时间完成这项工作。


2
投票
(NOW() * 1000) - (7 * 24 * 60 * 60 * 1000)

看起来不是有效时间。 它是

20170519568613000
,看起来像是
DATETIME
和某种毫秒的混合体。 也许您想要
UNIX_TIMESTAMP() * 1000 - (7 * 24 * 60 * 60 * 1000) = 1494742589000

您希望删除多少行? 如果数量很大,那就考虑分区,或者

分块删除


0
投票
purge_old_transactions

,其工作原理如下: create procedure purge_old_transactions() begin create temporary table if not exists tmp_purge ( `transaction_id` <datatype> not null, primary key (`transaction_id`) ) engine=memory; delete from tmp_purge; set transaction isolation level read uncommitted ; insert into tmp_purge(`transaction_id`) select transaction_id from <many-tables> where <complex-criteria> limit 1000 -- whatever chunk you can comfortably swallow while locking (later) ; commit; delete fatty from fatty_table fatty join tmp_purge tmp on tmp.transaction_id = transaction_id ; end $$ delimiter ;

	
© www.soinside.com 2019 - 2024. All rights reserved.