当前位置: 首页 > 面试题库 >

MySQL:事务与锁定表

洪照
2023-03-14
问题内容

我对事务与锁定表有些困惑,以确保数据库完整性,并确保SELECT和UPDATE保持同步,并且没有其他连接干扰它。我需要:

SELECT * FROM table WHERE (...) LIMIT 1

if (condition passes) {
   // Update row I got from the select 
   UPDATE table SET column = "value" WHERE (...)

   ... other logic (including INSERT some data) ...
}

我需要确保没有其他查询会干扰并执行相同的操作SELECT(在该连接完成更新行之前读取“旧值”。

我知道我可以默认为LOCK TABLES table只确保一次只有1个连接正在执行此操作,并在完成后将其解锁,但这似乎有点过头了。将它包装在事务中是否会做同样的事情(确保没有其他连接会尝试同一进程而另一个仍在处理)?或将一个SELECT ... FOR UPDATESELECT ... LOCK IN SHARE MODE更好?


问题答案:

锁定表可防止其他数据库用户影响您锁定的行/表。但是锁本身并不能确保您的逻辑以一致的状态出现。

想想一个银行系统。当您在线支付账单时,至少有两个受交易影响的帐户:您的帐户,从中提取资金。以及收款人的帐户,资金将汇入该帐户。还有银行的帐户,他们将愉快地存入交易中收取的所有服务费。鉴于(众所周知,这些天,人们)银行异常愚蠢,可以说他们的系统是这样的:

$balance = "GET BALANCE FROM your ACCOUNT";
if ($balance < $amount_being_paid) {
    charge_huge_overdraft_fees();
}
$balance = $balance - $amount_being paid;
UPDATE your ACCOUNT SET BALANCE = $balance;

$balance = "GET BALANCE FROM receiver ACCOUNT"
charge_insane_transaction_fee();
$balance = $balance + $amount_being_paid
UPDATE receiver ACCOUNT SET BALANCE = $balance

现在,由于没有锁也没有交易,该系统容易受到各种竞争条件的影响,最大的竞争条件是对您的帐户或接收方帐户并行执行多次付款。虽然您的代码已取回余额并正在执行huge_overdraft_fees()之类的操作,但其他付款完全有可能并行运行相同类型的代码。他们将取回您的余额(例如100美元),进行交易(取出您要支付的20美元,以及他们给您带来的30美元),现在这两个代码路径都有两个不同的余额:80美元和70美元。取决于最后完成的余额,您最终将在帐户中获得这两个余额中的任意一个,而不是应该以($
100-$ 20-$ 30)结尾的$ 50。在这种情况下,“银行错误对您有利”

现在,假设您使用锁。您的帐单付款($ 20)首先达到目标,因此它赢了并锁定了您的帐户记录。现在您已经拥有了专用权,可以从余额中扣除$
20,然后将新的余额放回原位…您的帐户最终会得到$
80的收益。但是…呃…您尝试去更新收款人的帐户,并且该帐户已被锁定,并且锁定的时间超出了代码允许的范围,导致您的交易超时…我们正在处理愚蠢的银行,因此没有适当的错误处理时,代码只需拉一个exit(),您的20美元就消失了。现在您只剩20美元了,还欠接收者20美元,您的电话就被收回了。

所以…输入交易。您开始交易,从您的帐户中扣款$ 20,然后尝试向收款人借入$ 20
…然后又发生了爆破。但这一次,exit()代码不是可以执行,而是rollback欺骗性地将您的$ 20神奇地加回到您的帐户中。

最后,归结为:

锁可以防止其他人干扰您正在处理的任何数据库记录。事务可防止任何“较晚”的错误干扰您所做的“较早”的事情。谁也不能保证最后一切都会好起来。但是他们在一起。

在明天的课程中:僵局的喜悦。



 类似资料:
  • 我需要使用表锁定(写入)并同时更新几个表,因此我需要同时进行事务,因为锁定不是事务安全的。 从mysql文档中,我阅读了以下 https://dev.mysql.com/doc/refman/5.6/en/lock-tables-and-transactions.html 对事务表(如InnoDB表)使用LOCK TABLES和UNLOCK TALES的正确方法是以SET autocommit=0

  • 13.4.1. START TRANSACTION, COMMIT和ROLLBACK语法 13.4.2. 不能回滚的语句 13.4.3. 会造成隐式提交的语句 13.4.4. SAVEPOINT和ROLLBACK TO SAVEPOINT语法 13.4.5. LOCK TABLES和UNLOCK TABLES语法 13.4.6. SET TRANSACTION语法 13.4.7. XA事务 MyS

  • 问题内容: 我在这里思考:如果您有2个线程执行需要同步的FAST操作,那么非阻塞方法不是比阻塞/上下文切换方法更快/更好的方法吗? 非阻塞的意思是: while(true){如果(checkAndGetTheLock())中断;} 如果您有太多线程在锁中循环,我唯一想到的就是饥饿(CPU耗尽)。 如何平衡一种方法与另一种方法? 问题答案: 以下是 Java Concurrency in Pract

  • 我知道悲观锁定与锁定数据库记录,并在获得锁的“交易”结束时释放它。但这是否意味着悲观锁定是在物理交易中,例如 开始交易 提交事务? 对于一个网页,当用户选择要编辑的记录时,当他按下编辑按钮时,我想悲观地锁定此记录,以便其他人无法更改它,然后在编辑按钮onpress()事件中,我开始物理事务? 似乎是不可能的,因为编辑过程可能很长......它在整个编辑过程中保持数据库事务(按编辑按钮,在网页中编辑

  • 问题内容: 如果我有如下代码: 在开始和提交之间,正在读取的表是否被锁定,并且随后是否会在多用户环境中引起问题,在该环境中,当另一个用户调用上面的相同代码时会发生问题? 如果以上情况有问题,我们是否应始终尝试缩短交易时间?并为此提供便利,而不是在懒惰的关系上调用getter方法,这是否意味着最好使交易简短并为父母的子女手动查找? 问题答案: Hibernate不会做任何事情来显式锁定您从中读取的表

  • 我们尝试使用infinispan作为带有读锁的远程缓存。客户通过“put”进行读取,以获得钥匙锁,正如悲观事务缓存“When cache.put(k1,v1)返回时”一节中所述的infinispan文档,k1被锁定,集群中任何地方运行的其他事务都无法对其进行写入。仍然可以读取k1。当事务完成(提交或回滚)时,k1上的锁被释放。因此,该场景: 远程缓存配置为具有悲观锁定的事务性缓存: 客户端正在使用