详解事务、隔离级别、悲观锁和乐观锁

发表于 3年以前  | 总阅读数:334 次

今天,我们来聊数据库事务ACID、隔离级别、悲观锁和乐观锁。无论是在工作中,还是在笔试面试中,数据库相关的问题,总是绕不开,不会的话,很容易歇菜,你懂的。

数据库事务场景

在银行系统中,数据库事务是必须的。在电商系统中,也是如此。

来看下A给B汇款100元的例子,可以看到,A账户扣款100元,此时如果进程崩溃或者机器掉电,那么这100元就没有加到B的账户中,自然会导致用户的强烈投诉:

如果先给B账户加钱,然后给A账户扣钱,会怎样呢?可以看到,此时如果进程崩溃或者机器掉电,银行白白给B加了100元,而没有扣减A的100元,只怕银行会亏得没裤子穿:

墨菲定律说:凡是会出错的事,一定会出错。 而且,一旦发生,将造成较大危害。所以,在软件设计上,有必要考虑这种异常。进程崩溃,机房掉电,网络抖动,硬件损坏,都应该被视为常态,都应该被考虑到。

如果要在应用层处理这些异常问题,将极为困难,甚至几乎不可能。做过软件开发的朋友应该知道,很多时候,如果异常问题处理得不妥当,将要投入大量时间分析和补救,且不一定能补救回来。

所以,有必要引入数据库事务。所谓事务,就是一组SQL操作,它们不可分割,不能被打断,要么都成功,要么都失败。具体地说,就是要满足ACID性质。

引入事务之后,应用层再也不用担心上述异常了,因为数据库已经为我们处理得很好了。很多书籍把ACID放在一起叙述,我认为有点扯,因为他们并不正交。在我看来,C是AID的最终目的。下面,我们来看下ACID性质。

Atomicity(原子性)

古希腊哲学家德谟克利特认为,原子是构成世界万物的单元,且不可分割: 所以,原子性这个词的含义就是不可分割。以上述的步骤一和步骤二为例,它们是一个整体,不可分割,要么同时成功,要么同时失败。

那么具体怎样去实现原子性呢?有兴趣的朋友可以了解下undo log, 在此不展开叙述。我们不是DBA, 不需要精通数据库的众多具体细节,但是,至少要知道大概的原理和可行性,这可以为我们解决类似问题提供思路和参考。

Consistency(一致性)

一致性是我们最终的目的,笼统地说,一致性就是要确保数据是正确无误的。所谓valid data, 其实就是正确无误的data: 原子性没法完全保证一致性,因为在多个事务操作数据库时,还需要涉及到隔离性。

Isolation(隔离性)

隔离性,就是要隔离不同事务,隔离性是本文的重点,我们会针对不同的隔离级别进行介绍,先来看一眼: 需要强调的是,每种存储引擎的实现不尽一致,在可重复读隔离级别下,有的朋友在进行验证时,并未出现所谓的幻读,这是因为:

  • InnoDB通过MVCC部分地解决了幻读问题:a. 针对select不会有幻读;b. 针对select for update会有幻读。
  • 其它很多数据库引擎,还是存在幻读问题。

关于InnoDB是否存在幻读问题,我们将在本文的实验部分进行验证。

Durability(持久性)

持久性的意思是,一旦事务提交,它对数据库的变更是永久性的。实际上,事务提交后,最后不一定会落地到数据库中(比如落地时机器断电了),那怎么保证一定要落地成功呢?

这就涉及到redo log了,我们也不需要具体知道redo log的细节,但是,我们从逻辑上可以缕清:redo log要记录什么?redo log为什么能保证持久性? 很多时候,就是这样,对于不太相关的东西,可以不精通,但至少要了解大概逻辑和思路。这样才能说服自己,才不会有一种玄乎其玄的感觉。

接下来,我们看这个问题:客户端A的事务,是否应该看到客户端B的事务所作的修改?这就涉及到数据库事务的隔离级别。

在本文中,如下图示都是基于我的实际验证。建议有兴趣的朋友一起动手,感受一下。

说明:事务A和事务B位于两个不同的终端窗口,对应两个不同的进程,在改变隔离级别时,仅改A的隔离级别来进行验证。

1.读未提交

我们来看看读未提交的场景: 可见,设置读未提交后,事务B在未提交时,事务A读出了a=10, 这是脏数据(B事务被回滚了),这就是所谓的“脏读”。

2.读已提交

我们来看看读已提交的场景: 可见,设置读已提交后,事务B在未提交时,事务A读出了a=0, 在事务B提交后,又读出了a=10, 出现了“不可重复读”。

3 . 可重复读

我们来看看可重复读的场景: 可以看到,看事务A内,读取的值具有前后不变的特点,这就是“可重复读”。只有当事务A提交后,才能读出a=10. 在MySql中,默认的隔离级别就是可重复读。

