一.全局锁介绍
2.如果在锁:
我们要去备份这三张表的数据的时候,我们的业务系统还在对这个数据库操作数据;
备份的时候一个表一个表的备份;
假如库存表正在备份的时候,正好有人下单,那么此时库存减一,紧接着生成订单,插入数据,然后插入订单日志。
此时库存还是原来的库存没有扣减。拿到的这份数据已经插入了订单,和订单日志
3.语法
Flush(先加全局锁),逻辑备份之前
mysqldunmp,执行数据备份 itcast指的是哪个数据库,itcast.sql(存到哪个数据库当中)
unlock(释放全局锁),逻辑备份之后
4.全局锁_一致性数据备份
备份使用mysqldump工具..
使用全局锁可以实现数据备份可以保证一致性和完整性..但粒度很大!
备份期间所有客户端不能执行写入操作
二、表级锁介绍
2.表级锁的分类:
表级锁_表锁的分类:
- 表共享读锁(read lock):读锁(read)
- 表独占写锁(wirte loc k):写锁 (write)
共享读锁的特点:
假设客户端1在这里加了一个读锁,那么锁住了之后,就可以读取数据,但是不能执行写入操作
客户端2也是一样
当释放掉就可以正常操作
当前加锁的客户端自己也不可以写!!!别的客户端执行写操作会进入阻塞!!
客户端一二无法进行写操作
当客户端一释放掉的时候,客户端二执行了写操作
独占写锁的特点:
假设客户端1加了写锁,加了写锁,其他客户端不能读也不能写,我当前客户端既能操作写也能读
当前加锁的客户端可以读,可以写.别的客户端不可以读,不可以写!会进入阻塞状态,等待加锁的客户端来解锁!
客户端二一直阻塞
当客户端一释放
客户端二开始执行查询操作
总结:
表级锁_
元数据<==>表结构<==>DDL(元数据可以简单地理解为表结构)
就是当还在执行事务的时候,还未提交事务,我们无法修改表的结构
SHARED_READ和SHARED_WRITE共享锁之间是兼容的!
但是一旦碰到EXCLUSIVE排他锁,就会进入阻塞!
演示:
两边执行select查询的时候就会自动加上元数据锁,兼容,我们可以查得到下图
此时我们在客户端二执行写操作的时候(DML也是能够执行的),当我们执行select,以及update语句的时候都会去加上对应的元数据锁,这两类都是共享锁,都是兼容的
如果再执行alter的话,那么就会处于阻塞:
客户端一,开启事务并执行查询语句
客户端二开启事务,修改表的结构,结果:此时客户端二处于阻塞状态,因为我们客户端一在执行查询语句的时候自动加上SHARE_READE(共享读锁);
那么客户端二执行alert语句的时候,我们是加上排他锁,这个锁与其他的锁都是互斥的,所以导致处于客户端阻塞状态,
得等到客户端一提交了事务后,才会去执行alter语句
理解:
在a事务中select语句会给表加上元数据读锁,事务b中insert update delete会加上元数据写锁.但是这种读锁和写锁是兼容的!
但是在别的事务中执行alter等语句时,会加'排他锁',这个锁和读锁还有写锁是排斥的.
所以如果当前有别的事务没提交,当前alter语句会被阻塞!
metadata_lock
表级锁_意向锁
线程A:开启事务,然后执行update语句,默认隔离级别根据主键更新默认加上行锁
线程B:想要直接锁表(加锁)(读锁或者写锁),加上可能导致表锁和行锁产生冲突,那么当它去锁表(执行sql)的时候,先去判断这张表有没有加行锁,以及根据行锁的类型确定看能不能加锁成功(第一条记录开始检查有没有加行锁,什么类型的行锁,检查到最后一行)
要想加上表锁,就必须一行一行检查,性能极低
所以引入意向锁(解决在执行DML语句的行锁以及表锁的冲入突)
使用意向锁来减少表锁的检查,提高性能
意向锁:
线程A:照样步骤,在执行update的语句先加上这行的行锁随之整张表加上意向锁
线程B: 要来加上表锁,先检查这张表意向锁的情况,通过意向锁的情况来判断能不能加锁成功,如果能够兼容就加上表锁,否则处于阻塞状态,等待线程A事务提交,释放掉行锁,意向锁,随之接触阻塞状态,拿到表锁
引入意向锁无需逐行检查行锁情况,只需根据意向锁的类型以及情况来添加
过程:
之前是事务要加表锁的时候需要逐行检查当前表中是否有行锁,以避免所冲突,但效率太低.
引入了意向锁后,别的事务加表锁的时候就不需要进行逐行检查是否加行锁了.直接根据加行锁时给表也加的
意向锁的类型和兼容性来决定,是否可以加表锁.
IS:
IX:
意向锁的兼容情况:
演示:
意向共享锁与表锁的兼容情况:
(1)事务a
(2)事务b
意向排他锁和表锁的兼容情况:
(1)事务a中:
(2)事务b中:
演示二:
线程A:
线程B:
加表锁的时候会去先判断这张表意向锁的情况
加读锁(read):成功
加上写锁:处于阻塞状态
提交事务
成功锁住
此时线程A锁释放
---------------------------------------------------------------------------------------------------------------------
线程A:
线程B:
读锁阻塞
加写锁,写锁也阻塞
提交事务,
线程A释放,
线程B成功锁住
三、行级锁
每次锁的是行数据,粒度最小.发生锁冲突最低.并发性最高.只支持InnoDB
InnoDB存储引擎的数据是基于索引组织形成的,行锁是对索引上的索引项加锁实现的.而不是对记录加的锁.
对索引项ID为34的记录加上行级锁
总结:
间隙锁的唯一目的就是防止其他事务插入间隙,造成幻读现象!
(对于唯一索引来说,约束就会限制住重复数据出现,所以直接就避免幻读)
注意:
兼容情况:
注意: 根据唯一索引select 和 根据普通索引select,加锁的情况是不一样的!!!
默认情况下,InnoDB会在RR隔离级别下,用临键锁来搜索,检索...防止幻读..(RC中只有行锁,没有间隙锁)
记住这里是通过唯一索引(技巧)
select * from city where id = ? lock in ... ====> 会对行记录加上共享读锁,且为表加上意向共享锁
客户二正常执行sql查询语句id=1
客户端一
那么客户端二查询是否存在锁 S(共享锁),REC_NOT_GAP(无间隙)
同时客户端二执行同样操作
当事务提交时,锁被释放
--------------------------------------------------------------
共享锁与排他锁互不兼容,针对行级记住,不要与前面学的锁搞混
排他锁与排他锁是否存在相互兼容????
客户端一:开启事务执行修改操作(DML),开启了排他锁
客户端二:开启事务执行同样的操作
结果:一直处于阻塞状态,必须等待左侧客户端一释放掉排他锁
排他锁与排他锁之间是相互排斥(两个窗口同时对ID为2的用户执行修改操作)
牢记:
客户端一:name没有加索引,全表的记录加锁(表锁)
客户端二:
注意:如果给name建索引的话,此时两个客户端之间不会互相影响
客户端一:update ... where id = 5; 此时存在的记录是id 3, 8. 于是会给(3, 8)开区间加间隙锁(锁住的是3和8之间的间隙)(防止幻读) + 意向排他锁
客户端二:
此时处于阻塞状态,必须得等客户端一提交事务
间隙锁的唯一目的就是防止其他事务插入间隙,造成幻读现象(多个事务并发问题)!
(对于唯一索引来说,约束就会限制住重复数据出现,所以直接就避免幻读)
假设表中有索引列 id
,现有数据 id = 1
、id = 3
。
- 事务 A 执行
SELECT * FROM table WHERE id BETWEEN 1 AND 3 FOR UPDATE;
- 此时,间隙锁会锁定 (1,3) 这个间隙(即不包含 1 和 3 的区间),防止其他事务插入
id=2
。 - 若其他事务尝试插入
id=2
,会被阻塞,直到事务 A 提交或回滚。
重点:
在唯一索引下的等值匹配,给已存在的记录加锁.临键锁会优化为行级锁,因为唯一性约束就直接避免了幻读.因为当前记录就不允许重复插入,所以避免了幻读.
在唯一索引下的等值匹配,给不存在的记录加锁.会优化为间隙锁,避免别的事务在当前不存在的记录的这个间隙之间插入数据!避免了幻读的问题!
在非唯一索引下的等值匹配,给存在的记录加锁.会加三把锁: 当前记录 + 当前记录之前的间隙 + 当前记录之后的间隙
为什么要锁间隙呢?? 因为防止别的事务在当前间隙下插入数据造成幻读现象
InnoDB下RR隔离级别下,默认采用临键锁来对数据进行检索.在不同场景下,有不同的优化.来防止幻读.
总之有的是因为唯一约束间接避免了幻读, 其他(非唯一)都是通过要加间隙的方式防止别的事务进行插入数据!
为什么不走索引/索引失效会造成表锁升级为行锁?
- 加锁是对索引项加锁,而不是记录本身.所以不走索引的话,需要全表扫描,那就只能对表加锁了!
- 注意,RC隔离级别下不会表升级.只有RR才会.RC下只有行级锁...
如何选择RC和RR?
(1)RC的虽然可能对数据的一致性会低一些,但是RC这种隔离级别的性能是很高的(多条读是不需要加事务的).对性能并发更好一些...
(2)如果是比如希望做一些报表,要求数据一致性要求比较高.且对性能要求没那么高,选择RR.
MVCC(快照读) + 锁机制(当前读)阐述:
锁机制方面: 行级锁 间隙锁 临键锁(左闭右开)
RC仅仅支持行级锁,因为它允许发生不可重复读和幻读
RR不仅支持行级锁,还支持间隙锁 + 临键锁的方式来解决幻读问题。所以更可能发生死锁问题
条件列未使用到索引,RR锁表(select也会),RC锁行