一文带你理解脏读,幻读,不可重复读与mysql的锁,事务隔离机制

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

1 ACID

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

(1) A:原子性(Atomicity)

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

(2) C:一致性(Consistency)

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

(3) I:隔离性(Isolation)

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

(4) D:持久性(Durability)

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


2 Mysql的锁

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

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

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

(1) S锁与X锁

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

show variables like ‘%storage_engine%‘;

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

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

create database test;
use test;
create table a
(
id int primary key auto_increment,
money int
);

Ⅰ.S锁

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

lock table a read;

然后.....

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

Ⅱ.X锁

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

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

set autocmmmit=0;
start transaction;
lock table a write;


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

set autocommit=0;
start transaction;
select * from a;


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

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

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

释放锁后:

unlock table;



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


(2) 乐观锁与悲观锁

Ⅰ.乐观锁

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

  • 版本控制
    版本控制就是为表增加一个version字段,读取数据时连同这个version字段一起读出来,之后进行更新操作,版本号加1,再将提交的数据的版本号与数据库中的版本号进行比较,若提交的数据的版本号大于数据库中的版本号才会进行更新.

    举个例子,假设此时version=1,A进行操作,更新数据后version=2,与此同时B也进行操作,更新数据后version=2,A先完成操作,率先将数据库中的version设置为2,此时B提交,B的version与数据库中的version一样,不接受B的提交.

  • 时间戳控制
    时间戳控制与版本控制差不多,把version字段改为timestamp字段

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

Ⅱ.悲观锁

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

set autocommit=0;
start transaction;


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

select * from a where xxx for update;


在另一事务也这样输入:

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


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

*** lock in share mode.

也会加上悲观锁.


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

(1) 脏读

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

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

事务A 事务B
开始事务 开始事务
更新X,旧值X=1,新值X=2  
 ? 读取X,X=2(脏读)
回滚X=1  
结束事务(X=1) 结束事务

(2) 幻读

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

事务A 事务B
开始事务 开始事务
select某个数据为空,准备插入一个新数据  
 ? 插入一个新数据
 ? 提交,结束事务
插入数据,发现插入失败,由于事务B已插入相同数据
结束事务  

(3) 不可重复读

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

事务A 事务B
开始事务 开始事务
读取X=1 读取X=1
更新X=2  
提交,结束事务  
 ? 读取X=2
 ? 结束事务

(4) 第一类丢失更新

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

事务A 事务B
开始事务 开始事务
读取X=1 读取X=1
修改X=2 修改X=3
 ? 提交,结束事务
回滚  
结束事务(X=1) X=1,X本应为提交的3

(5) 第二类丢失更新

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

事务A 事务B
开始事务 开始事务
读取X=1 读取X=1
更新X=2  
提交事务,X=2,结束  
 ? 更新X=3
 ? 提交事务,X=3,事务A的更新丢失,结束

5 封锁协议与隔离级别

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

(1) 一级封锁协议

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

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

set autocommit=0;
set session transaction isolation level read uncommitted;
start transaction;


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

a.引发脏读

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


出现了脏读.

b.引发幻读

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

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

c.引发不可重复读

未操作提交前:

另一事务修改并提交:

再次读:

引发不可重复读.

(2) 二级封锁协议

二级封锁协议本质上在一级协议的基础上(在修改数据时加X锁),在读数据时加上S锁,读完后立即释放S锁,可以避免脏读.但有可能出现不可重复读与幻读.二级封锁协议对应的是Read committed与Repeatable Read隔离级别.
先设置隔离等级

set session transaction isolation level read committed;

Ⅰ.Read committed

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

a.避免脏读

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

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

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

b.引发幻读

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

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

c.引发不可重复读

事务修改并提交前:

事务修改并提交:

出现不可重复读.

Ⅱ.Repeatable read

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

a.避免脏读

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

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

b.避免不可重复读

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

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

c.引发幻读

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

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

(3) 三级封锁协议

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

set session transaction isolation level serializable

a.避免脏读

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

b.避免幻读

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

c.避免不可重复读

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


6 两段锁协议

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

(1) 加锁

加锁会在更新或者

select *** for update
*** lock in share mode

时进行

(2) 解锁

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


7.最后

这是作者的CSDN地址
最后,以下是作者的公众号,一起学习,一起成长.

参考链接
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

原文地址:https://blog.51cto.com/14415843/2443420

时间: 2024-10-24 20:06:23

一文带你理解脏读,幻读,不可重复读与mysql的锁,事务隔离机制的相关文章

脏读 幻读 不可重复读

