数据库中的锁

数据库中有锁机制,有一些概念,如共享锁,排它锁,意向锁,悲观锁,乐观锁等。

共享锁

一个事务获得了共享锁之后,就可以对锁定范围内的数据进行读操作。其他事务同时也能够获得锁定范围内的共享锁
MYSQL中共享锁的锁粒度是行或者元组(多行)

排它锁

一个事务获得排他锁之后,就可以对锁定范围内的数据执行insert/delete/update操作。其他事务不能获得锁定范围内的任何锁(共享和排它都不能获得)
锁粒度和共享锁一样。

意向锁

意向锁是一种表级锁,锁的粒度是整张表。分为意向共享锁(IS)意向排他锁(IX)

那么意向锁是从何而来的呢?
因为表锁覆盖了行锁的数据,所以表锁和行锁也会产成冲突。

为了方便地检测表级锁和行级锁之间的冲突,于是引入了意向锁

例如一个事务1对表的某一行加了共享行锁,用来读取这一行的数据;于此同时,事务2想要修改表结构,想给整个表加上排它的表锁,那么事务2查看表锁没有被锁住,还需要查看每一行是否有行锁,效率很低下。事务2发现有行锁,阻塞等待。

加入意向锁后,在意向锁存在的情况下,事务1必须先申请表的**意向共享锁,成功后再申请一行的行锁。

悲观锁

它可以阻止一个事务以影响其他用户的方式来修改数据。如果一个事务执行的操作对某行数据应用了锁,那只有当这个事务把锁释放,其他事务才能够执行与该锁冲突的操作。
悲观并发控制主要用于数据争用激烈的环境,以及发生并发冲突时使用锁保护数据的成本要低于回滚事务的成本的环境中

加锁流程:

  1. 在对任意记录进行修改前,先尝试为该记录加上排他锁(exclusive locking)。

  2. 如果加锁失败,说明该记录正在被修改,那么当前查询可能要等待或者抛出异常。具体响应方式由开发者根据实际需要决定。

  3. 如果成功加锁,那么就可以对记录做修改,事务完成后就会解锁了。

  4. 其间如果有其他对该记录做修改或加排他锁的操作,都会等待我们解锁或直接抛出异常。

1
2
3
4
5
6
7
8
9
10
//0.开始事务
begin;/begin work;/start transaction; (三者选一就可以)
//1.查询出商品信息
select status from t_goods where id=1 for update;
//2.根据商品信息生成订单
insert into t_orders (id,goods_id) values (null,1);
//3.修改商品status为2
update t_goods set status=2;
//4.提交事务
commit;/commit work;

select…for update的方式,这样就通过开启排他锁的方式实现了悲观锁.注意,InnoDB中,加行锁必须要有索引才行,否则锁表。

悲观并发控制实际上是“先取锁再访问”的保守策略,为数据处理的安全提供了保证。但是在效率方面,处理加锁的机制会让数据库产生额外的开销,还有增加产生死锁的机会;另外,在只读型事务处理中由于不会产生冲突,也没必要使用锁,这样做只能增加系统负载;还有会降低了并行性,一个事务如果锁定了某行数据,其他事务就必须等待该事务处理完才可以处理那行数

乐观锁

它假设多用户并发的事务在处理时不会彼此互相影响,各事务能够在不产生锁的情况下处理各自影响的那部分数据。在提交数据更新之前,每个事务会先检查在该事务读取数据后,有没有其他事务又修改了该数据。如果其他事务有更新的话,正在提交的事务会进行回滚。

一般的实现乐观锁的方式就是记录数据版本。实现数据版本有两种方式,第一种是使用版本号,第二种是使用时间戳

乐观并发控制相信事务之间的数据竞争(data race)的概率是比较小的,因此尽可能直接做下去,直到提交的时候才去锁定,所以不会产生任何锁和死锁。但如果直接简单这么做,还是有可能会遇到不可预期的结果,例如两个事务都读取了数据库的某一行,经过修改以后写回数据库,这时就遇到了问题。