数据库中的悲观锁和乐观锁

代码云阙客
• 阅读 2572

现在我们简单聊一下数据库中的悲观锁和乐观锁。

悲观锁

悲观锁正如其名称,比较悲观。总会认为:每当修改数据时,会有其他线程也会同时修改该数据。所以针对这种情况悲观锁的做法是:读取数据之后就加锁(eg: select...for update),这样别的线程读取该数据的时候就需要等待当前线程释放锁,获得到锁的线程才能获得该数据的读写权限。从而保证了并发修改数据错误的问题。但是由于阻塞原因,所以导致吞吐量不高。悲观锁更适用于多写少读的情况。

数据库中的悲观锁和乐观锁

场景: 同学A和同学B都要给你转500块钱(开心坏了吧,这样最终你能得到1000块钱)。

使用悲观锁的流程:

  1. 同学A获取到你的账户余额balance = 0并对该条记录加锁。
  2. 同学B获取你的账户余额。由于同学A已经对这条记录加锁了,所以同学B需要等同学A转帐完成(释放锁)才能获得余额。
  3. 同学A转账完成并释放锁,此时你的账户余额balance=balance + 500 = 500
  4. 同学B获取到你的账户余额balance = 500,并对该条记录加锁(如果你人缘好,此时同学C给你转账也是需要等待同学B转账完成才可以转账哦)
  5. 同学B转账完成并释放锁(如果有同学C想给你转账,此时同学C就可以获得锁并转账了)。此时你的账户余额为balance = balance + 500 = 1000
  6. 最终你开开心心的得到了1000块钱。

假设转账过程没有锁,我们看看会发生什么:

  1. 同学A获取到你的账户余额balance_a = 0(没有加锁,此时同学B也可以获取到账户余额)
  2. 同学B获取到你的账户余额balance_b = 0
  3. 同学A转账完成,此时你的账户余额为balance = balance_a + 500 = 500
  4. 同学B转账完成,此时你的账户余额为balance = balance_b + 500 = 500
  5. 最终同学A和同学B都转了500,但是你最终只获得了500。这一定是不能接受的吧。

数据库中的悲观锁和乐观锁

丢失的500块去哪里了呢?从第2步可以看到同学B获取到的账户余额是0,而不是同学A转帐之后的余额500。所以问题出在这里,这是高并发场景的常见问题。所以加锁是非常必须的。但是加了悲观锁,同学都要排队给我转账,对于没有耐心的同学就直接不转帐了,我岂不是错失了发财的好机会。那有什么好办法呢?答案就是下面的乐观锁

乐观锁

乐观锁顾名思义比较乐观,他只有在更新数据的时候才会检查这条数据是否被其他线程更新了(这点与悲观锁一样,悲观锁是在读取数据的时候就加锁了)。如果更新数据时,发现这条数据被其他线程更新了,则此次更新失败。如果数据未被其他线程更新,则更新成功。由于乐观锁没有了锁等待,提高了吞吐量,所以乐观锁适合多读少写的场景。

常见的乐观锁实现方式是:版本号version和CAS(compare and swap)。此处只介绍版本号方式。

要采用版本号,首先需要在数据库表中新增一个字段version,表示此条记录的更新版本,记录每变动一次,版本号加1。依旧使用上面转账的例子说明:

  1. 同学A获取到你的账户余额balance = 0和版本号version_a = 0
  2. 同学B获取到你的账户余额balance = 0和版本号version_b = 0
  3. 同学A转账完成update table set balance = ${balance}, version = version + 1 and version = 0。(此时版本号为0,所以更新成功)
  4. 同学B转账完成update table set balance = ${balance}, version = version + 1 and version = 0。(此时版本号为1,所以更新失败,更新失败之后同学B再转一次即可)
  5. 同学B重新转帐之后,你还是美滋滋的获得了1000。

总结

悲观锁:读取时加锁,更新完释放锁,再此过程中会造成其他线程阻塞,导致吞吐量低,适用于多写场景。