转自:http://www.blogjava.net/hitlang/archive/2009/04/13/265256.html 1, 脏读 一个事务读到另一个事务,尚未提交的修改,就是脏读.这里所谓的修改,除了Update操作,不要忘了,还包括Insert和Delete操作. 脏读的后果:如果后一个事务回滚,那么它所做的修改,统统都会被撤销.前一个事务读到的数据,就是垃圾数据. 举个例子:预订房间.有一张Reservation表,往表中插入一条记录,来订购一个房间. 事务1:在Reserva

理解MySql的锁&事务隔离级别

这几篇文章是从网上(http://www.hollischuang.com)看到的一系列文章,也是重温了一下数据库的相关知识.下面是对这些文章的一些前后行文逻辑的说明: 我们知道,在DBMS的多个事业并发执行时,存在着脏读.不可重复读.幻读等情况. 为了解决这些问题,DBMS产品都会通过锁来实现数据库隔离级别从而解决上面的问题. 数据库的读现象浅析 :分析了脏读 & 不可重复读 & 幻读等情况. 数据库的锁机制 : 分析了常见的锁机制,划分了常见的锁分类. MySQL中的行级锁,表级锁,页

SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中的事务概念,ACID 原则,事务中常见的问题,问题造成的原因和事务隔离级别等这些方面的知识好好的整理了一下. 其实有关 SQL Server 中的事务,说实话因为内容太多, 话题太广,稍微力度控制不好就超过了我目前知识能力范围,就不是三言两语能够讲清楚的.所以希望大家能够指出其中总结的不足之处,对我

理解MySql事务隔离机制、锁以及各种锁协议

一直以来对数据库的事务隔离机制的理解总是停留在表面,其内容也是看一遍忘一边.这两天决定从原理上理解它,整理成自己的知识.查阅资料的过程中发现好多零碎的概念如果串起来足够写一本书,所以在这里给自己梳理一个脉络,具体的内容参考引文或在网上搜一下.由于平时接触最多的是MySQL,所以文章中某些部分是MySQL特有的特性,请读者注意. 数据库并发操作会引发的问题: 多个事务同时访问数据库时候,会发生下列5类问题,包括3类数据读问题(脏读,不可重复读,幻读),2类数据更新问题(第一类丢失更新,第二类丢失更

30秒读懂mysql四种事务隔离级别

一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有发生一样.也就是说事务是一个不可分割的整体,就像化学中学过的原子,是物质构成的基本单位.  2.一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 .比如A向B转账,不可能A扣了钱,B却没收到. 3.隔离性(Isolation):同一时间,只允许一个事务请求同一数据,

数据库隔离 脏读 幻读与不可重复读

一.隔离级别与现象 数据库隔离有4个级别 read uncomitted ,未授权读取 read committed ,授权读取 repeatable read , 可重复读 serializable ,串行化 可能产生的不一致的现象有3种 脏读 不可重复读 幻读 二.3种现象 脏读,就是读了脏数据:另一个事务修改了数据但未提交,我们读到了这个未提交的数据即脏数据,这种现象就是脏读: 举个栗子,balance字段值为100,另一个事务修改为200,未提交的时候我们读到了200,接着那个事务回滚了

数据库事务处理差异:可提交读和重复读区别

本文来自于自己的博客:http://www.wangerbao.com/?p=279 Mysql默认事物是重复读(Repeatable Read Isolation Level), PostGres默认事物是提交读(Read Committed Isolation Level),现在根据实例来对比下它们的差别. Mysql: 左边为事物1,右边为事物2. 第一步: 事物1查询,事物2查询,它们的查询结果一致.  第二步: 事物1更新-查询-但是不提交,事物2查询,他们的结果不一致 第三步: 事物

[MySQL]对于事务并发处理带来的问题,脏读、不可重复读、幻读的理解与数据库事务隔离级别 - 分析脏读 & 不可重复读 & 幻读

刚开始写博客.. 写的太low. 1.数据库的两种读,每种读读的数据版本不一样,所以也称为MVCC,即多版本并发控制 a) 快照读 select * from where xxx  这种形式的都是快照读. b) 当前读 update , insert ,delete ,select xx from xx for update ,  in share mode 都是当前读 当前读会等待,不会返回数据的历史版本 2.mvcc 的实现原理 mvcc是基于read view.活跃事务列表 做的,以后的文

事务的四大特性&脏读、幻读、不可重复读的区别&事务的隔离级别

一.什么是事务 事务:是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作:这些操作作为一个整体一起向系统提交,要么都执行.要么都不执行:事务是一组不可再分割的操作集合(工作逻辑单元): 事务的四大特性: 1 .原子性 (atomicity):强调事务的不可分割. 事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做 2 .一致性 (consistency):事务的执行的前后数据的完整性保持一致. 事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态.因此