欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-03-09 10:36:12
...

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

背景介绍:

一、事务的四大特性(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 引擎中,引入了不同的事务隔离机制,用来解决这些问题。

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

进行实验:

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; 事务的四大隔离级别的实验

设置隔离等级读未提交

 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;

结果:

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

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

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

结果:

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

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

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

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

read - commit 解决脏读

设置 事务隔离级别 ,

 set session transaction isolation level read committed;

重复上述操作:

session 2

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

session1 中查询到的数据:

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

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

接着操作:

结果符合事务的四大隔离级别的实验

read-commit的不可重复读

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

开启事务:

start transaction;

session1 首次查询:

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

session 2 进行更新,

 set session transaction isolation level read committed;
 START TRANSACTION;

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


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

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

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

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

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

产生了不可重复读。

repeatable-read可重复读

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


set session transaction isolation level repeatable read;

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

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

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

我们在session1 中也进行

UPDATE accout SET balance = balance +100;

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

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

-repeatable-read的幻读

1 :两事务都运行:

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

产生快照

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

2 :session 2

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

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

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

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

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

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

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

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

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

那么该如何解决呢?

采用当前读的方式

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

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

这样就解决了快照问题。

可串行化:

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

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

相关标签: mysql 数据库