在 MariaDB 中,默认的隔离级别是可重复读。我理解这意味着当我打开事务时,我不会看到任何对数据库的并发写入。
我还遇到一种情况,需要确保不对行进行并发更改。为此,我可以获得独占锁。我的问题实际上是关于两者如何结合使用。
如果我在更新事务中获取锁,则意味着我可能打开事务并被阻止,因为另一个进程具有排它锁,当我获取锁时,可能已对我看不到但可能需要的行进行了更改要知道。
我对于更改隔离级别犹豫不决,因为这是一个特定的场景。
目前,我已决定在开始更新事务之前获取锁,但这使得释放锁变得更加成问题,因为一旦我的事务提交,锁就不会被释放。我错过了一些明显的东西吗?所以基本上我打开一个事务 SELECT FOR UPDATE。然后打开第二个事务来进行更新。
半相关:以上是否意味着当事务级别是可重复读取时乐观锁定是一种禁忌?乐观锁定涉及检查版本/时间戳以确保自最初读取以来没有其他事务对其进行修改。通过可重复读取,在事务完成之前我们无法看到其他事务,这违背了目的。或者这只是意味着我应该在事务之外获取锁?
但您的交易可能会被阻止。
当发生阻塞时,会发生以下两种情况之一:
innodb_lock_wait_timeout
。)在这种情况下,要阻塞的第二个事务将停止,直到第一个事务释放某些锁。你不会意识到这一点,一切都会好起来的(尽管会延迟)。请务必使用
FOR UPDATE
任何SELECTs
获取随后可能在该事务中更改的数据的数据。这可以让其他线程知道您不希望该值意外更改。遵循上述建议,您可能永远不需要更改隔离级别。
“乐观锁定”——代码针对以下方面进行了优化
COMMIT
;ROLLBACK
需要更多的工作。“脏读”比“可重复读”更快,但你会失去事务 ACID 的大部分好处。
“在事务之外获取锁”——实际上不可能。您应该最大限度地减少事务中的工作量。
不管你如何表述,一切都涉及交易。
autocommit=ON
withoutBEGIN...COMMIT
使每个语句本身就是一个事务。必要的锁将被采取并兑现。
LOCK
语句;它可能几乎没有 InnoDB 表的位置。请注意我如何引导您摆脱对“锁”和“隔离级别”的纠缠。
另一种看待它的方式......
当您开始事务(并假设“可重复读取”)时,巨大的相机会拍摄所有数据的照片。您的交易将使用图中的数据进行。甚至
UPDATEing
图片中的值。当您执行此操作时COMMIT
,您对图片所做的所有更改都会立即复制到磁盘以供其他人查看。在这个简单的视图中缺少的是,如果其他进程试图更改您正在更新的相同值,该怎么办。这就是进程之间进行“锁”通信的地方。然后详细的规则(共享读、独占等)开始发挥作用。这会导致一个进程继续、延迟或死锁来解决锁定。
“巨型相机”是通过将序列号(la 时间戳)连接到每个表的每一列中的每个值来实现的。
当a
BEGIN
发生时,抓取序列号;它用来表示“我只能看到该数字之前的最新值”。当列为 时UPDATEd
,[暂时]有两个值,每个值都有不同的序列号。之后COMMIT
,列值的所有额外副本都会[在后台]被清除。(参见“历史”)回到“乐观”——注意新值如何准备永久存储,同时
ROLLBACK
需要进行更混乱的清理。回到“可重复读取”——注意巨幅图片如何继续只显示一个值;因此,
SELECT
无论其他进程试图改变什么,相同的结果都会得到相同的结果。“脏读”不关心重复性;因此可以避免锁定并运行得更快。“可序列化”不关心事务性的东西,但由于无法同时运行多个进程而运行速度较慢。
我希望这种简单的外观能有所帮助。它帮助我了解 InnoDB 的复杂性。也希望其他人指出其中的重大缺陷。
您的意思是第二个声明吗?
如果您确实开始第二个事务,那么您获取的锁
SELECT FOR UPDATE
将被释放。该START TRANSACTION
语句会导致隐式提交,从而释放前一个事务持有的所有锁。SELECT FOR UPDATE
通常,当您准备UPDATE
在同一事务中获取一些锁时,您会使用这些锁。这不是很明显,但在 InnoDB 中,锁定语句(包括
SELECT FOR UPDATE
或SELECT FOR SHARE
)始终锁定行的最近提交版本,就像您使用读提交隔离级别一样,即使非锁定读无法查看该版本该行的版本。这让一些人感到惊讶,但它很有帮助,因为它避免了一些幻象更新。