Innodb中的锁

         锁是计算机协调多个进程或多线程并发访问某一资源的同步机制,数据库设计到数据的安全性,所以锁的使用是不可避免的。   

1、锁分类

  • 悲观锁-Pessimistic lock
    • 全局锁:flush table with read lock;使用全局锁会锁住整个数据库,使其处于只读状态;
    • 表锁: lock table和 意向锁(Intention Locks) MataData Lock ,意向锁不用显示调用;
    • 行锁(Record-lock)
    • 间隙锁( gap locks)
    • 临键锁( next-key lock) ,由行锁和间隙锁组成;
  • 乐观锁- Optimistic Lock
    • 自旋cas机制;可通过version版本和时间戳来判断;

2、全局锁

2.1 全局锁: flush tables with lock:全局锁主要是对整个数据库的实例加锁,一个库被全局锁锁住后处于只读状态,以下动作将会被禁止:
  • DML:data manipulation language 对数据的增查改删更新操作;
  • DDL:   data definition language ,包括对表的修改,增加字段删除字段;
使用场景:全库逻辑备份。如果没有全局锁,那么系统备份得到的数据不是一个逻辑时间点的,这个视图的逻辑是不一致的,数据备份会出现问题。
 

3、表锁

3.1 lock tables 表锁

  • 加锁:lock table tableName  read/write;
  • 解锁:unlock table:
使用场景 在没有行锁这种粒度锁的时候,处理并发的时候就会使用表级锁;
lock table car write;
select * from car;
unlock tables;

特点:

  • (1). 可以主动unlock tables释放,在客户端断开连接的时候自动释放;
  • (2). lock tables除了限制别的线程的读写外,也限定了本线程接下来的操作对象;
例如线程执行:
lock tables t1 read, t2 write; //除了阻塞其他线程外,自己本身也只能读t1,读写t2,不能写t1.

3.2 MDL-意向锁

Mate Data Lock, 意向锁 -也称为 元数据锁 在mysql5.5版本中引入了MDL,引擎自己维护,用户不用手动操作。当对一个表做增查改删的时候+MDL读锁,当要对表结果做变更的时候,+MDL写锁。
  • 意向共享锁:如果一张表上面至少有一个意向共享锁,说明有其他的事务给其中的某些数据行加上了共享锁;
  • 意向排他锁:如果一张表上面至少有一个意向排他锁,说明有其他的事务给其中的某些数据行加上了排他锁;
作用:
(1)是防止DDL和MDL之间冲突,保证DDL-(数据定义语言)和DML-(操纵语言:增查改删)的数据一致性;
  • MDL的(增查改删)读锁和 DDL之间不互斥;
  • MDL的写锁和DDL的写锁修改表结构之间互斥;在对表结构进行修改时,加MDL锁,另外线程只能等待。
(2)提高加锁的效率;
  • 加x锁的前提是:表中没有任何锁,包括行锁的存在,意向锁避免了加行锁时检查锁状态的全表行锁扫描,先加意向锁后加x锁,它相当于一个标记,提高了加锁的效率;

4、行锁【Innodb】

4.1、行锁实现原理

  • 行锁是存储引擎innodb自己设计的,不是所有的存储引擎都支持,比如myisam就不支持;
  • 行锁是基于索引实现的,所以没有建立索引的更新将会扫描全表,使用的是表锁;通过锁住索引实现行及锁。
  • 如果select …for update没走索引,就会锁表,innodb内部是全表根据主键索引逐行扫描,逐行加锁,最后统一释放。
使用注意点没有索引或者索引失效时,InnoDB 的行锁变表锁。

4.2、两阶段锁协议

行锁是需要的时候加上的,不是不需要了就立即释放,在commit后才释放,释放时机如下:
  • 1. 执行commit语句或者rollback;
  • 2. 退出数据库;
  • 3. 客户端断开连接;

4.3、行锁的使用优化

由于行锁的等到事务提交后才释放,不是用完就释放,所以把有冲突的抢锁操作尽量放到事务操作的最后一步进行。如何理解: “尽量把临界资源的锁往后放”,实例如下:
实例场景:支付订单中一般流程较长,步骤较多,比如商品库存就是一临界资源,容易产生竞争,可以将扣减库存放在订单事务操作的最后一步,让事务在获取锁的等待时间尽量短,提高并发度。

5、行锁与索引的关系分析

从一条简单的SQL语句开始,分析索引对加锁的影响:
SQL: delete from Order where orderId = 33;

