天天看点

数据库事务 ACID事务ACID总结

文章目录

  • 事务
    • 隐式事务
    • 显示事务
      • 步骤
  • ACID
    • 原子性(Atomicity)
    • 一致性(Consistency)
    • 隔离性(Isolation)
      • 事务的并发问题
      • 读未提交
      • 读已提交
      • 可重复读
      • 串行化
    • 持久性(Durability)
  • 总结

事务

定义:所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。

准备工作:为了说明事务的ACID原理,我们使用银行账户及资金管理的案例进行分析。

// 创建数据库  
create table account(  
   idint primary key not null,  
   namevarchar(40),  
   moneydouble  
);  
// 有两个人开户并存钱  
insert into account values(1,'A',1000);  
insert into account values(2,'B',1000);  
           

隐式事务

没有明显的开启和结束标记,比如dml语句的insert,update,delete语句本身就是一条事务。

insert into account values(1,‘A’,1000);

显示事务

一般有多条sql语句组成,必须具有明显的开启和结束标记

步骤

  1. 取消隐式事务自动开启的功能

    show variables like “%autocommit%”; on/off

    set autocommit = 0;

  2. 开启事务

    start transaction; //or begin;

  3. 编写事务需要的sql语句(1条或者多条)

    insert into account values(1,‘A’,1000);

    insert into account values(2,‘B’,1000);

  4. 结束事务

    提交

    commit

    回滚

    rollback;

ACID

ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)所应该具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability).这是可靠数据库所应具备的几个特性.下面针对这几个特性进行逐个讲解.

原子性(Atomicity)

原子性是指事务是一个不可再分割的工作单位,事务中的操作要么都发生,要么都不发生。

  1. 案例
    数据库事务 ACID事务ACID总结
begin transaction  
update account set money= money - 100where name='A';  
update account set money= money +100where name='B';  
if Error then  
   rollback  
else  
   commit
           
  1. 分析

    在事务中的扣款和加款两条语句,要么都执行,要么就都不执行。否则如果只执行了扣款语句,就提交了,此时如果突然断电,A账号已经发生了扣款,B账号却没收到加款,在生活中就会引起纠纷。

  2. 解决方法

    在数据库管理系统(DBMS)中,默认情况下一条SQL就是一个单独事务,事务是自动提交的。只有显式的使用start transaction开启一个事务,才能将一个代码块放在事务中执行。保障事务的原子性是数据库管理系统的责任,为此许多数据源采用日志机制。例如,SQL Server使用一个预写事务日志,在将数据提交到实际数据页面前,先写在事务日志上。

一致性(Consistency)

是指事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。保证数据库一致性是指当事务完成时,必须使所有数据都具有一致的状态。在关系型数据库中,所有的规则必须应用到事务的修改上,以便维护所有数据的完整性。

  1. 案例

    对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNT表中aaa和bbb的存款总额为2000元。

  2. 解决方法

    保障事务的一致性,可以从以下两个层面入手

    2.1 数据库机制层面

    数据库层面的一致性是,在一个事务执行之前和之后,数据会符合你设置的约束(唯一约束,外键约束,Check约束等)和触发器设置。这一点是由SQL SERVER进行保证的。比如转账,则可以使用CHECK约束两个账户之和等于2000来达到一致性目的.

    2.2 业务层面

    对于业务层面来说,一致性是保持业务的一致性。这个业务一致性需要由开发人员进行保证。当然,很多业务方面的一致性,也可以通过转移到数据库机制层面进行保证。 比如用户user1对表进行了更新操作,用户user2在user1还没有进行提交前读表中数据,而且是大批量的读取(打个比方:耗时3分钟)而在这3分钟内user1进行了提交操作,那又会产生什么影响呢?这个时候怎么保证读写一致性呢?**这个时候DBMS就要保证有足够大的临时表来存放修改前的数值,**以保证user2读取的数据是修改前的一致数据.然后下次再读取时候就是更新后的数据了.

隔离性(Isolation)

