我有一个要求,我们需要在不更新的同时保持锁的状态下更新行。
以下是要求的详细信息,我们将每5分钟对表执行一次批处理update blogs set is_visible=1 where some conditions
,因为该查询要在数百万条记录上运行,所以我们不想阻塞所有行在更新过程中写。
我完全理解没有写锁的含义,这对我们来说很好,因为is_visible列将仅通过此批处理过程进行更新,而没有其他线程会更新此列。另一方面,我们不想阻止的同一表中其他列的更新很多
答案 0 :(得分:0)
最佳实践是在有可能与其他事务同时发生更新时始终获取特定的锁。如果您的存储引擎是MyISAM,则MySQL将在更新过程中锁定整个表,您对此无能为力。如果存储引擎是InnoDB,则MySQL可能只对更新目标记录添加了独占IX锁,但是有一些警告需要注意。要实现这一目标,您要做的第一件事就是SELECT ... FOR UPDATE
:
SELECT * FROM blogs WHERE <some conditions> FOR UPDATE;
为了确保InnoDB仅锁定正在更新的记录,在WHERE
子句中出现的列上需要有一个唯一索引。对于您的查询,假设id
是涉及的列,则它必须是主键,否则您将需要创建一个唯一索引:
CREATE UNIQUE INDEX idx ON blogs (id);
即使具有这样的索引,InnoDB仍可以在索引值之间的记录上应用 gap 锁,以确保执行REPEATABLE READ
合同。
因此,您可以在WHERE
子句中涉及的列上添加索引,以优化InnoDB上的更新。
答案 1 :(得分:0)
首先,如果您默认使用MySQL的InnoDB存储引擎,则没有方法可以在没有行锁的情况下更新数据,除非通过运行将事务隔离级别降低为READ UNCOMMITTED
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
但是,我认为数据库行为不是您所期望的,因为在这种情况下允许脏读。 READ UNCOMMITTED在实践中很少有用。
要补充@Tim的答案,在where子句中使用的列上具有唯一索引确实是个好主意。但是,也请注意,不能绝对保证优化器最终会使用创建的索引来选择这种执行计划。视情况而定,它可能行不通。
对于您的情况,您可以做的是将多头交易拆分为多个空头交易。与其一次更新数百万行,不如一次只扫描数千行。每个短事务提交或回滚时,X锁将释放,从而使并发更新有继续进行的机会。
顺便说一句,我认为您的批次的优先级低于其他在线流程,因此可以将其安排在高峰时段之外,以进一步减少影响。
P.S。 IX锁不在记录本身上,而是附加到较高粒度的表对象上。即使使用REPEATABLE READ事务隔离级别,当查询使用唯一索引时也没有间隙锁定。