十八、SqlServer事务

一、什么是事务?

事务是在数据库上按照一定的逻辑顺序执行的任务序列,是恢复和控制并发的基本单位,既可以由用户手动执行,也可以由某种数据库程序自动执行。
 
事务究竟有什么价值呢?
转账:
张三像李四转账一万块钱;
业务操作: ---存在两个操作; 业务中,必须要保证两个操作都成功才行!
1、张三的账户----存款减去10000元
2、同时李四的账户----存款数量要加上10000元
张三账户:50000元 转出了10000元 ====40000
李四账户:40000元 接受了10000元 =====500000

二、事务分类

2.1、显式事务

用 begin transaction 明确指定事务的开始,由 commit transaction 提交事务、rollback transaction 回滚事务到事务结束。

2.2、隐式事务

通过设置 set implicit_transactions on 语句,将隐式事务模式设置为打开。当以隐式事务模式操作时,不必使用 begin transaction 开启事务,当一个事务结束后,这个模式会自动启用下一个事务。只需使用 commit transaction 提交事务或 rollback transaction 回滚事务即可。

2.3、自动提交事务

SQL Server的默认模式,它将每条单独的T-SQL语句视为一个事务。如果成功执行,则自动提交,否则回滚。

三、事务的特性

3.1、原子性

保证任务中的所有操作都执行完毕;否则,事务会在出现错误时终止,并回滚之前所有操作到原始状态。

3.2、一致性

事务必须使数据库从一个一致性状态变换到另一个一致性状态。。

3.3、持久性

保证任务中的所有操作都执行完毕;否则,事务会在出现错误时终止,并回滚之前所有操作到原始状态。
持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

3.4、隔离性

保证不同的事务相互独立、透明地执行。

四、事务不隔离导致的问题

以上介绍完事务的四大特性(简称ACID),现在重点来说明下事务的隔离性,当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性,在介绍数据库提供的各种隔离级别之前,我们先看看如果不考虑事务的隔离性,会发生的几种问题:
更新丢失(Lost update)、脏读(Dirty Reads)、不可重复读(Non-repeatable Reads)

4.1、更新丢失

两个事务都同时更新一行数据,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。这是因为系统没有执行任何的锁操作,因此并发事务并没有被隔离开来。

4.2、脏读

一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。
当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。
--用户A向用户B转账1000元,B的钱增加
update account set money=money+1000 where name='B';
--此时A通知B我给你转钱了,A的钱减少
update account set money=money - 1000 where name='A';
 当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读)
   而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。

4.3、不可重复读

不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了
(1) 虚读:事务T1读取某一数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
(2) 幻读:事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据或者缺少了第一次查询中出现的数据(这里并不要求两次查询的SQL语句相同)。
这是因为在两次查询过程中有另外一个事务插入数据造成的。

五、事务的隔离级别

5.1、未提交读取(相当于with(nolock)):第一级别

也称未授权读取:允许脏读取,但不允许更新丢失。
如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。
该隔离级别可以通过“排他写锁”实现。
       缺点:会产生脏读、不可重复读、幻读。

5.2、提交读取(Oracle和SQLServer默认的):第二级别

这是大多数数据库系统的默认隔离级别(Oracle和SQLServer默认的)。
也称为授权读取:允许不可重复读取,但不允许脏读取。
这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,
但是未提交的写事务将会禁止其他事务访问该行。
       缺点:会产生不可重复读、幻读。

5.3、可重复读取(相当于(HOLDLOCK)):第三级别

MySQL的默认事务隔离级别。
  可重复读取(Repeatable Read):禁止不可重复读取和脏读取,但是有时可能出现幻读数据。
这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写
事务则禁止任何其他事务。
  缺点:会产生幻读。

5.4、序列化(这是最高的隔离级别):第四级别

序列化(Serializable):提供严格的事务隔离。
它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。
仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询
操作的事务访问到。
  缺点:可以解决并发事务的所有问题。但是效率底下,消耗数据库性能,一般不使用。
  隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed。它能够避免脏读
取,而且具有较好的并发性能。
 
尽管它会导致不可重复读、幻读和第二类丢失更新这些并发问题,在可能出现这类问题的个别场合,可
以由应用程序采用悲观锁或乐观锁来控制。

5.5、快照

(1)SNAPSHOT 在SNAPSHOT隔离级别下,当读取数据时可以保证操作读取的行是事务开始时可用的最
后提交版本。
(2)同时SNAPSHOT隔离级别也满足前面的已提交读,可重复读,不幻读;该隔离级别实用的不是共享锁,而
是行版本控制。

5.6、已提交读快照(READ COMMITTED SNAPSHOT)

READ COMMITTED SNAPSHOT也是基于行版本控制,但是READ COMMITTED SNAPSHOT的隔离级别是读操作之前的最后已提交版本,而不是事务前的已提交版本,有点类似前面的READ COMMITTED能保证已提交读,但是不能保证可重复读,不能避免幻读,但是又比 READ COMMITTED隔离级别多出了不需要获取共享锁就可以读取数据。

六、事务隔离总结

 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章