数据库锁机制及乐观锁,悲观锁的并发控制

1.数据库锁的种类mysql

  ① 共享锁程序员

    共享锁是在执行select操做时使用的锁机制.web

      共享锁与共享锁共存,即当一个事务正在对A表进行查询操做时,另外一个事务一样能够对A表进行查询操做,演示以下:sql

1 T1:  select * from A;(加共享锁A)
2 T2:  select * from A;(加共享锁B)
3 -- 此时T1,T2可同时执行

  ② 排它锁

    排它锁是在执行update,delete等对数据有修改操做时使用的锁.数据库

    排它锁不与排它锁以及共享锁共存,即当一个事务正在对A表进行更新操做时,会对操做的数据加上排它锁,其余事务没法访问更没法修改被加锁的数据行,演示以下:安全

1 T1: update A set a=1 where id<100;
2 T2: update A set a=2 where id>100;
3 T3: update A set a=3 where id=50;
4 T4: select * from A where id<50;

    当T1先到达时,会对id<100的数据加上排它锁,其余事务没法访问这些数据,因为T2访问的是id>100的数据,改数据未被加上排它锁,因此T2可以对id>100的数据加排它锁,session

    可是T3访问的数据已经被T1加上排它锁,除非T1执行完毕释放排它锁,T3才能访问id=50的数据.T4跟T3同理.并发

    排它锁的并发性低下,请看下面这个例子:spa

1 T1:update table set column1='hello' where id=10
2 T2:update table set column1='world' where id=20

    该例子有两种状况:code

    第一种状况:若是id是主键(有主键索引)或者有索引的一列,T1会一会儿找到id=10的那一列,并对该列加上排它锁.T2同理,这两个事务互不干涉,可以并发执行

    第二种状况:若是id是普通的列,且没有索引,那么当T1搜索到id=10这一列并对他加上排它锁以后,因为T2须要全表扫描找到id=20这一列,可是因为T1对id=10这一列加上了排它锁,T2没法访问,就会进入阻塞状态,试想若是T1是一个较大的事务,那么T2一直得不到想要的资源,严重影响用户体验,尤为是在web应用这种对程序的效率要求较高的应用.

    排它锁的并发性低下,可是更新锁可以比较好的解决这个问题.

  ③ 更新锁

     被加上更新锁的数据是可能被修改的数据.更新锁与排它锁及更新锁互斥,与共享锁共存.演示以下:

1 T1: select * from A;(加更新锁)
2 T2: select * from A;(加更新锁)
3 T3: select * from A;(加共享锁)
4 T4: update A set a=1;(加排它锁)

    当T1到达时,对A表加更新锁,而后T2到达,企图对A表加更新锁,可是发现表A已经被加上更新锁,其阻塞.而后T3到达,对A表加共享锁,因为共享锁与更新锁共存,因此T3可以加上共享锁并可以正常读取,而后T4到达,因为排它锁与更新锁互斥,因此T4阻塞.如下是另外一个例子:

1 T1:
2 select * from A;(加更新锁)
3 update set a=1 where id=1;
4 T2:
5 select * from A;(加共享锁)
6 update set a=1 where id=1;

    T1先到达执行查询操做,同时T2到达,T2开始查询操做,二者能够共存.

    第一种状况:假设T2查询操做先结束,准备执行更新操做对相应行上锁时,发现该表有了更新锁,因此该操做阻塞.当T1查询操做结束后,执行更新操做,更新锁自动升级为排它锁,T1更新结束以后,释放锁,T2开始更新.

    第二种状况:假设T1查询操做先结束,准备执行更新操做,更新锁升级为排它锁,因为排它锁与共享锁不一样存,T2的查询操做被阻塞,等T1的更新操做结束以后,T2才能继续操做.

  ④ 意向锁

     意向锁就是反应当前表是否有锁的锁机制.

     若是T1正在对A表的一部分数据进行更新操做(加了行级锁),这个时候另外一个事务T2须要对A表加表级锁,因为表若是存在行级锁以后没法再添加表级锁,它会对全表进行扫描判断表的每一行是否存在排它锁,这样效率低下.意向锁就会可以防止这种状况的发生,当A表有一行被加上了排它锁以后,A表会自动被加上意向排它锁,那么其余事务须要加表级锁的事务只须要判断A表是否有意向锁就好了.

