转载请注明出处: jiq•钦's technical Blog - 季义钦
引言:在网上搜了非常多关于事务的文章,感觉单独来看都非常难看懂,所以综合自己的理解写一篇我自己能理解的关于关系型数据库事务的文章。
一、事务特征
我们都知道数据库事务具备ACID特性:
Atomic(原子性):一个事务要么成功,要么失败
Consistency(一致性):一致性代表了底层数据存储的完整性。
事务运行前后数据库都必须处于一个合法的状态。什么才是一个合法的状态?
比方满足数据库的唯一性约束、数据类型验证、引用完整性等,更复杂的还包含事务运行后结果不会和现实业务期望的结果不同。
举个样例:比方由两张表学生表和专业表。由于学生要有一个所属的专业,所以学生表须要引用专业表,可是假设新增一个专业和一个学生,而这个学生恰好属于这个新专业,那么这两个插入操作必须放在一个事务中假设有一个失败都要回滚,不然就会导致学生可能引用一个不存在的专业,出现事务运行后数据库引用不完整。
另外比方银行系统有多个账户,钱仅仅会在账户之间相互转移,而不会凭空消失,假设要将账户A的钱转移到账户B。那么账户A的扣钱操作比方和账户B的加钱操作要么一起成功。要么一起失败,保证事务运行前后业务数据都处于一致的状态。
Isolation(隔离性):隔离性意味着事务必须在不干扰其它进程或事务的前提下独立运行
Durability(持久性):事务结束后,事务处理的结果必须可以得到固化。
二、问题
假设没有不论什么并发控制机制。不同的线程运行事务时会出现几类问题:
1.更新丢失
(1)更新丢失(Lostupdate)
两个事务同一时候更新,第二个事务回滚会覆盖第一个事务更新的数据,导致更新丢失
(2)两次更新问题(Secondlost updates problem)
两个事务都读取了数据,并同一时候更新。第一个事务更新失败,由于被第二个事务覆盖。
2.脏读(Dirty Reads)
事务T1读取到事务T2改动了可是还未提交的数据。之后事务T2又回滚其更新操作。导致事务T1读到的是脏数据。
3.不可反复读(Non-repeatableReads)
事务T1读取某一行数据后。事务T2对其做了改动。当事务T1再次读该数据时得到与前一次不同的值。
4.幻读(Phantom Reads)
事务T1读取在读取范围数据时,事务T2又插入一条数据,当事务T1再次数据这个范围数据时发现不一样了,出现了一些“幻影行”。
三、并发控制
为此我们须要通过提供不同类型的“锁”机制针对数据库事务进行不同程度的并发訪问控制,由此产生了不同的事务隔离级别:隔离级别(低->高)
●读未提交(Read Uncommitted)
含义解释:仅仅限制同一数据写事务禁止其它写事务。解决”更新丢失”
名称解释:可读取未提交数据
所需的锁:排他写锁
●读提交(Read Committed)
含义解释:仅仅限制同一数据写事务禁止其它读写事务。解决”脏读”。以及”更新丢失”
名称解释:必须提交以后的数据才干被读取
所需的锁:排他写锁、瞬间共享读锁
●可反复读(Repeatable Read)
含义解释:限制同一数据写事务禁止其它读写事务,读事务禁止其它写事务(同意读)。
解决”不可反复读”。以及”更新丢失”和”脏读”。
注意没有解决幻读,解决幻读的方法是添加范围锁(range lock)或者表锁。
名称解释:可以反复读取
所需的锁:排他写锁、共享读锁
●序列化(Serializable)
提供严格的事务隔离。它要求事务序列化运行,事务仅仅能一个接着一个地运行,但不能并发运行。假设仅仅通过“行级锁”是无法实现事务序列化的,必须通过其它机制保证新插入的数据不会被刚运行查询操作的事务訪问到。
限制全部读写事务都必须串行化实行。
下表是各隔离级别对各种异常的控制能力。
更新丢失 | 脏读 | 不可反复读 | 幻读 | |
RU(读未提交) | 避免 | |||
RC(读提交) | 避免 | 避免 | ||
RR(可反复读) | 避免 | 避免 | 避免 | |
S(序列化) | 避免 | 避免 | 避免 | 避免 |
四、经常使用数据库默认隔离级别
各类数据库默认事务隔离级别
数据库 | 默认级别 |
MySQL | 可反复读(Repeatable Read) |
Oracle | 读提交(Read Committed) |
SQLServer | 读提交(Read Committed) |
DB2 | 读提交(Read Committed) |
PostgreSQL | 读提交(Read Committed) |