mysql乐观锁实现 防止消费重复消费_一文带你理解脏读,幻读,不可重复读与mysql的锁,事务隔离机制...

首先说一下数据库事务的四大特性

1 ACID

事务的四大特性是ACID(不是"酸"....)

1.1 A:原子性(Atomicity)

原子性指的是事务要么完全执行,要么完全不执行.

1.2 C:一致性(Consistency)

事务完成时,数据必须处于一致的状态.若事务执行途中出错,会回滚到之前的事务没有执行前的状态,这样数据就处于一致的状态.若事务出错后没有回滚,部分修改的内容写入到了数据库中,这时数据就是不一致的状态.

1.3 I:隔离性(Isolation)

同时处理多个事务时,一个事务的执行不能被另一个事务所干扰,事务的内部操作与其他并发事务隔离.

1.4 D:持久性(Durability)

事务提交后,对数据的修改是永久性的.

2 Mysql的锁

Mysql的锁其实可以按很多种形式分类:

  • 按加锁机制分,可分为乐观锁与悲观锁.
  • 按兼容性来分,可分为X锁与S锁.
  • 按锁粒度分,可分为表锁,行锁,页锁.
  • 按锁模式分,可分为记录锁,gap锁,next-key锁,意向锁,插入意向锁.

这里主要讨论S锁,X锁,乐观锁与悲观锁.

2.1 S锁与X锁

S锁与X锁是InnoDB引擎实现的两种标准行锁机制.查看默认引擎可使用

show 

作者的mysql版本为8.0.17,结果如下:

8d7fd4c52a736371789cfbeb2c2489a2.png

先建好测试库与测试表,很简单,表就两个字段.

create 

72d6baab3a3052ac19e189f078be82b6.png

2.1.1 S锁

S锁也叫共享锁,读锁,数据只能被读取不能被修改.

玩一下,上锁!

lock 

然后.....

4c172206af19f1ae73ac0eccfa2cb624.png

只能读不能改,删,也不能增.

2.1.2 X锁

X锁也叫排他锁,写锁,一个事务对表加锁后,其他事务就不能对其进行加锁与增删查改操作.

设置手动提交,开启事务,上X锁.

set 

b500cad4c51128ff09f785d1112f9d2c.png

在开启另一个事务,使用select语句.

set 

3b561f88a869ddff70ab9e1e50f9d4e0.png

这里是阻塞select操作,因为一直都没释放X锁.

17d4dad7cfe7ca84767eda3a52493714.png

同样也不能再加锁,也是阻塞中.

a2b612342e04a5556d106fa56b7a3795.png

回到原来那个加锁的事务,嗯,什么事也没有,正常读写.

6bb36b7ef04095ba5b41920e5a7243b3.png

释放锁后:

unlock 

5c5780d6667b003c62aad6c9b8be1a78.png

d45ebc72a10e0beaf11e9bf473698bb5.png

在另一个事务中可以看到中断时间.

2.2 乐观锁与悲观锁

2.2.1 乐观锁

乐观锁就是总是假设是最好的情况,每次去操作的时候都不会上锁,但在更新时会判断有没有其他操作去更新这个数据,是一种宽松的加锁机制. mysql本身没有提供乐观锁的支持,需要自己来实现,常用的方法有版本控制和时间戳控制两种.

  • 版本控制:版本控制就是为表增加一个version字段,读取数据时连同这个version字段一起读出来,之后进行更新操作,版本号加1,再将提交的数据的版本号与数据库中的版本号进行比较,若提交的数据的版本号大于数据库中的版本号才会进行更新.
举个例子,假设此时version=1,A进行操作,更新数据后version=2,与此同时B也进行操作,更新数据后version=2,A先完成操作,率先将数据库中的version设置为2,此时B提交,B的version与数据库中的version一样,不接受B的提交.
  • 时间戳控制:时间戳控制与版本控制差不多,把version字段改为timestamp字段

还有一种实现方法叫CAS算法,这个笔者不怎么了解,有兴趣可以自行搜索.

2.2.2 悲观锁

悲观锁就是总是假设最坏的情况,在整个数据处理状态中数据处于锁定状态,悲观锁的实现往往依靠数据库的锁机制.每次在拿到数据前都会上锁. mysql在调用一些语句时会上悲观锁,如(先关闭自动提交,开启事务):

set 

33282574c2f2d0df60856e802e1f7a7f.png

两个事务都这样操作,然后其中一个事务输入:

select 

34fba194b0ad1203e44427a80d84b3df.png