乐观锁:不加锁,只有在更新时验证数据是否被其他线程更新,吞吐量较高,适用于多读场景。

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
java 里面 的锁
A、乐观锁、悲观锁B、偏向锁、轻量级锁、重量级锁C、互斥锁、自旋锁、适应性自旋D、可重入锁、读写锁E、公平锁、非公平锁F、总线锁、缓存锁(linux操作系统底层,由CPU提供的锁)G、锁优化:减少锁持有时间、减小锁粒度、锁分离、锁粗化、锁消除信号量与互斥量:信号
Easter79 Easter79
3年前
TiDB 4.0 新特性前瞻:白话“悲观锁”
如果说在TiDB3.0中,悲观锁是“千呼万唤始出来,犹抱琵琶半遮面”。那么在TiDB4.0中,悲观锁在经历了市场与时光的考验后,无论是性能还是稳定性都能够“轻拢慢撚抹复挑,初为《霓裳》后《六幺》”。TiDB4.0悲观锁,欢迎大家尝鲜与反馈。本文将从使用者的角度,介绍悲观锁的使用与注意事项,主要分为以下几方面:白话悲观锁
Easter79 Easter79
3年前
TiDB 性能竞赛 11.16
TiDB实现了快照隔离级别的分布式事务,支持悲观锁、乐观锁,同时也解决了大事务的难点。事务是数据库的基础,提供高效的、支持完整ACID的分布式事务更是分布式数据库的立足之本。事务是数据库执行的最小单元,允许用户将多个读写操作组合为一个逻辑单元。事务需要满足原子性、一致性、隔离性和持久性,也就是ACID。数据库有多种并发控制方法,乐观并发控制(
Wesley13 Wesley13
3年前
MySQL 乐观锁和悲观锁
前言  1)在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和一致性以及数据库的一致性。  2)加锁是为了解决更新丢失问题更新丢失  两次更新同时进行,后一次更新覆盖了前一次更新的情况,更新丢失是数据没有保证一致性导致的。事务A查询余额,
Wesley13 Wesley13
3年前
Java原子类操作原理剖析
◆CAS的概念◆对于并发控制来说,使用锁是一种悲观的策略。它总是假设每次请求都会产生冲突,如果多个线程请求同一个资源,则使用锁宁可牺牲性能也要保证线程安全。而无锁则是比较乐观的看待这个问题,它会假设每次访问都没有冲突,这样就提高了效率。但是事实难料、这个冲突是避免不了的,无锁也考虑到了肯定会遇到冲突,对于冲突的解决无锁就使用一种比较交换(CA
Wesley13 Wesley13
3年前
Java并发编程:Java中的锁和线程同步机制
锁的基础知识锁的类型锁从宏观上分类,只分为两种:悲观锁与乐观锁。乐观锁乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一
Wesley13 Wesley13
3年前
Mysql 乐观锁 和悲观锁
平时看博客或技术文章的时候,经常被各种锁搞得晕晕乎乎,包括在自旋锁、可重入锁、公平锁等等、乐观锁、悲观锁、行锁、表锁、意向锁、排它锁等。前段时间终于把Java多线程相关的锁有机会学习了一遍。现在开始整理mysql相关的锁概念。先从乐观锁和悲观锁开始聊聊。首先要知道,乐观锁和悲观锁不是真实存在的锁,只是两种抽象概念性的东西,就相当于Java中的接口,只
Wesley13 Wesley13
3年前
5分钟 BeetlSQL 快速入门
企业应用面临的问题高效编写数据库访问代码内置CRUD,不需要写SQL支持OR/Mapping悲观锁,乐观锁,逻辑删除等辅助支持等即支持简单的CRUD,也支持数十行,上百行SQL编写跨数据库平台支持,Oracle,Postgres,Mysql,以及SQLS
Wesley13 Wesley13
3年前
mysql面试题总结
1.Mysql中的myisam与innodb的区别?2.InnoDB存储引擎的四大特性?3.什么是事务?4.数据库事务的四大特性?5.不考虑事务的隔离性,会发生几种问题?6.MySQL数据库提供的四种隔离级别?7.有多少种日志?8.事务是如何通过日志来实现的?9.数据库的乐观锁和悲观锁是什么?10.什
Wesley13 Wesley13
3年前
Java中所有锁介绍
在读很多并发文章中,会提及各种各样锁如公平锁,乐观锁等等,这篇文章介绍各种锁的分类。介绍的内容如下:1.公平锁/非公平锁2.可重入锁/不可重入锁3.独享锁/共享锁4.互斥锁/读写锁5.乐观锁/悲观锁6.分段锁7.偏向锁/轻量级锁/重量级锁8.自旋锁上面是很多锁的名词,这些分类并不是全是指锁的
Stella981 Stella981
3年前
MMVC多版本并发控制&事务的特性与隔离级别
多版本并发控制(Multiversionconcurrencycontrol,MVCC)是一种思想,有很多种实现方法。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,PessimisticConcurrencyControl,缩写“PCC”)是一种并发控制的方法。在
代码云阙客
代码云阙客
Lv1
我一直在你身后从未离开,只要你能回头
文章
7
粉丝
0
获赞
0