事务的四大隔离级别的实验

数据库实验之事务的四大级别操作

背景介绍:

一、事务的四大特性(ACID)

了解事务隔离级别之前不得不了解的事务的四大特性。

1、原子性(Atomicity)

事务开始后所有操作,要么全部做完,要么全部不做。事务是一个不可分割的整体。事务在执行过程中出错,会回滚到事务开始之前的状态,以此来保证事务的完整性。类似于原子在物理上的解释:指化学反应不可再分的基本微粒,原子在化学反应中不可分割 。

2、一致性(Consistency)

事务在开始和结束后,能保证数据库完整性约束的正确性即数据的完整性。比如经典的转账案例,A向B转账,我们必须保证A扣了钱,B一定能收到钱。个人理解类似于物理上的能量守恒。

3、隔离性(Isolation)

事务之间的完全隔离。比如A向一张银行卡转账,避免在同一时间过多的操作导致账户金额的缺损,所以在A转入结束之前是不允许其他针对此卡的操作的。

4、持久性(Durability)

事务的对数据的影响是永久性的。通俗的解释为事务完成后,对数据的操作都要进行落盘(持久化)。事务一旦完成就是不可逆的,在数据库的操作上表现为事务一旦完成就是无法回滚的。

在现代互联网中,为了满足大量用户同时操作数据的需求,类似于双十一淘宝,现代的数据库普遍都有着并发机制,但是任何事物得到一样性能必须会牺牲另一项性能,为了实现并发性,也往往造就了许多的问题,这一点无论在数据库,亦或是操作系统中都有体现。

Q: 发生的问题:

1、脏读

又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。

2:可重复读

同一个事务中,多次读出的同一数据是不一致的。

3:脏读是指当事务不是独立执行时发生的一种现象。

事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。

A:如何解决这个问题呢?

为此mysql 的innodb 引擎中,引入了不同的事务隔离机制,用来解决这些问题。

image-20201025101808075

进行实验:

1:建表:

CREATE TABLE accout(
id INT NOT NULL,
personname VARCHAR (255) NOT NULL,
balance INT NOT NULL,
PRIMARY KEY (id)
)     

2: 插入数据:

INSERT INTO accout 
VALUES(1,"alice" ,1000),
			(2, "bob", 900),
			(3, "jack", 800),
			(4, "Amy" , 700),
			(5,"Andy", 600)

此时的表:

set session transaction isolation level read ;
START TRANSACTION;
UPDATE accout SET balance = balance + 100; image-20201025102955906

设置隔离等级读未提交

 set session transaction isolation level read uncommitted;

开启两个事务: session 1 和session 2

其中 : session 2 :

set session transaction isolation level readSTART TRANSACTION;
UPDATE accout SET balance = balance + 100;

执行 ,同时 在session 1 中查询结果为:

select * from accout;

结果:

image-20201025103938026

由于某种原因 session 2 回滚,同时 session1 进行余额减100 的操作

ROLLBACK;
COMMIT;
UPDATE accout set balance = balance -100;
commit;
selet * from accout;

结果:

image-20201025105123520

我们会发现,莫名其妙的多了100 元, 为啥 ?

这是因为,在 session1开始加钱100 元时, session1觉得查询结果就是正确的数据。

我们发现因为session1的脏读造成了最终数据不一致。正确的结果应该为Andy 500;
到此我们怎么避免脏读呢,将事务的隔离性增加一个级别到read-commit

read - commit 解决脏读

设置 事务隔离级别 ,

 set session transaction isolation level read committed;

重复上述操作:

session 2

image-20201025105809989

session1 中查询到的数据:

image-20201025105701481

我们发现将 事务的隔离升级为read-committed;后有效的隔离了两个事务,使得session1中的事务无法查询到session2中事务对数据的改动。有效的避免了脏读

接着操作:

结果符合image-20201025110210365

read-commit的不可重复读

重置数据,使数据恢复到初始状态

开启事务:

start transaction;

session1 首次查询:

image-20201025111336800

session 2 进行更新,

 set session transaction isolation level read committed;
 START TRANSACTION;

UPDATE accout SET balance = balance - 100;
SELECT * FROM accout;


session 1 再次查询,发现一致,原因是session2没有commit,

image-20201025111336800

但是当commit 后,再次查询结果如下

image-20201025123923495

查看查询结果可知,session1在开启事务期间发生重复读结果不一致

产生了不可重复读。

repeatable-read可重复读

恢复数据 , 设置隔离级别为 repeatable-read


set session transaction isolation level repeatable read;

重复上述操作 ,session1 的第二次查询为

image-20201025124518022

看起来并未改变 , 让我们探索一下这是如何保证两个这两个事务的互不影响。

我们在session1 中也进行

UPDATE accout SET balance = balance +100;

发现无法进行,原来是锁机制,只有session2 commit后 ,session1 才能运行

image-20201025125446817

-repeatable-read的幻读

1 :两事务都运行:

 set session transaction isolation level repeatable read;
 start transaction;
 select * from  accout;

产生快照

image-20201025125846522

2 :session 2

INSERT INTO accout values(6,"wen" , 50);
commit;

image-20201025130056834

3:::在session 1 中 进行查询,发现结果为

image-20201025130223394

此时按照我们通常的业务逻辑,此时应该是能成功插入id=6的数据

INSERT INTO accout values(6,"wen" , 50);

image-20201025130414386

,结果插入失败,提示该条已经存在,但是我们查询里面并没有这一条数据啊。为什么会插入失败呢?

因为①中的select语句生成了快照,之后的读操作(未加读锁)都是进行的快照读,即在当前事务结束前,所有的读操作的结果都是第一次快照读产生的快照版本。疑问又来了,为什么②步骤中的select语句读到的不是快照版本呢?因为update语句会更新当前事务的快照版本

那么该如何解决呢?

采用当前读的方式

 select * from tb_bank lock in share mode;//采用当前读

image-20201025130821636

这样就解决了快照问题。

可串行化:

使用了SERIALIZABLE——可串行化隔离级别时,在这个事务没有被提交之前
其他的线程,只能等到当前操作完成之后,才能进行操作,这样会非常耗时,而且,影响数据库的性能,通常情况下,不会使用这种隔离级别

牺牲了性能 ,是的线程只能被当前执行完才能进行下一个问题。

猜你喜欢

转载自blog.csdn.net/weixin_43843172/article/details/109276577
今日推荐