在另一事务也这样输入:

f25d15924d119e31739e25e1ed4083ee.png

这时语句会被阻塞,直到上锁的那个事务commit(解开悲观锁).

b8b643d4c7469957cea152b7aa9dc197.png

e6aae541edd383f7831120f7cf740e65.png

在另一事务中可以看到这个事务被阻塞了2.81s.

*** 

也会加上悲观锁.

3 脏读,幻读,不可重复读与两类丢失更新

3.1 脏读

脏读是指一个事务读取到了另一事务未提交的数据,造成select前后数据不一致.

比如事务A修改了一些数据,但没有提交,此时事务B却读取了,这时事务B就形成了脏读,一般事务A的后续操作是回滚,事务B读取到了临时数值.

970251586ee803f50b2a5d3e660e2fde.png

3.2 幻读

幻读是指并不是指同一个事务执行两次相同的select语句得到的结果不同, 而是指select时不存在某记录,但准备插入时发现此记录已存在,无法插入,这就产生了幻读.

b7754c779c970c71e61a749beb490b06.png

3.3 不可重复读

不可重复读指一个事务读取到了另一事务已提交的数据,造成select前后数据不一致. 比如事务A修改了一些数据并且提交了,此时事务B却读取了,这时事务B就形成了不可重复读.

214af13a325d17d354943a68e1c4ae0d.png

3.4 第一类丢失更新

第一类丢失更新就是两个事务同时更新一个数据,一个事务更新完毕并提交后,另一个事务回滚,造成提交的更新丢失.

849d005b164875fb357cbe1e75b5ab88.png

3.5 第二类丢失更新

第二类丢失更新就是两个事务同时更新一个数据,先更新的事务提交的数据会被后更新的事务提交的数据覆盖,即先更新的事务提交的数据丢失.

b214311709c133d5e4e5adceb698893c.png

4 封锁协议与隔离级别

封锁协议就是在用X锁或S锁时制定的一些规则,比如锁的持续时间,锁的加锁时间等.不同的封锁协议对应不同的隔离级别.事务的隔离级别一共有4种,由低到高分别是

  • Read uncommitted
  • Read committed
  • Repeatable read
  • Serializable

分别对应的相应的封锁协议等级.

4.1 一级封锁协议

一级封锁协议对应的是Read uncommitted隔离级别,Read uncommitted,读未提交,一个事务可以读取另一个事务未提交的数据,这是最低的级别.一级封锁协议本质上是在事务修改数据之前加上X锁,直到事务结束后才释放,事务结束包括正常结束(commit)与非正常结束(rollback).

一级封锁协议不会造成更新丢失,但可能引发脏读,幻读,不可重复读. 设置手动提交与事务隔离等级为read uncommited,并开启事务(注意要先设置事务等级再开启事务).

set 

9288f93134cc109c400ed8c41e88b6eb.png

(中间有一行打多了一个t可以忽略.....)

4.1.1 引发脏读

在一个事务中修改表中的值,不提交,另一个事务可以select到未提交的值.

59f9c8d56b988e0f65d31c7ae4abfe3e.png

2b5929a5b6add03e4b1632441aa19e05.png

出现了脏读.

4.1.2 引发幻读

在一个事务中插入一条数据,提交.

d983313114c79ecf2ae588b0ac8bf871.png

另一事务中select时没有,准备insert,但是insert时却提示已经存在.引发幻读.

1fc1898536780a896088e7c582dcd623.png

4.1.3 引发不可重复读

未操作提交前:

57b520e4b30e441263195347236dc2eb.png

另一事务修改并提交:

b9f7468772b48e9a252e5e162b93cd49.png

再次读:

97a9d14afaa2e63b76536b15b94c48a5.png

引发不可重复读.

4.2 二级封锁协议

二级封锁协议本质上在一级协议的基础上(在修改数据时加X锁),在读数据时加上S锁,读完后立即释放S锁,可以避免脏读.但有可能出现不可重复读与幻读.

二级封锁协议对应的是Read committed与Repeatable Read隔离级别. 先设置隔离等级

set 

4.2.1 Read committed

Read committed,读提交,读提交可以避免脏读,但可能出现幻读与不可重复读.

4.2.1.1 避免脏读

开启一个事务并更新值,在这个事务中money=100(更新后)

da329f2160ac6f85a4cb860eb5945ac6.png

另一事务中money为未更新前的值,这就避免了脏读.

b2ca47950b26fa31b14b34b311243f69.png

