事务隔离级别总结

时间:2022-07-24
本文章向大家介绍事务隔离级别总结,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

事务隔离级别总结

一. ACID特性

事务(Transaction)是数据库系统中一系列操作的一个逻辑单元,所有操作要么全部成功要么全部失

。 事务是区分文件存储系统与Nosql数据库重要特性之一,其存在的意义是为了保证即使在并发情况下也

能正确的执行CRUD操作。怎样才算是正确的呢?这时提出了事务需要保证的四个特性即ACID:

  1. A:原子性(Atomicity) 一个事务中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  2. C: 一致性(Consistency) 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的预设规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
  3. I:隔离性(Isolation) 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行 时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(read uncommitted)、读已提交(read committed)、可重复读(repeatable read)和串行化(serializable)。
  4. D:持久性(Durability) 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。

二. 事务隔离级别

在高并发的情况下,要完全保证其ACID特性是非常困难的,除非把所有的事务串行化执行,但带来的负 面的影响将是性能大打折扣。很多时候我们有些业务对事务的要求是不一样的,所以数据库中设计了四 种隔离级别,供用户基于业务进行选择。这四种隔离级别分别是:

  1. 读未提交(read uncommitted)
  2. 读已提交(read committed)
  3. 可重复读(repeatable read)
  4. 串行化(serializable)

其中,Oracle默认的级别是read committed,而MySQL默认为repeatable read。

注:MySQL中查看、设置事务隔离级别:

#查看事务隔离级别
SELECT @@tx_isolation;

#设置隔离级别为read-uncommitted
set tx_isolation='read-uncommitted';

三. 常见的数据库事务的问题

  1. 脏读(Dirty Read) 指一个事务读取到另一事务未提交的更新数据。
  2. 不可重复读(NonRepeatable Read) 指在同一事务中,多次读取同一数据返回的结果有所不同。也就是说,后续读取可以读到另一事务已提交的更 新数据。相反, 可重复读在同一事务中多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据。
  3. 幻读(Phantom Read) 指在同一个事务中,多次读取到的数据数量不一致。也就是说,后续读取可以读取到另一个事务删除或者新增的数据。 不可重复读与幻读在概念上容易混淆,其不同在于:不可重复读侧重的是另一个事务对数据的修改,是行级别的操作。而幻读侧重的是另一个事务对数据的新增或删除,是整个表级别的操作。

采用不同的事务隔离级别,可解决不同的问题,总结如下:

隔离级别

脏读

不可重复读

幻读

read uncommitted

不可解决

不可解决

不可解决

read committed

可解决

不可解决

不可解决

repeatable read

可解决

可解决

不可解决

serializable

可解决

可解决

可解决

可以看到,隔离级别越高,事务的安全性就越高,但是对性能的影响也越大,实际应用中应根据不同业务场景选择不同的隔离级别。

四. 事务隔离级别演示

  1. 准备 表结构:
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `money` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;
  1. 脏读 开启2个数据库session,session1执行
#设置为读未提交
set tx_isolation='read-uncommitted';
BEGIN;
insert INTO `account` (`name`,`money`) VALUES ('Kobe',100);
  1. 此时session1向数据库插入了一条数据,还未提交。 这时,在session2中读取数据:
#设置为读未提交
set tx_isolation='read-uncommitted';
SELECT * from `account`;
  1. 不可重复读 初始化数据:
insert INTO `account` (`name`,`money`) VALUES ('Kobe',100);
  1. 在session1中执行查询:
#设置为读已提交
set tx_isolation='read-committed';
BEGIN;
SELECT * from `account`; 
# 其他操作

此时查询到表中的数据是{1,Kobe,100}。

这时session2更新了表中的数据

#设置为读已提交
set tx_isolation='read-committed';
UPDATE `account` SET money= money+100 where `id`=1;
  1. 这时再在session1的当前事务中执行查询:
SELECT * from `account`; 
  1. 幻读 初始化数据:
insert INTO `account` (`name`,`money`) VALUES ('Kobe',); 
#设置为可重复读
set tx_isolation='REPEATABLE-READ';
BEGIN;
SELECT * FROM `account`;
#设置为可重复读
set tx_isolation='REPEATABLE-READ';
insert INTO `account` (`name`,`money`) VALUES ('James',100);