四大特性
如果一个数据库支持事务的操作,那么该数据库需要具备ACID四大特性:
1.原子性——Atomicity
原子性指事务里的所有操作要么一起成功,要么一起失败回滚。
2.一致性——Consistency
数据在事务执行前后,从一个一致性状态转变至另一个一致性状态。
举例说明,A和B之间转账,A有2000元,B有3000元,无论A和B之间怎样转账,他们的金额之和一定为5000,这就是一致性。
3.隔离性——Isolation
当多个事务并发访问数据库时,各个事务之间应该是隔离的,独立的。一个事务不应该影响其他事务的效果。事务查看数据更新时,数据要么是其他事务执行前的状态,要么是其他事务执行后的状态,不能看到中间的状态。
完全的隔离性只有事务串行执行,那样则不存在并发,效率很低,因此在开发中应结合业务需求设置事务的隔离级别,隔离级别越高,效率越低,反之,效率越高。四个隔离级别稍后介绍。
4.持久性——Durability
持久性,很好理解,就是说事务一旦提交,对数据库造成的影响是永久性的,即便数据库遭遇断网断电等故障,也不会丢失提交的更新。
SQL SERVER通过write-ahead transaction log来确保事务提交后的持久性。事务在提交后,对数据库所做的改变首先会按顺序记录在事务日志中,然后才会写入数据库,当数据库遇到故障重启后,首先会按顺序检查日志中该执行但未执行的更改,并依次执行,保证了持久性。
隔离级别
接下来介绍事务的四个隔离级别:
首先上结论,隔离级别从低到高,性能从高到低:
未提交读,引起脏读;
提交读,解决脏读,引起不可重复读;
可重复读,解决脏读、不可重复读,引起幻读;
串行读,解决脏读、不可重复读、幻读,数据保证了安全,但是失去并发性,效率低。
1.未提交读——Read Uncommitted
事务A修改了数据后,无论该事务A有没有提交,其他任何事务都可以读取到事务A修改后的数据。
潜在问题:当事务A修改数据后提交之前,其他事务读取到A修改后的数据,并在该数据基础上进行操作。此时事务A提交失败数据回滚,数据则出现错误。称其他事务读取到A事务修改但未提交的数据为“脏读”。
锁机制:select句指定 with (nolock),即读数据时不加共享锁,允许其他事务更改数据,则可能读到其他事务未提交的数据。
2.提交读——Read Committed
事务A修改了数据,只有A提交之后,其他事务才可以看到A修改后的数据。解决了“脏读”。
潜在问题:事务B读取了数据后,事务A对该数据进行了修改并提交,此时事务B再次以相同条件读取数据,发现前后两次读取的数据不一致,这就是所谓的“不可重复读”。
锁机制:读取数据时正常加共享锁即可,读完释放,无论事务有没有提交。因为共享锁和排他锁互斥,所以只可能读到排他锁被释放(即提交/回滚后)后的数据。
3.可重复读——Repeatable Read
当使用可重复读隔离级别时,在事务执行期间会锁定该事务以任何方式引用的所有行。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么产生的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并对它们执行任意操作,直到提交或回滚操作终止该事务。。解决了“脏读”、“不可重复读”。
潜在问题:幻读,当一个事务对数据的修改涉及到“全部数据行”时,比如修改表里所有行的column1字段为‘string1’,同时,另一个事务以新增的方式向表里插入了一条新的数据(可重复读不允许其他事务修改已存在的数据,因为可重复读只在事务结束时才释放锁。但是对于新插入的数据没办法加锁啊,新插入的数据由执行插入sql的事务持有X锁。)。那么之后,操作第一个事务的用户就会发现明明自己修改了全部行的数据,为什么还有一行数据没有被修改,就好像发生了幻觉一样,这就是幻读。
锁机制:对事务A扫描范围内的数据加U锁或S锁,并一直持有直到事务结束。因此其他事务不可以修改数据,但是可以读取数据。但是可以通过插入新数据的方式改变事务A的结果集。
4.串行读——Serializable
串行读,很容易理解,就是各个事务之间完全隔离,串行执行。
存在问题:已经不是潜在问题了,问题很明显,串行执行,那么并发效率就很低。
锁机制:加Range lock,范围锁可以覆盖到所查询出的行的索引的键值范围,而任何其他事务对范围内数据的修改、添加和删除都需要修改索引,所以此时将会被阻塞,因为范围锁覆盖了索引条目。深入了解Range Lock