注意,事实上脏读在read committed隔离级别下是不被允许的,但是mysql不会阻塞查询,而是返回未修改之前数据的备份,这种机制叫MVCC机制(多版本并发控制).

4.2.1.2 引发幻读

在一个事务中插入数据并提交.

4b7522109dba8569cd05f6ba7a9649d3.png

另一事务中不能插入"不存在"的数据,出现幻读.

1a6ada10a73c39fbf02ca524d31ada4f.png

4.2.1.3 引发不可重复读

事务修改并提交前:

8462231704ae68a6583a864e9c9319be.png

事务修改并提交:

553eaeb188b6c625dc17052df939ace1.png

出现不可重复读.

87a920309e4ae19d75449f6322a92141.png

4.2.2 Repeatable read

Repeatable read比Read committed严格一点,是Mysql的默认级别,读取过程更多地受到MVCC影响,可防止不可重复读与脏读,但仍有可能出现幻读.

4.2.2.1 避免脏读

在一个事务中修改数据,不提交.

37c57aa7692f0aa69efdae8a6cc9ee51.png

另一事务中两次select的结果都不变,没有出现脏读.

9ff4a1796781c5ee7e8cd612e9f16eb9.png

4.2.2.2 避免不可重复读

一个事务修改数据并提交.

8e0bb94b3769f47b7a8c56a06d9d9b73.png

另一事务中select的结果没有发生改变,即没有出现不可重复读.

4cc63c20cc42b898a9e37ab2a802f87b.png

4.2.2.3 引发幻读

同理,一个事务插入一条数据并提交.

c893f772eeea06bfa58d477fd8c852a4.png

另一个事务插入时出现幻读.

7d04470d2ac8eeb8687a6f04df35f1a4.png

4.3 三级封锁协议

三级封锁协议,在一级封锁协议的基础上(修改时加X锁),读数据时加上S锁(与二级类似),但是直到事务结束后才释放S锁,可以避免幻读,脏读与不可重复读.

三级封锁协议对应的隔离级别是Serializable. 先设置Serializable隔离级别

set 

4.3.1 避免脏读

设置事务隔离等级后开启事务并update,发现堵塞.从而避免了脏读.

abbfca8653c76584722f72ae62111145.png

4.3.2 避免幻读

插入时直接阻塞,避免了幻读.

dbb19ab7288fa57335e19002859c17ce.png

4.3.3 避免不可重复读

在脏读的例子中可以知道,update会被堵塞,都不能提交事务,因此也避免了不可重复读.

5 两段锁协议

事务必须分为两个阶段对数据进行加锁与解锁,两端锁协议叫2PL(不是2PC),所有的加锁都在解锁之前进行.

5.1 加锁

加锁会在更新或者

select 

时进行

5.2 解锁

解锁在事务结束时进行,事务结束包括rollback与commit.

参考链接 1:ACID1

2:ACID2

3:mysql的锁1

4:乐观锁与悲观锁1

5:乐观锁与悲观锁2

6:乐观锁与悲观锁3

7:mysql修改事务隔离等级

8:mysql三级封锁与二段锁

9:数据库封锁协议

10:mysql事务隔离机制1

11:mysql事务隔离机制2

12:mysql幻读

13:mysql脏读,不可重复读与幻读

14:mysql两段锁1

15:mysql两段锁2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
脏读是指一个事务可以读取到另一个事务未提交的数据。不可重复读是指在一个事务内多次读取同一数据时,由于其他事务的修改导致每次读取的结果不一致。幻读是指在一个事务内多次执行相同的查询,由于其他事务的插入或删除导致每次查询的结果不一致。 在MySQL中,事务隔离级别对应的脏读不可重复读幻读的情况如下: - 读未提交(READ UNCOMMITTED)级别下存在脏读不可重复读幻读的问题。 - 读已提交(READ COMMITTED)级别下不存在脏读的问题,但仍可能存在不可重复读幻读的问题。 - 可重复读(REPEATABLE READ)级别下不存在脏读不可重复读的问题,但仍可能存在幻读的问题。 - 串行化(SERIALIZABLE)级别下不存在脏读不可重复读幻读的问题。 因此,在MySQL中,脏读不可重复读幻读都是与事务隔离级别密切相关的读一致性问题。根据需求和业务场景,可以选择合适的事务隔离级别来解决这些问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Mysql-详解脏读不可重复读幻读](https://blog.csdn.net/ahuangqingfeng/article/details/124407846)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [一文搞懂MySQL脏读,幻读不可重复读](https://blog.csdn.net/liuqinhou/article/details/126360614)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值