1、ACID
ACID,是指在可靠数据库管理系统(DBMS)中,事务(transaction)所应该具有的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability).这是可靠数据库所应具备的几个特性。
2、事务
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
3、事务的语句
开始事物:BEGIN TRANSACTION
提交事物:COMMIT TRANSACTION
回滚事务:ROLLBACK TRANSACTION
4、数据库所具有的四个事务特性
(1)原子性(Atomic)
即要么都发生,要么都不发生。事务是一个不可再分割的工作单位。
例子:银行的转账
A转给B 100块钱,在转账的过程中,如果A扣款成功,那么B 的账户必然多出了一百块钱,如果A转账过程出现的异常,那么事务回滚,A的账户不会扣款,B的账户也不会多出 100块钱。
在A扣款和B的账户中多出钱,这两个步骤要么都执行,要么都不执行,如果只执行了其中的一个,那么就会出现钱财的纠纷。
解决方法
在数据库管理系统(DBMS)中,默认情况下一条SQL就是一个单独事务,事务是自动提交的。只有显式的使用start transaction开启一个事务,才能将一个代码块放在事务中执行。保障事务的原子性是数据库管理系统的责任,为此许多数据源采用日志机制。例如,SQL Server使用一个预写事务日志,在将数据提交到实际数据页面前,先写在事务日志上。(MySQL的Innodb日志机制以后在写)。
(2)一致性(Consistency)
一致性是指在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。这是说数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。
也就是说不管局部之间是怎么发生变化的,但是在总体上不能发生变化,数据库的完整性不能被破坏。
例子:
仍然用银行的转账来说,如果A有100块钱,B有100块钱,那么不论A与B之间经过多少次的转账,但是A+B的账户总额度还是应该为200块钱,这个是不能有变化的。要维持这个总体的一致性。
解决方法
保障事务的一致性,可以从以下两个层面入手
2.1数据库机制层面
数据库层面的一致性是,在一个事务执行之前和之后,数据会符合你设置的约束(唯一约束,外键约束,Check约束等)和触发器设置。这一点是由SQL SERVER进行保证的。比如转账,则可以使用CHECK约束两个账户之和等于200来达到一致性目的
2.2业务层面
对于业务层面来说,一致性是保持业务的一致性。这个业务一致性需要由开发人员进行保证。当然,很多业务方面的一致性,也可以通过转移到数据库机制层面进行保证。
(3)隔离性(Isolation)
在并发的环境中,多个事务之间是相互隔离的,一个事务的运行运行不应该影响其他的事务的运行效果。
在并发的环境中,当不同的事务同是操作数据的时候,他们都应该有自己完整的数据操作环境,由其他并发事务所做的修改必须与其他事务所做的修改隔离开。事务去查看、修改或者其他操作去操作数据的时候,这组数据要么处在其他事务修改(或其他操作)它之前的状态,要么是之后的状态,不能处在其他事务未提交的状态,中间的状态。
解决方法:
当事务并发是,sql server会采用加锁或阻塞来保证事务之间不同等级的隔离性。但是,如果,要实现完全隔离是不现实的,因为如果要实现完全隔离,那么要求数据库在同一时间,只运行同一个事务,这会很浪费、影响数据库的性能,因此,只能尽可能的实现隔离。
事务之间的干扰:
3.1事务之间的干扰,主要是以下几种
脏读、幻读、不可重复读、丢失更新。
3.2脏读
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。
如下图
如图,发生了脏读,那么B肯定会因为此操作而造成损失。
3.3幻读
幻读是指当事务不是独立执行时发生的一种现象。
事务A读取与搜索条件相匹配的若干行。事务B以插入或删除行等方式来修改事务A的结果集,然后再提交。
比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。那么,以后就会发生操作第一个事务的用户发现表中还存在没有修改的数据行,就好象发生了幻觉一样.一般解决幻读的方法是增加范围锁RangeS,锁定检索范围为只读,这样就避免了幻读。
如图:
在事务A处理了以后,事务B又进行了操作,那么,结果就是,出现了好像是幻觉一样的感觉,事务A并没有进行处理。
3.4不可重复读
不可重复读,是指在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。
这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。
一种更易理解的说法是:在一个事务内,多次读同一个数据。在这个事务还没有结束时,另一个事务也访问该同一数据。那么,在第一个事务的两次读数据之间。由于第二个事务的修改,那么第一个事务读到的数据可能不一样,这样就发生了在一个事务内两次读到的数据是不一样的,因此称为不可重复读,即原始读取不可重复。
如图:
如图,这么查询,会使两次查询的结果发生不同。
3.5更新丢失
两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
3.6四种隔离级别
Read uncommitted(未授权读取、读未提交):
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。
避免了更新丢失,却可能出现脏读。也就是说事务B读取到了事务A未提交的数据。
Read committed(授权读取、读提交):
读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
该隔离级别避免了脏读,但是却可能出现不可重复读。事务A事先读取了数据,事务B紧接了更新了数据,并提交了事务,而事务A再次读取该数据时,数据已经发生了改变。
Repeatable read(可重复读取):
读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。
避免了不可重复读取和脏读,但是有时可能出现幻读。这可以通过“共享读锁”和“排他写锁”实现。
Serializable(序列化):
提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。如果仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。
序列化是最高的事务隔离级别,同时代价也花费最高,性能很低,一般很少使用,在该级别下,事务顺序执行,不仅可以避免脏读、不可重复读,还避免了幻读。
隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读取,而且具有较好的并发性能。尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可以由应用程序采用悲观锁或乐观锁来控制。
大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。
Mysql的默认隔离级别就是Repeatable read。
隔离级别 脏读 不可重复读 幻读 更新丢失 并发模型
Read uncommitted 是 是 是 否 悲观锁
Read committed 否 是 是 是 悲观锁
Repeatable read 否 否 是 是 悲观锁
Serializable 否 否 否 否 悲观锁
(4)一致性
持久性,意味着在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚。(此时即使系统崩溃,修改的数据也不会丢失。持久性是个有占模糊的概念,因为实际上持久性也分很多不同的级别。有些持久性策略能够提供非常强的安全保障,而有些则未必,而且不可能有能做到100%的持久性保证的策略。)