数据库(一):ACID,事务,隔离级别

来源:互联网 发布:python smtp发送邮件 编辑:程序博客网 时间:2024/05/19 13:45

事务

事务(transaction),最简单的理解就是多个对数据库操作的有序组合。举例来说,多个顺序执行的sql语句就构成了一个事务。

在现实中,对于数据库的数据操作,基本以事务为基本单位。

最常见的例子,转账时,A账户扣掉100,B账户增加100,这些操作,都是需要至少两个以上的sql语句来完成的。从数据库操作层面上理解,单独一个sql无法完成现实中的任务,因此,采用多个sql的有序组合来完成某个现实的任务。

明白了事务的基本含义,事务的特性就可以总结出来了:ACID。

ACID

开宗明义,ACID的解释如下:

  • Atomic,原子性
  • Consistent,一致性
  • Isolated,隔离性
  • Durable,持久性

原子性:事务中各项操作,要么全做要么全不做
一致性:事务从开始到结尾,保持一个一致的状态
隔离性:两个并发的事务无法知道对方的中间状态,就好像两个事务是串行(先后)的一样
持久性:事务完成后所做的改动都会被持久的保存到数据库中,不会回滚

以上是事务的基本特性,除了隔离性,其他三个特性都是针对单个事务的。那么出现并行的事务时,会有哪些问题呢?

三类数据读取问题

以最通常的银行转账为例,理解各种事务问题的后果
脏读—Dirty Read
A事务读取B事务尚未提交(Commit)的数据,而B事务出现回滚。

事务A 事务B 开始事务 开始事务 读取账户A的金额为100元 取款50元,账户A的余额修改为50元 读取账户A的金额为50元 撤销事务,账户A的金额恢复为100元 取款20元,账户A的余额修改为30元

①:对于刚学习的事务的人来说,这里或多或少有些困惑,单独事务的整个过程并不都是在内存完成后,在持久化到数据库中的。上例事务B取出50元的操作,是实际上修改了数据库账户A的余额(update …)。因此事务A读取的账户余额是账户B修改后的余额。
②:由于隔离性,此处的回滚并不会知道事务A读取了B事务修改的数据,因此,回滚是直接回退到事务B开始时的状态。

于是最后就出现了,账户A凭空消失50元的不一致状态。

在多说一点,会有人有疑问,为什么事务A取款后的操作,不是update account set money = money - 50 where name = 'A',而是update account set money = 30 where name = 'A',毕竟只要这样,那么及时B事务回滚,也不会出现脏读的问题,或者事务不一致等问题了。但仔细想想,针对事务A,我们在实际操作中,一般是先查看余额,有50元,然后取款20(此时事务B回滚),此时账户A要是在看一眼余额,会发现有80元,也会一脸疑惑吧。

不可重复读—Unrepeatable Read
事务A重新读取前面的数据,发现由于事务B的修改,两次读取的数据不一样。

事务A 事务B 开始事务 开始事务 读取账户A的金额为100元 读取账户A的金额为100元 取款20,账户A余额80(并提交事务) 读取账户A的金额为80元

字面意思,不需要太多解释。

幻读—Phantom Read
事务A重新读取的,由于事务B的修改(一般指插入)而出现了新的行。

事务A 事务B 开始事务 开始事务 读取所有账户,总100条记录 新增一个账户(insert),提交事务 读取所有账户,共101条记录

上述都是事务并发引起的数据读取问题,还有两个数据更新问题如下:

数据跟新问题

事务A 事务B 开始事务 开始事务 查看账户A的余额为100元 查看账户A的余额为100元,取出20元,余额80元 提交事务 取出20元,余额80元 提交事务(或者撤销事务)

③:此处,无论是事务A提交还是撤回,都会出现丢失更新问题。
撤回事务,导致第一类丢失更新;提交事务,导致第二类丢失更新

隔离级别

数据库的并发访问会产生上述的问题,通常,数据库会通过锁机制来解决数据并发访问问题,按锁定对象不同可以分为表级锁和行级锁;按并发事务锁定关系可以分为共享锁和独占锁(见下一节博客)。

直接使用锁是非常麻烦的,为此提供了自动锁机制,只要用户指定会话的事务隔离级别,数据库就会通过分析SQL语句然后为事务访问的资源加上合适的锁,此外,数据库还会维护这些锁通过各种手段提高系统的性能。

四个隔离级别如下:

隔离级别 脏读 不可重复读 幻读 第一类丢失更新 第二类丢失更新 READ UNCOMMITED √ √ √ × √ READ COMMITTED × √ √ × √ REPEATABLE READ × × √ × × SERIALIZABLE × × × × ×
阅读全文
0 0