1 T1: update A set a=1 where id=1;(加行级排它锁)
2 T2: update A set a=1 where id=1;(加表级排它锁)

    当T1执行更新操做时,对id=1这一行加上行级锁以后,数据库会自动对A表加上一个意向排它锁,当T2到达以后,T2会判断A表是否有意向排它锁,若是存在则阻塞,不存在则对A表加上表级排他锁.

     ⑤ 小tips

    在操做数据库时,数据库会自动为操做的数据添加合适的锁,这就是为何不须要懂得数据库的锁机制咱们依然能够愉快的写SQL.不一样的存储引擎支持的锁机制不一样,根据须要可选择不一样的存储引擎.

    程序员可手动对对象加锁,这里以我熟悉的mysql数据库为例:这里我使用的是mysql默认的innodb存储引擎,该引擎支持行级锁,表级锁.InnoDB行锁是经过给索引上的索引项加锁来实现的,若是不存在索引,则对整张表加锁.

    添加表级锁以下:      

1 LOCK table admin write; -- 对admin加表锁
2 unlock tables;-- 释放当前session具备的表锁

      对表添加了写锁以后,是没法对其余表进行操做的,全部事务必须在一开始获取本身须要的全部锁,获取了写锁以后能进行写与读操做,获取了读锁以后,只能进行读操做

     添加行级锁以下:

1 select * from admin for update; -- 得到排它锁

    更多关于mysql的锁机制请自行看官方文档.

2.乐观锁与悲观锁

  乐观锁与悲观锁是为了保持事务的隔离性以及数据库的一致性而被业内定义出来的一种手段,并不属于数据库锁机制,可是悲观锁是依赖于数据库的锁机制实现的.

    ① 乐观锁

     在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。它假设多用户并发的事务在处理时不会彼此互相影响,各事务可以在不产生锁的状况下处理各自影响的那部分数据。在提交数据更新以前,每一个事务会先检查在该事务读取数据后,有没有其余事务又修改了该数据。若是其余事务有更新的话,正在提交的事务会进行回滚。

    乐观锁认为其余事务都不会修改本身的正在操做的数据,因此不会使用数据锁机制(数据库锁机制没法关闭,这里说的不使用是不会像悲观锁那样显示加锁,悲观锁操做见第4点)..

    ② 悲观锁

    在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。它能够阻止一个事务以影响其余用户的方式来修改数据。若是一个事务执行的操做都某行数据应用了锁,那只有当这个事务把锁释放,其余事务才可以执行与该锁冲突的操做。
      悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中。

      悲观锁的实现主要依赖于数据库的锁机制,会显示的使用锁.

3.乐观锁并发控制

  乐观锁并发控制经常使用方法是使用一个版本号控制手段.即在表中添加一个版本号version字段,当有一个事务对该数据进行操做时会取得版本号,当事务对数据更新时会判断数据库中的version是否跟本身取得version一致,一致则说明该数据未被修改过,不然说明数据被修改过,更新操做失败.更新成功则对version字段加一.

T1: 
select * from A;
update A set a=1,version=version+1 where id=1 and version=vs;
T2:
select * from A;
update A set a=2,version=version+1 where id=1 and version=vs;
-- vs为事务取得的版本号

    假设T1,T2并发的对A表进行操做,T1,T2取得的version字段值相同,T1事务先执行完,T1执行完以后version被加一,T2在提交更新时,由于version字段已经被修改,因此更新失败.

    乐观锁不适用于更新操做较多的状况,若是更新操做较多,会出现不少更新失败的状况,须要大量回滚,从新操做,程序效率大幅度下降.可是他的查询并发度高.

4.悲观锁并发控制

  悲观锁并发控制主要经过对操做的数据加锁实现.下面这个例子是经过加排它锁实现:

select * from A where id=1 for update

  在操做A表示,对id=1的行加上行级锁,其余事务没法操做该数据.实现了悲观锁.

  悲观锁并发度低,每次都须要对表上锁,可是可以保证数据的安全.

相关文章
相关标签/搜索