事务并发控制
前段是时间涉及到数据库的隔离级别问题,在网上搜索一下,发现大部分都说的模棱两可,而且也有很多错误。于是查阅了上学时的课本《数据库系统概论》 和JDBC的文档。现在把总结的结果记录下来,供大家分享.
1. DBMS事务并发控制
1.1 事务并发操作的问题 DMBS允许东西运行多个事务,这些事务可能同时操作同一数据对象。这可能造成以下问题 丢失修改 : 一个事务的修改被另一个事务覆盖。 不可重复读 : 在一个事务中执行相同的查询,两个的结果不一样。 幻读 : 幻读是不可重复多的一种,在一个事务中执行相同的查询,第二次的结果行比第一次少或者多(行被delete或者被insert) 脏读 : 一个事务读到的数据无效的,是另外一个事务回滚的数据
所以需要对事务进行并发控制,采用的主要技术是锁。
1.2 锁
锁是指一个事务对每个数据对象进行操作时,需要对数据对象加对应锁。这样其他事务最此数据对象访问就受限。当操作结束后事务释放此锁。
1.2.1 锁的粒度
锁的粒度包括逻辑单元也可以是逻辑单元:
列值(注意不是列)、列值集合、行、表、索引项、整个索引、数据库;
也可以是物理单元:
数据页、索引页、块。
事务可以对各种类型的数据对象加锁,即多粒度加锁。这样在DBMS中就存在了可以被加锁的数据对象树,简称多粒度树。如下,三级多粒度树。
1.2.2 锁的类型
锁分为5中类型,如下 排他锁(X锁): 也叫写锁。若事务T对数据对象A添加X锁,则只允许T读取和修改A,其他任何事务都不能再对A添加任何类型的锁,直到T释放此锁。 共享锁(S锁): 也叫读锁。若事务T对数据对象A添加此类型锁,则事务T允许读取A但不允许修改A,其他事务只能再对A添加S锁,而不能加X锁,直到A上的所有S锁都被释放。 意向排他锁(IX锁): 如果对一个数据对象加IX锁,表示他在多粒度树中的后裔(包括直接孩子,孩子的孩子等等所有的后面的结点)有被加X锁的可能(意向)。 意向共享锁(IS锁): 如果对一个数据对象加IS锁,表示他在多粒度树中的后裔(包括直接孩子,孩子的孩子等等所有的后面的结点)有被加S锁的可能(意向)。 共享意向排他锁(SIX): 如果对一个数据对象加SIX锁,则表示对他加S锁,再加IX锁。
这个5类锁的相容矩阵如下, T1已经对数据对象加锁,T2尝试对数据对象加锁。Y表示成功,N表示失败。
在Oracle中上面五个类型的锁分别对应X锁、S锁、RX锁、RS锁、SRX锁,而且加锁粒度包括行级和表级,不包括数据库。
1.2.3锁协议
对数据对象加锁时,需要约定一些规则,例如合适申请、持锁时间、何时释放。这些规则称为锁协议。DBMS遵循哪些协议要看具体情况。下面介绍一些协议: 基本锁协议:当事务T对数据对象A加锁时,如果A上已经存在锁且和T欲加锁不相容, 那么T对待,直到A上存在的所被释放。 一级锁协议:事务T在修改数据对象A之前必须先对其加X锁,知道事务结束才释放。 二级锁协议:在一级锁协议的基础上加上事务T在读取数据对象A之前必须先对其加S锁,读完后即可释放S锁。 三级锁协议:在一级锁协议的基础上加上事务T在读取数据对象A之前必须先对其加S锁,直到事务结束才释放。 多粒度锁协议: 多粒度树中的每个结点被独立地加锁(显式锁)。对一个结点加锁意味着其所有后裔结点也被加以同样类型的锁(隐式锁, 并没有真正在后裔结点加锁,只是在逻辑上后裔结点具有同样类型的锁,所以叫隐式锁。)。显式锁和隐式锁的效果是一样的,因此检查锁相容时不仅要检查显式锁还要检查隐式锁。所以事务T要对数据对象A加锁,需要检查其祖先(包括直接的和间接的所有上层结点)上的显式锁(检查A的隐式锁是否与欲加的显式锁不相容)、检查其显式锁(检查A已存在的显式锁是否与欲加的显式锁不相容)、检查其后裔的显式锁(检查欲加给后裔的隐式锁是否与后裔已存在的显式锁不相容),如果发现一个不相容锁,则T等待。 意向锁协议:对任一结点加锁时,必须先对其祖先(包括直接的和间接的所有上层结点)加意向锁,顺序为自上而下。释放结点上锁后,释放其祖先上的意向锁,释放顺序为自下而上。意向锁的含义是如果对一个结点加意向锁,则说明该结点的后裔结点正在被加锁。所以事务T要对数据对象A加锁,需要检查A祖先上的所有锁(检查祖先上的锁是否与欲加给祖先的意向锁相容)、检查A上的所有锁(检查A上的锁是否与欲加他的锁相容),如果发现一个不相容锁,则T等待。 多粒度锁协议,在给结点加锁时需要递归检查子结点的显式锁,当子结点很多时,将带来很多的性能开销。意向锁是针对这个问题进行的改进,他将锁在多粒度树间的传递由原来的由上而下变成由下而上,从而提高了性能。Oracle采用了意向锁协议。
2.DBMS事务隔离级别
2.1 事务隔离级别的说明 上一节讨论的内容是DBMS实现事务并发控制的内容,是DBMS内部问题。那么如何从外面干预DBMS内部的事务并发控制呢? 当事务写数据对象时,DBMS使用X锁及其相关锁(例如IX)进行操作,这个过程是固定的,外界无法也无需干预。 当事务读数据对象时,哪些数据对象应该对此事务可见呢?例如,其他事务已写还未提交的、其他事务已写已提交的,等等。这个应该是可以选择的,也就是 说外界可以干预。DBMS通过事务隔离级别来让事务选择读哪些数据对象。 可以这样理解,写是严格操作,不允许发生错误,所以其并发控制是最严格的,也是不可选的。而读是宽松的操作,允许出现 不可重复读 、幻读、脏读问题,所以其并发控制是分多个级别的,可以选择相应的级别改变并发度和允许出现的错误。
2.2 事务隔离级别类型 ANSI/ISO SQ92标准定义了如下事务隔离级别: 2.2.1未提交读(read uncommitted) 事务可以读其他事务未提交数据对象。 潜在问题: 脏读、不可重复读、幻读 DBMS锁操作 :不加任何锁,直接读任意数据对象。 2.2.2提交读(read committed) 事务可以读其他事务已提交数据对象,读完后此数据对象可以被其他事务再读写。 可能出现的问题: 不可重复读、幻读 DBMS锁操作(采用意向锁协议) :加S锁,祖先加IS锁,读完后立即释放S锁和祖先的IS锁。 2.2.3 重复读(repeatabe read) : 事务可以读其他事务已提交数据对象,读完后此数据对象不可以被其他事务再写但可以读,直到 此事务结束。 可能出现的问题: 幻读 DBMS锁操作 (采用意向锁协议):加S锁,祖先加IS锁。事务结束后释放S锁和祖先的IS锁。 2.2.4 序列化(seriaizabe): 事务读之前,其他事务已提交的数据对象对此事务可见,读完后整个多粒度树只读不能写,直到事务结束。所以他严重的降低了事务的并发度,特别是多粒度树很深时,这也是Oracle没有将数据库作为多粒度树根的原因。在此级别下事务完全隔离,就如所有的事务都顺序执行一样。 可能出现的问题: 无 DBMS锁操作 (采用意向锁协议):加S锁,祖先加SIX锁,事务结束后释放S锁和祖先的SIX锁。
2.3 隔离级别的使用 隔离级别是针对事务的。但是也可以配置Session(对应JDBC的Connection)默认的和DBMS全局默认的事务隔离级别。这样创建事务时不指定就采用默认的事务隔离级别。
3. JDBC事务 在JDBC中通过Connection执行事务。一个Connection可以执行多个事务。一个事务可以执行多个SQL语句。
3.1 事务的开启 在Connection中,如果不存在事务,那么在执行SQL语句之前自动开启一个事务。如果事务已经存在那么直接使用其执行SQL语句。
3.2 事务的结束 SQL语句完成之后调用Connection的commit或者rollback方法结束当前事务。在执行之后的SQL语句之前自动开启一个新的事务。
3.3 设置事务的隔离级别 在JDBC中不能单独设置事务的隔离级别,只能通过Session(对应Connection)间接设置,通过Connection的setTransactionIsolation方法。调用此方法之后,如果存在当前事务并且改变了Connection的事务隔离级别那么会引起当前事务立刻提交,之后开启的事务将采用此事务隔离级别。
3.4 SQL语句的完成时间 SQL语句的类型不同,其SQL语句完成时间不同。具体如下 DML, DDL语句:SQL语句执行完成,那么SQL语句就算完成。 Select语句:查询语句执行完成后返回ResultSet,当调用ResultSet的close方法后SQL语句算完成。 多结果语句:当SQL语句返回多结果时(例如执行存储过程,批处理),那么SQL语句的完成时间是所以结果的最大者。
3.5Connection的自动提交模式 Connection有个模式叫自动提交,在这种模式下SQL完成后立即调用Connection的commit方法或者rollback方法。这就使每个SQL在独立的事务中执行。自动提交模式模式是开启的。
版权声明:本文为博主原创文章,未经博主允许不得转载。