情况一在RC级别下,id为主键索引,orderId为普通索引,忽略订单ID重复的情况;

表结构如下:
CREATE TABLE `Order` (
  `id` int(11) NOT NULL,
  `orderId` bigint NOT NULL,
  `goods_name` varchar(45) DEFAULT NULL COMMENT '商品名称',
   PRIMARY KEY (`id`),
   KEY `orderId` (`orderId`)
) ENGINE=InnoDB

分析:此时加行锁会锁住对应条件的二级索引及主键索引的行记录;
情况二:在RC级别下,id为主键索引,orderId为唯一索引;
表结构如下:
CREATE TABLE `Order` (
  `id` int(11) NOT NULL,
  `orderId` bigint NOT NULL,
  `goods_name` varchar(45) DEFAULT NULL COMMENT '商品名称',
   PRIMARY KEY (`id`),
   UNIQUE KEY `orderId_UNIQUE` (`orderId`),
) ENGINE=InnoDB

分析:由于id是唯一索引时,where会走orderIde列的索引进行过滤,在找到orderId=33的记录后对唯一索引orderId=33的记录加X锁。
同时会回表查询主键索引orderId=33的数据,并对orderId=33的数据也加上X锁。此时依然加行锁,锁住唯一索引及主键索引对应的行记录
 
情况三:在RC级别下,只有id主键索引;
表结构如下:
CREATE TABLE `Order` (
  `id` int(11) NOT NULL,
  `orderId` bigint NOT NULL,
  `goods_name` varchar(45) DEFAULT NULL COMMENT '商品名称',
   PRIMARY KEY (`id`),
) ENGINE=InnoDB

分析:从上面分析的行锁的锁原理可知,因为没有索引,所以走的是全表扫描,此时没有行锁,会对主键索引上每一条记录施加X锁,会锁住整个的表数据;
思考: 为什么会对所有记录施加X锁,而不是表锁或者说符合条件的数据加X锁呢?
这是由于InnoDB的实现决定的,由于没有索引, 无法在存储引擎层过滤(执行计划里的Using Where),所以存储引擎对每一条数据加锁后返回给Sql Server进行过滤,因为Innodb的行锁是通过索引来实现的。

5.1、行锁使用的注意事项

  • (1)在不通过索引条件查询的时候,或没有索引的情况下,InnoDB只能使用表锁。
  • (2)由于MySQL的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。
  • (3)当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB都会使用行锁来对数据加锁。
  • (4)即便在条件中使用了索引字段,但是否使用索引来检索数据是由MySQL通过判断不同执行计划的代价来决定的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此,在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引。
 
对于在RR级别下,对数据的写操作不仅会锁住行还会锁住相邻值之间的间隙;也就是会出现 gap Next-key lock。

6、三种细粒度锁的实现

  • record lock-行锁:
  • gap lock-间隙锁:
  • Next-Key lock-临键锁:行锁和间隙锁共同实现的锁;

6.1、行锁Record lock

实现:通过索引实现,锁住一行记录,锁粒度最小,并发度高。
使用条件:等值查询;

6.2、使用间隙锁Gap Lock

使用条件:查询一个不存在的值,锁定就是该值左右的区间,就是间隙锁。
  • 间隙锁与间隙锁之间是不冲突的,跟间隙锁冲突的是: 往间隙之间插入一条数据的这个操作

6.3、临键锁 Next-key Lock

临键锁锁住的是什么?
使用条件:范围查询,包含等值和区间
  • 锁住的是下一个记录的左开右闭的区间;
  • 解决了RR级别下的数据幻读;

7、小结

Mysql的行锁和表锁区别:
  • 表级锁: 锁住整张表。    开销小,加锁快;不会出现死锁;锁粒度最大,发生锁冲突的概率最高,并发度低;
  • 行级锁: 锁住一行数据。开销大,加锁慢;容易出现死锁;锁粒度最小,发生锁冲突的概率最低,并发度高;
行锁的实现:
  • 如果有索引,那么会先扫描索引文件,查询到主键id,通过索引锁定行记录实现行锁;
  • 如果没有索引,就会锁住全表的数据;
行锁的出现大大提高了数据库的并发度,当然实现相对复杂,并且产生死锁的概率也提升了,使用时需要注意。

 

   OK---盛年不再来,一日难再晨。
 
 
水滴石穿,积少成多。学习笔记,内容简单,用于复习,梳理巩固。
 
##参考资料,
《Innodb存储引擎》
 
 
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值