数据库事务隔离级别

前几天项目上合作公司的系统出现了一次死锁,突然想到由于近几年开发设计的系统并发用户比较少,很久没有碰到过死锁了,因此对死锁的概念也比较生疏了,需要温习一下。

事务

先从最基本的概念开始,事务、及其ACID特性。

事务的概念就用一句话概括:事务中的SQL语句要么全部执行成功,要么全部执行失败。

Atomicity:原子性,一个事务必须被视为不可分割的工作单元,整个事务中的所有操作要么全部提交成功,要么全部失败回滚。对于一个事务来说,不能只执行其中的一部分操作,这就是事务的原子性。

Consistency:一致性,事务可以确保数据总是从一个一致性状态转换到另一个一致性状态。

Isolation:隔离性,事务隔离性的目的是要确保一个事务所做的修改在最终提交之前,对其他事务是不可见的。这一点其实是很难做到的,所以才有了iso level,隔离级别的概念。

Durability:持久性,一旦事务提交,修改就会永久保存在数据库中。

隔离级别

SQL标准定义了四种隔离级别:
READ UNCOMMITTED(未提交读)
事务中的修改即使没有提交,其他事务中也是能看见的。事务能够读取未提交数据也被称之为“脏读”(dirty read)。这个级别会导致很多问题,从性能上来说,READ UNCOMMITTED不对比其他级别好太多。但却缺乏其他级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用。

*** READ COMMITTED(提交读) ***
大部分数据库(SQL Server/Oracle等)默认的隔离级别,但Mysql不是。一个事务开始后,只能看到其他事务已经提交的修改。READ COMMITTED避免了脏读,但是READ COMMITTED不可重复读(nonrepeatable read),因为在一个事务中两次执行同样的查询,可能会读到不一样的结果。

REPEATABLE READ(可重复读)
可重复读确保了在一个事务中多次读取同样记录的结果是一致的。但是在理论上REPEATABLE READ还是无法解决另外一个幻读(phantom read)的问题。幻读指的是某个事务在读取某个范围内的记录时,另一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围内的数据时,会产生幻行(phantom row)。InnoDB和XtraDB存储引擎通过引入MVCC解决了幻读问题。

可重复读是MySql默认的隔离级别。

SERIALIZABLE(串行化)
最高隔离级别,通过强制事务串行执行避免幻读问题。简单来说RERIALIZABLE会在读取的每一行数据上加锁,所以可能会导致大量的超时和锁争夺问题,实际应用中很少用这个隔离级别。

在这里插入图片描述

死锁

死锁指的是两个或多个事务在统一资源上的相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

当多个事务试图以不同顺序锁定资源时,就可能产生死锁,多个事务同时锁定同一个资源时,也可能产生死锁。

比如,事务一:

start transaction;
update stock set price=1.2 where id=3;
update stock set price=1.5 where id=4;
commit;

事务二:

start transaction;
update stock set price=1.2 where id=4;
update stock set price=1.5 where id=3;
commit;

如果事务一执行了第一条update语句、锁定了第一条数据、事务二也执行了第一条update语句、锁定了第一条数据,此时两个事务准备执行第二条语句是发现已经被对方锁定,彼此等待对方释放锁资源、同时又持有对方等待的锁资源,这样就陷入了死循环,造成死锁。

死锁发生后必须有第三方介入才可能解除。比如大部分的数据库都设置了等待的timeout时长,超过该时长后事务会主动放弃等待(InnoDB引擎将持有最少行级排它锁的事务回滚)、rollback事务。

死锁是事务性数据库系统中不可避免的现象,我们可以认为死锁其实是数据一致性的一种保护性措施,但是我们在设计应用的时候,还是要在充分理解死锁底层逻辑的前提下,尽可能避免死锁的发生。

MVCC

MVCC是multi-version concurrency control的缩写,意思是多版本并发控制。多版本并发控制是尽可能避免加锁操作而实现数据库的ACID,因此开销更低。

MVCC是通过保存数据在某一个时间点的快照来实现的,也就是说,不管执行多长时间,每个事务看到的数据是一致的。根据事务开始时间的不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。

MVCC没有统一的实现标准,不同存储引擎的MVCC实现是不同的。

InnoDB的MVCC实现的简化版行为:

InnoDB的每行记录后面保存两个隐藏的列,一个保存行的创建时间、一个保存行的删除时间。但是实际记录的并不是时间,而是版本号。

没开始一个新的事务,版本号会自动递增,事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行版本号作比较。

在REPEATABLE READ隔离级别下:

SELECT操作,InnoDB会以下两个条件检查每行记录:

  1. 只查找版本早于当前事务版本号的数据行(也就是,行的系统版本号小于等于事务的系统版本号),这样可以确保事务读取的数据行,要么是事务开始前已经存在的,要么是当前事务插入或修改过的。
  2. 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读到到的行,在事务开始之前未删除。

INSERT操作:
为插入的每一行保存当前事务的系统版本号作为行版本号。
DELETE操作:
为删除的每一行保存当前事务的系统版本号作为行的删除标识。
UPDATE操作:
插入一条新纪录,保存当前事务版本号作为行版本号,同时保存当前事务版本号到原有行作为删除标识。

MVCC机制可以确保在大多数情况下的读操作都不需要加锁,使得读数据操作很简单、性能更好,并且能确保读取到符合标准的数据。不足之处是每行记录都需要额外的空间,并且要做更多的检查工作,以及一些额外的维护工作。

MVCC只在REPEATABLE READ和READ COMMITTED 两个隔离级别下工作。其他两个隔离级别都和MVCC不兼容。

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值