数据库中的锁

在很多文章中都能见到不少“锁”,下面整理下常见的“锁”

1、按照并发策略分类

数据库中的并发控制主要分为三种,一是乐观锁,二是悲观锁,三是时间戳
乐观锁持有的是一种乐观的态度,认为一个用户读数据的时候,其他人不会修改它所读的那个数据,悲观锁则相反,所以悲观锁在读取数据的时候,会加上锁,防止其他人修改它所读的数据,或者说,当用户修改一个数据时,其他人不能读它正在修改的数据,只有当事务提交后,才会释放锁,其他用户才能访问该数据,基于这两种情况,又能分为“排他锁”(写锁)和”共享锁“(读锁)。
第三种并发策略就是加时间戳,也就是在数据库表中添加一列”时间戳“,每次读数据的时候,都会把这个字段也读进来,写回去时,都会把这个字段加1,当事务提交的时候,比对这个字段,如果比数据库中的值大,那么就可以写入。
悲观锁的使用需要数据库本身支持,即select…..for update操作
//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或者 LOCK IN SHARE MODE都是对记录添加悲观锁,注意select…..for update会根据你是否明确索引来绝对锁的颗粒度,如select status from t_goods where id=1 for update就会锁住那些被扫描的行,如果是where name=“小明”,name上无索引,那么就会锁住整个表
总结就是一句话,Select…..for update加的是行锁还是表锁,看是不是用了索引/主键,没有索引/主键就是表锁,否则就是行锁。

2、按照锁的颗粒度划分

MySql的锁按照颗粒度又可以划分为行锁和表锁和页级表
Innodb引擎支持行锁,但MyIsAM不支持行锁。MySql加行锁的语句有:
select……for update(加排他锁,X锁)
select……lock in share mode(加共享锁,S锁)
update语句,delete语句加排他锁,X锁

3、行锁按照隔离等级划分

行级锁在不同的隔离等级下可以分为不同种类
在读已提交级别下,行锁为记录锁(Record lock),就是锁住当前的记录
在可重复读级别下,存在幻读问题,所以为了解决幻读问题,可重复读级别下存在一种叫间隙锁的行锁,间隙锁锁住的是一个范围,假设有一张表id有3,4,6,7,如果有一个2
到8的间隙锁,那么这个范围内就不能存在第二个事务插入id为5的记录了,即不存在的键值也被锁住了,所以有时候这会导致业务错误。
Next-key LOCK是Record lock和gap lock的组合版,相当于范围有个闭区间,比如(2,5】也会锁住id为5的。
Gap key是左开右开,next-key lock左开右闭

4、MySql怎么加锁的

目前讨论的都是悲观锁(乐观锁并不是真的加锁),不同存储引擎加的锁都不一样,下面主要讨论innodb如何加锁
innodb加的锁默认都是next-key锁,有时候会退化到Record lock和Gap Lock
讨论比较简单常用的唯一索引情况(主键,聚集)

  • 主键索引(聚集索引)
    对于主键索引,因为是唯一的且没有回表操作,所以只会在这个索引上加锁,我们假设这个字段就是id
    id有1,5,10,15,20值

1.大于存在的记录,如where id> 15

会在id=20的记录上加(15,20]的next-key锁,会在(20,无穷]加next-key锁

2.大于等于存在的记录,如where id>=15

会在id=15的记录上加Record lock,加(15,20]的next-key锁,会在(20,无穷]加next-key锁

3.小于存在的记录,如where id<5

会在id=1的记录上加(无穷,1]加next-key lock,在id=5上加(1,5)的Gap lock

4.小于等于存在的记录,如where id<=5

会在id=1的记录上加(无穷,1]加next-key lock,在id=5上加(1,5】的next-key lock

5.小于不存在的记录,如where id <6

会在id=1的记录上加(无穷,1]加next-key lock,在id=5上加(1,5】的next-key lock,会在id=10的记录上添加(5,10)的gap lock

6.小于等于不存在的记录,如where id<=6,跟where id<6一样

  • 针对非唯一索引(二级索引)
    当我们用非唯一索引进行等值查询的时候,因为存在两个索引,一个是主键索引,一个是非唯一索引(二级索引),所以在加锁时,同时会对这两个索引都加锁,但是对主键索引加锁的时候,只有满足查询条件的记录才会对它们的主键索引加锁。
    我们假设age字段有一个索引,age允许存在重复,所以是非唯一索引
    假设age有19.21,22,20,39
    在分析二级索引时,我们要先按照二级索引排序,即19,20,21,22,39(对应ID为1,15,5,10,20)

1.等值查询存在的记录,如where age=22

会在age=22的记录上(也就是第一个22之前的第一个记录的值)加(21,22】的next-key锁,然后给age=22的记录上添加第二个锁,就是(22,39)的gap锁(也就是最后一个22之后的第一个age的值),!!!!这就说明gap的X锁并不排除其余的X锁,gap锁的目的是克服可重复读级别的幻读问题,然后又因为age=22存在,所以也会对age=22的主键索引添加Record 锁

2.等值查询不存在的记录,如where age=25

扫描的时候遇到不满足age=25的记录,即age=39,扫描到这里就说明不存在age=25,会在age=39的记录上添加(22,39)的gap锁,这个说明不能添加这个范围内的记录。

3.范围查询,如where age>=22

当扫描到age=22时,这是满足范围的第一条记录,在这个记录上添加(21,22】的next-key锁,在后面的二级索引上添加(上一条记录,当前记录】的next-key锁,然后在对应的主键索引上添加Record 锁

原文地址:http://www.cnblogs.com/spark-cc/p/16917014.html

发表评论

您的电子邮箱地址不会被公开。