数据库锁机制

来源:互联网 发布:淘宝小商品 编辑:程序博客网 时间:2024/06/06 16:46
主要就是两种锁,共享锁和排他锁(也叫独占锁)。在执行select语句的时候需要给操作对象(表或者一些记录)加上共享锁,但加锁之前需要检查是否有排他锁,如果没有,则可以加共享锁(一个对象上可以加n个共享锁),否则不行。共享锁通常在执行完select语句之后被释放,当然也有可能是在事务结束(包括正常结束和异常结束)的时候被释放,主要取决与数据库所设置的事务隔离级别。   
      执行insert、update、delete语句的时候需要给操作的对象加排他锁(我感觉在执行insert的时候应该是在表级加排他锁),在加排他锁之前必须确认该对象上没有其他任何锁,一旦加上排他锁之后,就不能再给这个对象加其他任何锁。排他锁的释放通常是在事务结束的时候(当然也有例外,就是在数据库事务隔离级别被设置成Read Uncommitted(读未提交数据)的时候,这种情况下排他锁会在执行完更新操作之后就释放,而不是在事务结束的时候)。   
      数据库是支持在一个事务中进行自动锁升级的,例如,在某个事务中先执行select语句,后执行update语句,这两条语句操作了同一个对象,并且假定共享锁是在事务结束的时候被释放的。如果数据库不支持自动锁升级,那么当update语句请求排他锁的时候将不能成功。因为之前select语句的共享锁没有被释放,那么事务就进入了无限等待,即死锁。有了自动锁升级,在执行update语句的时候就可以将之前加的共享锁升级为排他锁,但有个前提,就是这个共享锁必须是本事务自己加的,而且在操作对象上没有在加其他任何锁,否则共享锁是不能被升级为排他锁的,必须等待其他锁的释放。   
      在看《精通Hibernate》的时候,里面还提到了更新锁。因为通常在执行更新操作的时候要先查询,也就是我们通常会在update语句和delete语句中加where子句。那么,有的数据库系统可能会在执行查询的时候先给操作对象加共享锁,然后在更新的时候加排他锁,但这么做会有问题,也就是如果两个事务同时要更新一个对象,都先给这个对象加了共享锁,当要更新的时候,都请求升级锁,但由于这个对象上存在对方事务加的共享锁。。所以无法升级。这样两个事务就在等待对方释放共享锁,进入死锁状态。更新锁就是为了解决这个问题,即在执行查询操作的时候加的不是共享锁而是更新锁(一个对象上只能有一个更新锁和n个共享锁),当要更新的时候,再将更新锁升级为排他锁,升级前提是这个对象上只有本事务加的更新锁,没有其他任何锁了。其实,,我想,如果在执行查询的时候就给事务加排他锁不也能解决死锁问题吗,但这样似乎会减弱系统的并发性能。   
      现在说说数据库的事务隔离级别。   
      在《精通Hibernate》中提到了4种数据库事务隔离级别: Read Uncommitted:读未提交数据(这个通常很少用)   使用这种隔离级别并不是说在读取或者更新数据的时候不加锁。。其实还是加锁的,只是在执行完操作之后马上释放锁。而不是等到事务结束之后再释放。对于共享锁,排他锁都是这样。
      Read Committed:读已提交数据(这个很常用,也常是数据库默认的设置)   这种隔离级别共享锁在读取数据之后马上释放。。而排他锁则是在事务结束的时候再释放。

      Repeatable Read:可重复读   这种隔离级别共享锁和排他锁都是在事务结束的时候再释放的。因此叫“可重复读”,即一个事务所读取的数据是不会被别的事务更新的。。。在事务执行过程中任何时候都可以读取刚才读过的数据。读过的数据是不会被改变的,直到事务结束。
      Serializable:串行化   共享锁和排他锁也是在事务结束的时候被释放,和Repeatable Read不同的是,通常,Repeatable Read。。在读取数据的时候。。都是对一条条记录加共享锁的。而Serializable的共享锁则是对整个表加的,这样不但读取的数据不会被别的事务修改。。。在同一个表中的其他未被读取的数据也不会被修改,,甚至不用担心读到新加入这个表中的数据(根本无法往表中加数据),所以这种隔离级别是不会出现虚读的。

事务(Transaction)

  数据库的事务是数据库并发控制的基本单位,一组操作的集合、序列。要么都执行,要么都不执行,是一个不可分割的整体。比如银行的转账,钱从一个账户转移到另一个账户,账户A扣钱账户B加钱,要么都执行,要么都不执行。不可能A扣了钱B没有加钱,也不可能A没扣钱B却加了钱。

  数据库的事务应当具有以下四种特性:

  Atomic(原子性)

  事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。

  Consistency(一致性)

  只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。

  Isolation(隔离性)

  事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。

  同时,并行事务的修改必须与其他并行事务的修改相互独立。

  事务的隔离性一般由事务的来进行控制。

  Durability(持久性)

  事务结束后,事务处理的结果必须能够得到固化。

  数据库的事务和程序的线程有相似的地方:

  1.线程之间共享同一片资源,而事务共享的则是数据库内的数据。

  2.多线程的意义在于并发执行,提高效率;事务并发执行也能提高程序与数据库交互的效率。

  因此如何使用事务与事务相互之间的隔离级别,直接影响了数据库的并发性和数据的准确性。我们在设计事务和选择隔离级别时这些是应该要考虑的。

  选择完隔离级别与设计完事务之后,在使用过程中常常会遇到以下几种情况:

  1.更新丢失(Lost update):两个事务同时更新,但是第二个事务却中途失败退出,导致对数据的两个修改都失效了。

  2.脏读(Dirty Reads):一个事务开始读取了某行数据,但是另外一个事务已经更新了此数据但没有能够及时提交。这是相当危险的,因为很可能所有的操作都被回滚。

  3.不可重复读取(Non-repeatable Reads):一个事务两次读取,但在第二次读取前另一事务已经更新了。

  4.虚读(Phantom Reads):一个事务两次读取,第二次读取到了另一事务插入的数据。

  5.两次更新问题(Second lost updates problem):两个事务都读取了数据,并同时更新,第一个事务更新失败

原创粉丝点击