多个事务并发访问时,事务之间是隔离的,一个事务不应该影响其它事务运行效果。

这指的是在并发环境中,当不同的事务同时操纵相同的数据时,每个事务都有各自的完整数据空间。由并发事务所做的修改必须与任何其他并发事务所做的修改隔离。事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。

在Windows中,如果多个进程对同一个文件进行修改是不允许的,Windows通过这种方式来保证不同进程的隔离性:

数据库事务 ACID事务ACID总结

企业开发中,事务最复杂问题都是由事务隔离性引起的。当多个事务并发时,MySql利用加锁和阻塞来保证事务之间不同等级的隔离性。一般情况下,完全的隔离性是不现实的,完全的隔离性要求数据库同一时间只执行一条事务,这样会严重影响性能。

事务的并发问题

1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据.

2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致.

3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。

小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

  

在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示:

数据库事务 ACID事务ACID总结

mysql默认的事务隔离级别为repeatable-read

数据库事务 ACID事务ACID总结

读未提交

(1)打开一个客户端A,并设置当前事务模式为read uncommitted(未提交读),查询表account的初始值:

数据库事务 ACID事务ACID总结

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表account:

数据库事务 ACID事务ACID总结

(3)这时,虽然客户端B的事务还没提交,但是客户端A就可以查询到B已经更新的数据:

数据库事务 ACID事务ACID总结

(4)一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那客户端A查询到的数据其实就是脏数据:

数据库事务 ACID事务ACID总结

(5)在客户端A执行更新语句update account set balance = balance - 50 where id =1,lilei的balance没有变成350,居然是400,是不是很奇怪,数据不一致啊,如果你这么想就太天真 了,在应用程序中,我们会用400-50=350,并不知道其他会话回滚了,要想解决这个问题可以采用读已提交的隔离级别

数据库事务 ACID事务ACID总结

读已提交

(1)打开一个客户端A,并设置当前事务模式为read committed(未提交读),查询表account的所有记录:

数据库事务 ACID事务ACID总结

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表account:

数据库事务 ACID事务ACID总结

(3)这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题:

数据库事务 ACID事务ACID总结

(4)客户端B的事务提交

数据库事务 ACID事务ACID总结

(5)客户端A执行与上一步相同的查询,结果 与上一步不一致,即产生了不可重复读的问题

数据库事务 ACID事务ACID总结

可重复读

(1)打开一个客户端A,并设置当前事务模式为repeatable read,查询表account的所有记录

数据库事务 ACID事务ACID总结

(2)在客户端A的事务提交之前,打开另一个客户端B,更新表account并提交

数据库事务 ACID事务ACID总结

(3)在客户端A查询表account的所有记录,与步骤(1)查询结果一致,没有出现不可重复读的问题

数据库事务 ACID事务ACID总结

 (4)在客户端A,接着执行update balance = balance - 50 where id = 1,balance没有变成400-50=350,lilei的balance值用的是步骤(2)中的350来算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了MVCC机制,select操作不会更新版本号,是快照读(历史版本);insert、update和delete会更新版本号,是当前读(当前版本)。

数据库事务 ACID事务ACID总结

(5)重新打开客户端B,插入一条新数据后提交

数据库事务 ACID事务ACID总结

(6)在客户端A查询表account的所有记录,没有 查出 新增数据,所以没有出现幻读

数据库事务 ACID事务ACID总结

串行化

(1)打开一个客户端A,并设置当前事务模式为serializable,查询表account的初始值.

(2)打开一个客户端B,并设置当前事务模式为serializable,插入一条记录报错,表被锁了插入失败,mysql中事务隔离级别为serializable时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

持久性(Durability)

持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。

即使出现了任何事故比如断电等,事务一旦提交,则持久化保存在数据库中。

总结

  1. 事务隔离级别为读提交时,写数据只会锁住相应的行。
  2. 事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
  3. .事务隔离级别为串行化时,读写数据都会锁住整张表。
  4. 隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。

参考:

https://www.cnblogs.com/wyaokai/p/10921323.html (MySQL的四种事务隔离级别)