点击上方蓝字关注我们
相信看完mysql前面部分的文章,有了个清晰的认识,但是还是少考虑了一些东西,高并发的时候,数据库是怎么样工作的,对于大部分程序员来说,高并发的时候第一个想到的是加锁,数据库同样也是利用锁机制来实现并发访问。
面试点:mysql你知道有哪些锁?
一张图了解锁
🔽

▪锁的粒度
a. 表级锁:开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般。
例子:例如在回家的火车上,你在卧铺车厢想去硬座车厢上厕所,表级锁意味着去往硬座车厢的门关着了,不能进入了。
b.页级锁:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。
例子:例如在回家的火车上,你在卧铺车厢想去硬座车厢上厕所,页级锁意味着去往硬座车厢的终于开了,可是这面的厕所也有一个锁,只能去对面。
c.行级锁:开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低
例子:例如在回家的火车上,你在卧铺车厢想去硬座车厢上厕所,页级锁意味着去往硬座车厢的终于开了,可是这面的厕所都有人了,只能去对面。去对面发现有两个锁。每个锁代表一个行锁。
对于不同的存储引擎拥有锁级的粒度不一样,以及实现也不一样.
▪MyISAM表级锁
MyISAM只有表级锁,表级模式包括:
a.表共享读锁 (Table Read Lock):不会阻塞其他用户对同一表的读请求,但会阻塞对同一表的写请求;
b.表独占写锁 (Table Write Lock):会阻塞其他用户对同一表的读和写操作;
▪ InnoDB行级锁和表级锁
InnoDB 实现了以下两种类型的行锁:
♦共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
♦排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

首先想一个问题:当我想给一个表加一个排他锁,这时候都需要满足什么条件?
1, 表的排他锁和行的排他锁不能共存,有个事务A对行数据加了排他锁想要修改,事务B对表加排他锁可以修改表中每一行数据这样就形成了冲突。
2, 如果行有排他锁,我想加表的排他锁需要每一行检查是否存在排他锁,会影响性能。
这时出现了意向锁。看看意向锁是如何解决问题的?
▪InnoDB 实现了以下类型表锁
♦意向锁
· 意向共享锁(IS):事务有意向对表中的某些行加共享锁(S锁)。
-- 事务要获取某些行的 S 锁,必须先获得表的 IS 锁。
SELECT column FROM table ... LOCK IN SHARE MODE;
· 意向排他锁(IX)事务有意向对表中的某些行加排他锁(X锁)
-- 事务要获取某些行的 X 锁,必须先获得表的 IX 锁。
SELECT column FROM table ... FOR UPDATE;

通过上述图表看到意向锁之间是兼容的,只有意向共享锁和普通共享锁是兼容的。
注意:这里的排他 共享锁指的都是表锁!!!意向锁不会与行级的共享 排他锁互斥!!!
现在我们回到刚才 users 表的例子:
事务 T1 获取了某一行的排他锁,并未提交:
SELECT column FROM table WHERE id = 1 FOR UPDATE;
复制代码
此时 table表存在两把锁:table表上的意向排他锁与 id 为 1 的数据行上的排他锁。
事务 T2 想要获取 table表的共享锁:
LOCK TABLES table READ;
复制代码
此时事务 T2 检测事务 T1 持有 table 表的意向排他锁,就可以得知事务 T1 必然持有该表中某些数据行的排他锁,那么事务 T2 对 table 表的加锁请求就会被排斥(阻塞),而无需去检测表中的每一行数据是否存在排他锁。大大的提高了性能。从火车车厢这面就看到了是否锁着了,无需一个一个看锁着没有。
意向锁的并发性
面试点:意向锁有什么作用?
我们最后看看意向锁存在的意义,是如何提高并发的,如果你看完下面的例子你就会明白的。
首先我们准备一个简单的表 student 表的例子来概括一下意向锁的意义(减少锁的范围,让更小范围进行加锁,即保证数据的一致性,又增加并发):

事务 A 先获取了某一行的排他锁,并未提交:
SELECT * FROM student WHERE id = 3 FOR UPDATE;
复制代码
1. 事务 A 获取 id 为 3 ,史珍香的数据行上的排他锁。
2. 事务 A 获取了 student 表上的意向排他锁,存储引擎自动添加意向排它锁。
之后事务 B 想要获取 student表的共享锁:
LOCK TABLES student READ;
复制代码
1. 事务 B 检测到事务 A 持有 student表的意向排他锁。
2. 事务 B 对 student表的加锁请求被阻塞(排斥)。
最后事务 C 也想获取 student表中某一行的排他锁:
SELECT * FROM student WHERE id = 1 FOR UPDATE;
复制代码
1. 事务 C 申请 student表的意向排他锁。
2. 事务 C 检测到事务 A 持有 student表的意向排他锁。
3. 因为意向锁之间并不互斥,所以事务 C 获取到了 student表的意向排他锁。
4. 因为id 为 1 的数据行上不存在任何排他锁,最终事务 C 成功获取到了该数据行上的排他锁。
♦排他锁(X)
♦共享锁(S)
▪MVCC行级锁
上面的锁如果你都掌握可能面过了大部分公司,要想更深入那么你需要知道下面的问题?
如果一行数据有排它锁,这时有我只是想查看下数据,都不让,岂不是太残酷了。性能也太差了。这时会有一个方案能解决就是这行数据在上排它锁之前,先考出来一份,我们读取备份出来的数据不就行了么。这个就是一致性的非锁定读。
注意:由于事物的隔离级别不同,导致生成快照的时机不同,这样会出现读取的时候快照的版本不同。
一行记录可能有不止一个快照数据,一般称这种技术为行多版本技术。由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control,MCVCC)
▪行锁的三种算法
a.Record Lock :单个记录上的锁
锁总会锁住索引记录,锁住的时key。如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB会使用隐式的主键进行锁定。
b.Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
锁定索引记录间隙,确保索引记录的间隙不变
间隙锁时针对事务隔离级别为可重复读或以上级别而配的
Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读
c.Next-Key Lock:Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身
在Next-Key Lock 算法下,InnoDB对于行的查询都是采用这种锁定的算法。可以有效的防止幻读。
当查询的索引含有唯一属性时,InnoDB存储引擎会对Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。
当查询的索引为辅助索引时,默认使用Next-Key Locking技术进行加锁,锁定范围是前一个索引到后一个索引之间范围。
▪解决 Phantom Problem
在默认的事务隔离级别下,即REPEATABLE READ下,InnoDB存储引擎次采用Next-Key Locking机制来避免Phantom Problem(幻读问题).这点可能不同与其他的数据库,比如Oracle数据库,因为其可能需要在SERIALIZABLE的事务隔离级别下才能解决Phantom Problem。
Phantom Problem 是指在同一事务下,连续执行两次同样的SQL语句可能导致不同的结果,第二次SQL语句可能会返回之前不存在的行。
set session tx_isolation='read-committed'; 设置事务隔离级别为不可重复读
OS:您读到了这里,说明你已经耐得住烦躁了,如今沉下心来学习的人多。你可以识别下方二维码和山虎一起学习,一起进步。无论是工作问题还是生活问题,只要我知道的都可以帮助和你一起分析。
大家好,我是山虎,喜欢数学,编码,算法,股票,AI。经历过一次失败的创业。东西不要死记硬背,要做到自己真正的理解。年轻人就要折腾,年轻人就要折腾,年轻人就要折腾。原创不易,帮忙转发。
JAVA八股文
随时欢迎与我讨论各种问题


点个在看你最好看





