gbase8s的事务、并发控制、锁机制、隔离级别

时间:2024-10-13 19:58:31

一、事务概念

事务是指作为单个逻辑工作单元执行的一系列操作。事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。

数据库服务器保证在事务范围内执行的操作完整且正确地提交至磁盘,否则数据库会复原至事务启动之前的状态。

一个逻辑工作单元要成为事务,必须满足所谓的ACID属性。ACID的具体含义如下:

1 A(Atomicity):操作序列要么完整的执行,否则什么都不做;

2 C(Consistency):一致性,事务执行后,保证数据库从一个一致性状态到另外一个一致性状态;

3 I(Isolation):隔离,一个事务的中间状态对其他事务不可见,即每个用户都感觉他们在单独使用数据库。隔离级别用来定义多大程度的隔离多个不同的事务;

4 D(Durability):持久性,事务的有效性,不会应用硬件或软件的失败而丢失。

二、并发控制

1相关概念 

1)隔离(+一致性) => 并发控制;

2)多个事务可以访问或修改相同的资源;

3)只要多个进程共享资源,就需要对访问进程进行排队控制;

4) 在进行并发控制时,数据库内部将生成多个并发事务访问资源的操作序列表,并且每一个事务内部的各个操作都需要序列化。

2 串行调度存在的问题

在串行调度中,采用操作序列,一个事务完成了再完成另外一个,即使两个事务T1、T2更新的是数据库中不同的对象。此种方式从并发和性能角度考虑,都不能很好的利用计算机资源。为了改善性能,需要采用非串行调度,即允许事务并发执行,即一个事务内的操作可以在其他事务提交前开始执行。

3 并发调度的常见问题

1)脏读:事务T2读取到了事务T1没有提交的结果

例如如下的操作序列会导致脏读:

事务T1读取记录,然后更新记录;

事务T2读取了更新后的记录;

若T1后续操作失败,会导致更新的记录回滚,而同时事务T2已经使用了这个没有提交的值。

2)不可重复读:事务T1中多个读操作返回不同的结果

事务T1读取一个对象;

事务T2读取并更新同一个对象;

事务T1再次读取同一个对象返回了不同的值。

3)幻影读:如果事务T1在同样的情况下多次执行select读取的结果不同

事务T1从一个表中检索满足特定条件的记录返回m条记录;

事务T2往该表中insert/delete其他满足相同条件的记录;

事务T1再次以相同的条件检索该表的数据返回的数据记录不为m条。

三、锁机制

1相关概念

1)用户可以锁定一个对象,可以防止 其它用户修改锁定的对象;

2)在多个用户并发访问数据库的情况下,为保证数据完整性,锁是很有必要的;

3)程序可以显式的对数据枷锁,数据库系统可以隐式的对对象进行加锁。

2 锁的类型

1)共享锁(Shared locks):多个用户可以读取相同的记录,如果一个对象上没有排他锁,则共享锁可以加在该对象上,它可以防止其他事务更新数据,但同时,其他事务可以读取该数据,多个事务可以在同一个对象上加多个共享锁。

2)排他锁(Exclusive locks):同一时间仅仅有一个用户可以读取相同的记录,如果一个对象上没有任何锁,排他锁才可以加在该对象上。一旦在记录上加了排他锁,则不能在该记录上增加任何锁了,直至锁释放。它可以防止其他事务读取和更新数据。

3)提升锁(Update lock):可以对锁进行升级或者降级,在更新游标时使用,由游标在含有“for update”选项执行时产生,只能在没有排他锁或其他更新锁的记录上加更新锁。当锁定的记录真正执行的时候,更新锁将提升为排他锁。

在使用select for update 时使用 RETAIN UPDATE LOCKS 选项来影响它的行为。

在隔离级别设置为 Dirty Read、Committed Read 或 Cursor Stability 的数据库中,数据库服务器在 SELECT ... FOR UPDATE 语句获取的行上放置更新锁。当您开启 RETAIN UPDATE LOCKS 选项时,数据库服务器保持更新锁,直到事务结束为止,而不是在下一随后的 FETCH 时或当关闭游标时才释放它。此选项防止在当前的用户到达事务的结束之前其他用户在更新了的行上放置排他锁。

4)意向锁(Intent lock):是一种表级锁,标识在该表上有一个游标在读取数据。

首先我们考虑以下这种情况:事务A在表1中上了一个行锁,此时事务B要对整个表1进行修改,此时B就要看表1是否上了锁,或者遍历表中所有的行,当表1没上锁或者表1中所有行都没上锁时B才能给表1上锁然后操作。但是遍历表中所有行效率太低。因此,A在上行锁之前先申请给该行所属的表1的I锁,上了I锁后再上行锁。当B看到表1上有意向锁就知道表被占用了,就不用再通过遍历表这种低效的方式找锁了。

意向锁一般与S锁或X锁连用,即意向共享锁(Intent Shared Lock,IS锁)和意向排他锁(Intent Exclusive Lock,IX锁)。若某对象上存在IS锁或IX锁,说明该对象的下层某数据对象被加了S锁或X锁。同理,若某对象被加了S锁或X锁,则该对象的上级对象也要对应加上IS锁或IX锁。

3 锁的有效期

