据我所知,
DELETE FROM ... WHERE
在桌子上发出了锁。
现在,我在 MariaDB 中有一个巨大的 InnoDB 表,大小为 1TB+,正在积极使用,并且在整个数据库搜索完要删除的行之前将其锁定是不可能的。
有什么方法可以删除符合特定条件的行而不在删除发生时锁定它?
以下是此案例的更多具体信息:
innodb_buffer_pool_size = 20G
INSERT
和 SELECT
data (BIGINT id, LONGTEXT data)
的结构(其中 data
是一大块 JSON。我知道这不是一个完美的关系数据库模型,但 JSON 来自第三方,它非常复杂,并且可能包含第三方随时进行的结构更改,恕不另行通知)SELECT
s。 (简化的示例,这可以具有类似 data_index (BIGINT id, INT userId, INT itemId, BIGINT timestamp)
的结构,因此我可以在 userId 和 itemId 上使用 SELECT
,并加入实际数据。(时间戳是以毫秒为单位的 unix 时间戳)为了完成任务,我自然会想出这个简单的查询:
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
查询保持功能?
不,如果不锁定所检查的行,则无法删除。
但是您可以通过在您正在搜索的
timestamp
列上创建索引来最大限度地减少检查的行数。
这还将针对您可能尝试在表末尾插入的潜在行创建间隙锁,以确保新行不会影响 DELETE。
在InnoDB中,像DELETE创建的普通写锁不会阻塞读。并发事务仍然可以读取行,甚至是您正在删除的行。
普通写锁不会锁定整个表。嗯,它使用“意向锁”锁定表,这只会阻止其他表锁,例如 ALTER TABLE 或 DROP TABLE 所需的锁。换句话说,当表正在进行任何读取或写入时,您无法更改/删除表。 您可能会喜欢我的演示:
用简笔画解释 InnoDB 锁定,其工作原理如下:
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 ;