接下来,我们看一个魔幻现象: 在B事务提交后,A事务执行select ... where a = 100时,发现还是无记录,可见此时并未产生“幻读”。但是,如果用select for update, 则出现了“幻读”现象。

可见,在InnoDB可重复读的隔离级别中,并未完全解决“幻读”问题,而是解决了读数据情况下的“幻读”问题,而对于修改的操作依然存在“幻读”问题。

4.串行化

我们来看看串行化的场景: 可以看到,即使对于读操作,也会加锁,一个事务要等待另一个事务完成。串行化是完全的隔离级别,会导致大量超时和锁竞争问题,在高并发场景中,较少用到串行化。在SQLite中,默认的隔离级别就是串行化。

丢失更新问题

有了这些隔离级别,就万事大吉了吗? 当然不是。以MySql为例,在默认隔离级别下,会有丢失更新的问题。

领导A给你加了30元的鸡腿,领导B给你加了40元的鸡腿,最终结果发现,只有40元鸡腿,显然,这是不合理的: 怎么解决这种问题呢?可以考虑引入悲观锁或乐观锁。

悲观锁

所谓悲观锁,就是持悲观态度,认为一定会有冲突,所以提前加强保护。悲观锁可以用select for update来实现,之前项目中就经常这样玩,但后来重构了代码,统一优化成了分布式锁。

使用分布式锁, 代码示意如下(如下使用方法有问题):

func proc() {
money := queryMoneyFromDb()
  begin lock
     begin transaction
         money += req.Money
         setToDb(money)
     end transaction
  end lock
}

上述代码的使用是有问题的,想一下为什么?

当两个进程都读取money=0后,进程A获取锁,并且执行完毕后,money=30,然后进程B获取锁,执行完毕后,显然可知,最后的结果是money=40,仍然存在丢失更新的问题。

曾经在项目中,就出现过这种错误,导致了低概率的金额不匹配,比较难发现问题,最后还是通过对账发现了,然后查出上述错误的用法。

正确使用悲观锁代码示意如下:

func proc() {
    begin lock
      begin transaction
        money := queryMoneyFromDb()
        money += req.Money
        setToDb(money)
      end transaction
    end lock
}

乐观锁

所谓乐观锁,就是抱有很乐观的态度,也就是假定不会存在数据冲突(即使有冲突也不怕,乐观得很)。具体实现时,可以在数据上打一个version标记,基于version进行控制,代码示意如下:

func proc() {
   begin transaction
      select * from T where user_id = 123456  // 假设查到的version为100
      update T set money = xxx, version = version + 1 where user_id = 123456 and version = 100;
   end transaction
}

分析一下:进程A和进程B都读到了version=100的数据,进程A在加完30元后,同时让version变成了101;此时进程B去执行,突然发现不满足where version=100这个条件,所以更新失败,这是合理的,符合预期,宁可执行失败,也不能产生数据错误。

这里有一个极为微妙的问题:在MySql可重复读隔离级别下,当进程A的update执行成功并且提交事务后,version变为了101, 但是在进程B看来,version还是100(可重复读), 为什么B在执行update的时候,在where version=100条件下又无法真正执行update呢?

要注意,可重复读是针对select而言的,而不是select for update或者update之类的操作,当A进程事务提交后,B进程事务看到的情况如下:


mysql> select * from user;
+----+-------+---------+
| id | money | version |
+----+-------+---------+
|  1 |     0 |     100 |
+----+-------+---------+
1 row in set (0.00 sec)

mysql> select * from user for update;
+----+-------+---------+
| id | money | version |
+----+-------+---------+
|  1 |    30 |     101 |
+----+-------+---------+
1 row in set (0.25 sec)

mysql> select * from user;
+----+-------+---------+
| id | money | version |
+----+-------+---------+
|  1 |     0 |     100 |
+----+-------+---------+
1 row in set (0.00 sec)

可见,对B事务而言,用select看,看不到B事务的更新,这满足事务的可重复读。但是,当使用select for update时,能看到B事务的更新。

所以,当B事务使用update尝试更新where version=100的记录时,发现更新失败,这是我们期望的结果,宁可执行失败,也不能产生数据错误。针对这种失败,可以采用多次重试。

至于悲观锁和乐观锁的选择,还是要依赖于具体业务。数据的一致性如此重要,可千万别把用户的钱给算错了。

对于频繁写冲突的业务,用乐观锁肯定是不太好的,重试操作会增加各种开销,此时可以考虑使用悲观锁。对于写冲突较少发生的场景,那乐观锁就非常适合了。

·················· END ··················

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/zEEF3v62GQa5q2bChqtJVw

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237229次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8063次阅读
 目录