Mysql_锁定读取

在MySQL中,为了在事务中确保数据安全,可以使用锁定读取来防止其他事务修改查询到的数据。锁定读取分为共享模式锁定和排他模式锁定,用于保护单个表或跨表的数据操作。在事务中,锁定读取的锁定会在提交或回滚时释放,但必须在禁用自动提交的情况下才能使用。这种技术在处理引用完整性、递增计数器等场景时非常关键,能有效避免并发操作导致的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

锁定读取

如果查询数据,然后在同一事务中插入或更新相关数据,则常规SELECT 语句不能提供足够的保护。其他事务可以更新或删除刚查询的相同行。 InnoDB支持两种类型的 锁定读取,这些读取提供了额外的安全性:

  • SELECT ... LOCK IN SHARE MODE

    在读取的任何行上设置共享模式锁定。其他会话可以读取行,但是在事务提交之前不能修改它们。如果这些行中的任何一个被尚未提交的另一个事务更改,则查询将等待直到该事务结束,然后使用最新值。

  • SELECT ... FOR UPDATE

    对于索引记录,搜索遇到的情况,锁定行和任何关联的索引条目,就像您UPDATE对这些行发出 语句一样。其他事务被阻止SELECT ... LOCK IN SHARE MODE在某些事务隔离级别更新这些行,执行操作或读取数据。一致的读取将忽略读取视图中存在的记录上设置的任何锁定。(记录的旧版本无法锁定;可以通过在记录的内存副本上应用撤消日志来重构它们 。)

这些子句在处理单个表或跨多个表的树结构或图结构数据时最有用。您从一处到另一处遍历边缘或树枝,同时保留返回的权利并更改这些“ 指针 ”值中的任何一个 。

提交或回滚事务时,将释放 由LOCK IN SHARE MODE和 设置的所有锁定FOR UPDATE

注意

仅当禁用自动提交时(START TRANSACTION通过autocommit以0 开始事务或设置 为0 才可以进行锁定读取) 。

除非在子查询中也指定了锁定读取子句,否则外部语句中的锁定读取子句不会锁定嵌套子查询中表的行。例如,以下语句不会锁定table中的行 t2

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2) FOR UPDATE;

要锁定table中的行,t2请向子查询添加锁定的read子句:

SELECT * FROM t1 WHERE c1 = (SELECT c1 FROM t2 FOR UPDATE) FOR UPDATE;
锁定阅读示例

假设您要在表中插入新行 child,并确保子行在表中具有父行parent。您的应用程序代码可以确保整个操作序列的引用完整性。

首先,使用一致的读取来查询表 PARENT并验证父行是否存在。您可以安全地将子行插入表格 CHILD吗?不可以,因为某些其他会话可能会在您SELECT和您之间的时刻删除父行 INSERT,而您却没有意识到。

为避免此潜在问题,请执行以下 SELECT使用LOCK IN SHARE MODE

SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

在之后LOCK IN SHARE MODE的查询返回父'Jones',你可以放心地将孩子记录添加到CHILD表并提交事务。任何试图获取PARENT表中适用行中的排他锁的事务都将 等到您完成操作(即所有表中的数据处于一致状态)后再进行。

再举一个例子,考虑一个表中的整数计数器字段,该字段CHILD_CODES用于为添加到table的每个子代分配唯一标识符 CHILD。不要使用一致读取或共享模式读取来读取计数器的当前值,因为数据库的两个用户可能会看到该计数器的相同值,并且如果两个事务尝试使用以下方式添加行,则会发生重复键错误:与CHILD表相同的标识符。

在这里,LOCK IN SHARE MODE这不是一个好的解决方案,因为如果两个用户同时读取计数器,则其中至少有一个在尝试更新计数器时会陷入死锁状态。

要实现读取和递增计数器,请先使用进行锁定读取FOR UPDATE,然后再递增计数器。例如:

SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;

A SELECT ... FOR UPDATE读取最新的可用数据,并在读取的每一行上设置排他锁。因此,它设置了与搜索的SQL UPDATE 在行上设置的锁相同的锁。

前面的描述只是工作方式的一个示例 SELECT ... FOR UPDATE。在MySQL中,生成唯一标识符的特定任务实际上可以仅通过单次访问表来完成:

UPDATE child_codes SET counter_field = LAST_INSERT_ID(counter_field + 1);
SELECT LAST_INSERT_ID();

SELECT语句仅检索标识符信息(特定于当前连接)。它不访问任何表。

更多内容欢迎关注我的个人公众号“韩哥有话说”,100G人工智能学习资料,大量后端学习资料等你来拿。

韩哥有话说

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值