数据库事务隔离级别 数据库事务的四大特性(简称ACID) 以及数据库事务的三大问题

首先讲概念 数据库隔离级别 四大特性或者脏读重复读幻读 这些名词 都是简称,都需要加上事务两个字。这个事务跟写代码的那个事务是同一个东西。

如果没有数据库事务的概念。会出现很多问题。那究竟啥事数据库事务

数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。
一个数据库事务通常包含了一个序列的对数据库的读/写操作。它的存在包含有以下两个目的:
a.为数据库操作序列提供了一个从失败中恢复到正常状态的方法,同时提供了数据库即使在异常状态下仍能保持一致性的方法。
b.当多个应用程序在并发访问数据库时,可以在这些应用程序之间提供一个隔离方法,以防止彼此的操作互相干扰。

有点绕。大白话就是1要保证要么一起成功。要么一起失败 2.也就是保证数据读写一致性问题,比如你读的数据是改过的还是没改过的。有点类似java并发里头共享变量的数据一致性问题。

数据库事务的四大特性

原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行。
一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束。
隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行。
持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中。

说实话这四个单词概念总感觉不是那么重要。因为我觉得吧。这四个特性改成四个问题才比较好。就是数据库操作会出现以上四个问题。 原子性问题。举例就是 插入两个表。第一个表失败。第二个表插入成功了。 那这个操作从整体上看就不是原子操作。

一致性问题。举例就是说两个操作读同一条记录 你读的是100。 我读的是200.数据出现了不一致的情况。

隔离性问题。这里涉及到隔离级别,简单举例就是你修改了一条记录,我也修改一条记录,我的修改会影响到你的修改,假如你要修改的条件被我的修改改变的情况。这就出现了隔离性问题了。

持久性问题。这个应该说的是事务未提交跟提交的区别。未提交的事务还有可能回滚。这个一旦回滚。上面说的三种问题就更加突出。下面说具体的三大问题。

脏读,不可重复读,幻读究竟说的啥

脏读的概念
A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据。就好像原本的数据比较干净、纯粹,此时由于B事务更改了它,这个数据变得不再纯粹。这个时候A事务立即读取了这个脏数据,但事务B良心发现,又用回滚把数据恢复成原来干净、纯粹的样子,而事务A却什么都不知道,最终结果就是事务A读取了此次的脏数据,称为脏读。

不可重复读的概念
事务A在执行读取操作,由整个事务A比较大,前后读取同一条数据需要经历很长的时间 。而在事务A第一次读取数据,比如此时读取了小明的年龄为20岁,事务B执行更改操作,将小明的年龄更改为30岁,此时事务A第二次读取到小明的年龄时,发现其年龄是30岁,和之前的数据不一样了,也就是数据不重复了,系统不可以读取到重复的数据,成为不可重复读。

幻读的概念跟不可重复读有点类似。也是两个不同的事务并行。事务A第一次读取到的数据跟第二次读取的数据由于事务B的提交发送了改变。但是这里的区别是 记录条数的改变。 比如第一次读取到了1条。由于B的删除。第二次读取不到了。注重的是记录数的变化。

针对这三种情况。数据库给出了解决方案。就是定义隔离级别。先说四种隔离级别

Read Uncommited 也就是等于没有隔离的状态,Read Uncommitted最直接的效果就是一个事务可以读取另一个事务并未提交的更新结果。这种脏读。不可重复读,幻读都会出现

2.READ COMMITTED:读已提交,只读提交的数据,也就是脏读没了

3.Repeatable Read. Repeatable Read隔离级别可以保证在整个事务的过程中,对同一笔数据的读取结果是相同的,不管其他事务是否同时在对同一笔数据进行更新,也不管其他事务对同一笔数据的更新提交与否。 Repeatable Read隔离级别避免了脏读和不可重复读取的问题,但无法避免幻读。(mysql默认隔离级别)

4.SERIALIZABLE: 串行化,加表锁,全部串行,无所有问题。

了解了四种级别。再来看数据库如何保证这些级别。MVCC,英文全称为Multi-Version Concurrency Control,翻译为中文即 多版本并发控制。看到这里你估计就能猜出了。对。每次事务开启。分配一个版本号。另外在每一行数据中额外保存两个隐藏的列:当前行创建时的版本号和删除时的版本号(可能为空,其实还有一列称为回滚指针,用于事务回滚,不在本文范畴)。这里的版本号并不是实际的时间值,而是系统版本号。每开始新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询每行记录的版本号进行比较。

这里具体不讲实现了。插入就是创建版本号+1 删除就是删除版本号+1 ,修改就是先标记删除版本号加一。然后删除。最后插入新的记录。 查询的时候

  1. 删除版本号未指定或者大于当前事务版本号,即查询事务开启后确保读取的行未被删除。(即上述事务id为2的事务查询时,依然能读取到事务id为3所删除的数据行)

  2. 创建版本号 小于或者等于 当前事务版本号 ,就是说记录创建是在当前事务中(等于的情况)或者在当前事务启动之前的其他事物进行的insert。

这里感觉好像已经搞定了脏读跟不可重读问题。但是感觉是解决不了幻读的。具体没有去假定了。但是网上说mysql的innodb又通过一个gap lock 组合mvcc 解决了幻读问题。这里不做假定了。直接复制原理。

MySQL InnoDB支持三种行锁定方式:

l 行锁(Record Lock):锁直接加在索引记录上面,锁住的是key。

l 间隙锁(Gap Lock):锁定索引记录间隙,确保索引记录的间隙不变。间隙锁是针对事务隔离级别为可重复读或以上级别而已的。

l Next-Key Lock :行锁和间隙锁组合起来就叫Next-Key Lock。

默认情况下,InnoDB工作在可重复读隔离级别下,并且会以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。Next-Key Lock是行锁和间隙锁的组合,当InnoDB扫描索引记录的时候,会首先对索引记录加上行锁(Record Lock),再对索引记录两边的间隙加上间隙锁(Gap Lock)。加上间隙锁之后,其他事务就不能在这个间隙修改或者插入记录。

2、 间隙锁(Gap Lock)

例如:

create table test(id int,v1 int,v2 int,primary key(id),key idx_v1(v1))Engine=InnoDB DEFAULT CHARSET=UTF8;

该表的记录如下:

±—±-----±-----+

| id | v1 | v2 |

±—±-----±-----+

| 1 | 1 | 0 |

| 2 | 3 | 1 |

| 3 | 4 | 2 |

| 5 | 5 | 3 |

| 7 | 7 | 4 |

| 10 | 9 | 5 |

间隙锁(Gap Lock)一般是针对非唯一索引而言的,test表中的v1(非唯一索引)字段值可以划分的区间为:

(-∞,1)

(1,3)

(3,4)

(4,5)

(5,7)

(7,9)

(9, +∞)

假如要更新v1=7的数据行,那么此时会在索引idx_v1对应的值,也就是v1的值上加间隙锁,锁定的区间是(5,7)和(7,9)。同时找到v1=7的数据行的主键索引和非唯一索引,对key加上锁。

我的总结就是一旦开始事务造作。记录key周围的区间也都是被锁定的。也就解决了增删改的问题。也就解决了幻读。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值