事务部分
事务具有ACID特性
A: 原子性,要求事务内操作是单一的、完整的
一个操作要么完整反映、要么不反映
C:一致性,要求数据在事务前的状态与事务后的状态是一致的
操作的数据不能凭空发生变化
I:隔离性,要求两个事务之间是不存在互相感知的
一个事务不应该和同时进行的事务发生联动
D: 持久性,要求事务对数据库操作是永久的
事务完成后,任何故障都不能丢失数据
下面是不同例子:
- 原子性违反例子:如果一个银行转账操作被分成两部分:一部分是从A账户扣款,另一部分是向B账户加款。假设扣款成功,但在加款时发生了故障,导致只完成了一半操作,这就违反了原子性。
- 一致性违反例子:考虑一个简单的帐户余额更新事务,如果事务途中更新了余额,但未能正确更新余额总和或其他相关的完整性约束,导致数据库处于不一致状态。
- 隔离性违反例子:如果两个事务同时操作同一个银行账户,一个事务正在将金额从账户A转移到账户B,同时另一个事务正在读取账户A的余额,可能会读取到中间状态的数据,导致不一致的结果。
- 持久性违反例子:事务完成后,如果系统故障,使得刚才事务的修改未能写入磁盘持久保存,而是丢失了,这违反了持久性。
事务执行
单个事务执行一般没有问题,但是多个事务执行执行具有很多问题。
采用并发执行提高效率,但是多个事务之间犬牙交错十分容易出现数据不一致性问题,所以需要串行化调度。由于串行化造成事务执行顺序不固定,可能会造成N个事务N!个结果的情况,我们需要实现可串行化。
数据不一致性的常见问题:丢失更新,脏读,不可重复读,幻象
可串行化是指的一个并发执行的事务调度在效果上等同于某种串行执行的事务序列。
实现可串行化
使用事务隔离级别
将事务分为四大级别,读未提交、读已提交、可重复读、可串行化
它们级别不断上升,可解决数据不一致性问题增加
| 事务级别 | 丢失更新 | 脏读 | 不可重复读 |
|---|---|---|---|
| read uncommitted | y | n | n |
| read committed | y | y | n |
| repeatable read | y | y | y |
| serializable | y | y | y |
下面所说的未提交和提交,是对于正在被操作数据来说的,如果这条数据没有排它锁(见下),则是未提交,反之是提交
读未提交级别,是允许读取未提交的数据,但是不能修改未提交数据
读已提交级别,是只允许读已提交的数据,读不了未提交的数据,即正被其它事务更新的数据
可重复读级别,是只允许读其它事务完成后的数据,
使用封锁协议
上述事务隔离都是用户角度的,站在数据库系统角度,是通过封锁协议的实现的。
封锁协议所用的锁:
- 共享锁(S锁):允许一个事务读取一个数据项,但不允许修改。当一个数据项加上共享锁后,其他事务仍可以加共享锁来读取这个数据项,但不能加排他锁来修改它。
- 排他锁(X锁):允许一个事务修改一个数据项。当一个数据项加上排他锁后,其他事务既不能加共享锁来读取这个数据项,也不能加排他锁来修改它。
- 意向锁:是一种表级锁,用来表明某个事务打算在表中的某些行上加共享锁或排他锁。这种锁有助于提高数据库系统的并发度。
当然封锁协议也分为许多:一级封锁协议,二级封锁协议,三级封锁协议,二段封锁协议,粗粒封锁
- 一级封锁协议,是仅采用排他锁,同时在事务结束时释放锁,用以解决丢弃更新
简单理解,每次更新都上锁,防止这次更新失效嘛,所以可以解决丢弃更新
- 二级封锁协议,是采用排他锁和短暂的共享锁实现,每次读的时候上锁,读完释放锁,用以解决脏读
简单理解,每次读取都会上锁,如果这个数据正在被更新,就不允许读取了。
因为被更新的数据可能出现更新异常的问题,导致读取了本不存在的错误数据,所以就直接禁止读正在被更新的数据,用来解决脏读问题
- 三级封锁协议,是采用排他锁和长久的共享锁实现,每次读的时候上锁,但是事务结束后才释放锁,用以解决不可重复读问题
简单理解,如果读前上锁,最后释放锁,则这个数据在第一次读取和往后读取都不会发生变化,因为被锁住了,实现了可重复读
- 二段锁协议,由于三级封锁协议太占用数据了,读和写了一次就一直上锁,十分不合理,所以设定可以提前释放锁,用来提高并发效率