数据库系统原理
一、事务
什么是事务呢?事务是具有ACID特性的一组操作,可以通过commit进行提交事务,也可以通过rollback对事务进行回滚。
ACID
1. 原子性(Atomicity)
整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
2. 一致性(Consistency)
一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。
3. 隔离性(Isolation)
隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,必须串行化或序列化请求,使得在同一时间仅有一个请求用于同一数据。
4. 持久性(Durability)
一旦事务提交,则其所做的修改将会永远保存到数据库中。即使系统发生崩溃,事务执行的结果也不能丢失。
使用重做日志来保证持久性。
事务的 ACID 特性概念简单,但不是很好理解,主要是因为这几个特性不是一种平级关系:
-
只有满足一致性,事务的执行结果才是正确的。
-
在无并发的情况下,事务串行执行,隔离性一定能够满足。此时只要能满足原子性,就一定能满足一致性。
-
在并发的情况下,多个事务并行执行,事务不仅要满足原子性,还需要满足隔离性,才能满足一致性。
-
事务满足持久化是为了能应对数据库崩溃的情况。
AUTOCOMMIT
MySQL 默认采用自动提交模式。也就是说,如果不显式使用 START TRANSACTION 语句来开始一个事务,那么每个
查询都会被当做一个事务自动提交。
二、并发一致性问题
在并发环境下,由于事务的隔离性很难保证,所以会产生并发一致性问题。
并发一致性问题主要有以下四种
- 丢失修改
- 数据脏读
- 幻影读
- 不可重复读
我们假设目前有两个事务,Transaction1和Transaction2,简称T1、T2 它们目前处于并发执行状态,由于隔离性不能保证的情况下,出现了以上四种情况。
首先是丢失修改,T1 和 T2 两个事务都对一个数据进行修改,T1 先修改,T2 随后修改,T2 的修改覆盖了 T1 的修改。其次是数据脏读,T1对数据进行修改的同时
T2对数据进行了读取,但是T1最后进行了回滚操作,但是T2读取了T1修改后的数据造成了脏读数据。然后就是幻影读,T1在对一个数据范围进行读取的时候,T2
对范围新增了数据,导致T1的读取结果与预期不一致。最后是不可重复读,T1事务对数据有两次读取操作,但在T1事务执行期间,T2事务对数据做了修改,导致T1
事务的第二次读取事务的结果和第一次不一致。
产生并发不一致性问题主要原因是破坏了事务的隔离性,解决方法是通过并发控制来保证隔离性。并发控制可以通过
封锁来实现,但是封锁操作需要用户自己控制,相当复杂。数据库管理系统提供了事务的隔离级别,让用户以一种更
轻松的方式处理并发一致性问题。
三、封锁
封锁粒度
MySQL 中提供了两种封锁粒度:行级锁以及表级锁。
应该尽量只锁定需要修改的那部分数据,而不是所有的资源。锁定的数据量越少,发生锁争用的可能就越小,系统的
并发程度就越高。
但是加锁需要消耗资源,锁的各种操作(包括获取锁、释放锁、以及检查锁状态)都会增加系统开销。因此封锁粒度
越小,系统开销就越大。
在选择封锁粒度时,需要在锁开销和并发程度之间做一个权衡。
封锁类型
- 读写锁
- 排它锁 简写为 X 锁,又称写锁
- 共享锁 简写为 S 锁,又称读锁。
2. 意向锁
使用意向锁(Intention Locks)可以更容易地支持多粒度封锁。
在存在行级锁和表级锁的情况下,事务 T 想要对表 A 加 X 锁,就需要先检测是否有其它事务对表 A 或者表 A 中的任
意一行加了锁,那么就需要对表 A 的每一行都检测一次,这是非常耗时的。
意向锁在原来的 X/S 锁之上引入了 IX/IS,IX/IS 都是表锁,用来表示一个事务想要在表中的某个数据行上加 X 锁或 S
锁。有以下两个规定:
一个事务在获得某个数据行对象的 S 锁之前,必须先获得表的 IS 锁或者更强的锁;
一个事务在获得某个数据行对象的 X 锁之前,必须先获得表的 IX 锁。
通过引入意向锁,事务 T 想要对表 A 加 X 锁,只需要先检测是否有其它事务对表 A 加了 X/IX/S/IS 锁,如果加了就表
示有其它事务正在使用这个表或者表中某一行的锁,因此事务 T 加 X 锁失败。
总结:
首先我们知道了事务的概念:是一组具有ACID特性的操作,由于在并发环境下,事务会出现同时处理操作的现象,导致了事务的隔离性得不到保证,隔离性得不到保证就会使的
事务的一致性出错,从而导致并发一致性的一系列问题:脏读、幻读、不可重复读、丢失修改等。而解决的办法就是进行封锁,封锁有封锁粒度和封锁类型两个概念,封锁粒度有
行级锁和表级锁,封锁类型有 读写锁、意向锁,读写锁又包括了排它锁(写锁)、共享锁(读锁),最后就是对数据库表进行加锁操作是兼容性的问题。