1)程序可以控制数据库锁的有效期;

2)当数据库关闭后,数据库锁将被释放;

3)根据数据库使用了事务的情况,表锁的有效期不同。如果数据没有使用事务(没有使用事务日志,也不使用commit work语句),显式对一个表lock,当执行unload table时,锁将被释放;

4)当数据库使用了事务,事务结束时,将释放事务所有的锁。

4 锁的共存情况

5 不同粒度的锁 

1)数据库级别的锁:数据库管理活动,比如打开一个数据库时

DATABASE  database_name; 对数据库加共享锁  

DATABASE  database_name EXCLUSIVE; 对数据库加独占锁

2)表级别的锁:当整个表或表的大部分数据需要更新,加表级锁效率高。

创建排他锁实例:

 LOCK TABLE tab1 IN EXCLUSIVE MODE;

创建共享锁实例:

 LOCK TABLE tab2 IN SHARE MODE;

取消表上的锁实例:

 UNLOCK TABLE tab1;

3)页级锁:当按数据物理顺序进行访问和更新时,页级锁效率高。修改锁模式为页级锁

 ALTER TABLE tab1 LOCK MODE PAGE; 

4)行级锁:OLTP事务采用行级锁效率高。

 CREATE TABLE tab1(col1) LOCK MODE ROW;

将某表修改为行级别的语句如下:

 ALTER TABLE tab1 LOCK MODE(ROW);

5)字节锁

6)分段锁

多发生在智能大对象的访问中

7)

当删除表中的数据时,如果此列上有索引则不仅会在行上加锁,也会在对应的索引页上加锁

四、隔离级别

1)Dirty Read(脏读)

设置为Dirty Read隔离级别的参考语句如下:

 SET   ISOLATION   TO  DIRTY  READ ;

采用这种隔离级别,数据库服务器不会分配任何锁。查询过程中,可以查询到任何数据行,甚至那些被修改但尚未提交的记录。非日志数据库中只有Dirty-Read一种隔离级别。它一般用在如下应用场景: 静态表(没有更新,只读的表),速度要求更高且可以接受数据不一致情况。

2)Commited Read(只读已提交的数据)

设置为该级别的参考语句如下:

 SET   ISOLATION   TO   COMMITTED   READ ;

这种隔离级别能确保所有的记录都是提交到数据库的,因而能避免读取到脏数据,能确保所有读取到的记录都是已提交的,当一个进程读完记录后,其它进程就可以修改。在读取数据前,数据库服务器尝试在记录上加共享锁。加锁前,需要先检查是否可以对对象加共享锁;如果可以加锁,则要保证要加锁的记录没有其他进程正在更新;当继续正在更新时,记录上有排他锁,此时我们不能对记录加共享锁。

3)Cursor Stability(游标读)

参考设置语句如下:

 SET   ISOLATION   TO   CURSOR  STABILITY;

给游标读的所有记录加上共享锁。通过游标检索数据,共享锁将一直保持到执行下一个FETCH语句。不仅可以看到提交的记录,也可以保证看到的记录不会被更新,其它进程不能更新或删除你锁看到的记录,当移动到下一行时,锁才会释放,记录就可以进行修改。

4)Repeatable Read(可重复读)

参考设置语句如下:

 SET   ISOLATION   TO   REPEATABLE   READ ;

数据库在读取的记录上加共享锁,验证是否可以读取数据,直到事务提交,锁才能释放,其他用户可以读取数据,但是不能修改。可以保证在同一事务中前后两次读取记录是一致的。当必须要信任所有读取的记录,保证记录不被修改,我们可以采用repeatable read,例如统计数据(银行账号的余额情况)、关联查询多个表等。

5)Last Committed Read(读取最后提交的数据)

这是一种乐观锁,它解决了Commited Read的不足,因为Commited Read在记录被锁时,其它进程需要等待。这种隔离级别常被用在WEB应用系统中,例如在电子商务系统中,可以选择商品添加到购物篮中,但是此时后台可能你正在对商品的价格进行更新,当再次检查的时候,可能发现商品的价格已经发生变化。

设置语句参考如下:

 SET   ISOLATION   TO   COMMITTED   READ  LAST  COMMITTED ;

相对Committed Read而言,提高了并发量和系统的吞吐量。可以通过ONCONFIG参数设置为隔离级别:USELASTCOMMITTED。表需要设置为行级锁,不能是页级锁。

6)几种隔离级别的比较

           

五、设置事务/会话的锁模式 

1 不等待锁的释放 

为默认的锁模式,如果数据库对象被锁,则立即返回错误。错误消息参考如下:

 - 244:Could not do a physical order read to fetch the  next row

 - 107:ISAM error:record is locked

设置为此种锁模式的参考语句如下:

 SET  LOCK MODE  TO   NOT  WAIT;

2 一直等待锁的释放

一个事务可能发生死锁或挂住,等待资源的释放。设置为此种模式的语句如下:

 SET  LOCK MODE  TO  WAIT;

3 在锁释放前,等待n秒

直到等待n秒后,如果锁一直没有释放将返回锁等待超时错误信息。参考设置语句如下:

 SET  LOCK MODE  TO  WAIT  20 ;

下一章:锁的监控与解锁