美文网首页
数据库锁相关

数据库锁相关

作者: 瓢鳍小虾虎 | 来源:发表于2021-02-03 18:56 被阅读0次

MVCC

MVCC,即Multi-Version Concurrency Control, 多版本并发控制,是伴随着事务的需求而产生的设计思想,主要用来支持事务的并发操作、事务回滚等特性。

innoDB的MVCC实现是基于undoLog,通过表记录的隐藏字段实现多版本控制。
1)表隐藏字段:
3部分,最后事务id(DB_TRX_ID)6字节,回滚段(DB_ROLL_PTR)7字节,递增记录id(DB_ROW_ID)6字节,再加上一个记录删除delete标记。
2)undoLog
insert类型的操作undoLog会记录undo_no,table id,数据唯一键的信息,事务id。
update类型的操作undoLog会记录undo_no,table id,数据唯一键的信息,字段修改前的值,旧记录事务id,事务id。

mysql的锁

mysql的锁分为行级锁和表级锁。

  1. 行级锁:innoDB引擎实现。分为共享锁(读锁,S锁)和排它锁(写锁,X锁,独占锁)。

共享锁的特点是多个事务可以同时加读锁,但是不能加写锁。

显示加读锁:
select ... lock in share mode;

隐式加读锁:下面这种情况比较隐蔽,table_b会被加读锁,确切的说是间隙锁,所谓间隙锁就是锁住id为0到当前查询的最后一条记录的id的范围,不管这个范围中实际上有没有记录。由于select * 没有具体条件,这里innoDB会认为范围无限大,就变成table_b整个表都锁住了。
insert into table_a select * from table_b;

排他锁的特点是其他事务不能读也不能写。

update/delete/select ... for update;

关于间隙锁单记录锁

单记录锁就是锁定一行数据的锁。
innoDB对间隙锁的具体实现是使用了next-key锁
next-key锁是单记录锁和间隙锁的组合用法,锁定的是一个范围。具体会锁住查询过程中所有遍历过的数据和数据之间的间隙
next-key的查询规则具体会一直遍历找到需要查询的数据,如果查询条件是未加索引的字段,则会全表扫描,导致全表被被锁。
select * from table_a where pay = 3 for update; // pay没加索引导致全表被加了锁
特例
1)使用唯一特性的字段查询不会使用间隙锁。插入的时候包含唯一字段也会转为单记录锁。
2)如果事务隔离级别降级为读已提交(read commited,RC)则间隙锁失效。

插入意向锁
insert操作的时候加的锁。插入意向锁也是一种间隙锁,具体是指在插入之前先设置间隙锁
一个典型的例子就是当前一个事务使用非主键作为条件update操作的时候,后一个事务insert会被阻塞,影响到了并发效率。如果两个事务都先不通过索引update然后insert就会出现死锁。

总之,innoDB加锁默认都是使用next-key锁,锁定的是一个范围,如果sql写法不注意,就会锁住意料之外的行,影响到并发事务效率,甚至死锁。所以我们更新和查询都要尽量使用主键和索引。

Predicate Lock
因为next-key锁对多维空间列的支持不好,innoDB又提供了专门的Predicate Lock。大致思想就是通过锁查询条件而不是锁记录来达到目的。

  1. 表级锁:表级锁是由mysql服务实现的。

读锁(LOCK_S锁):
lock table tablename read;
写锁(LOCK_X锁):
lock table tablename write;

通常上述方式一般不会在生产环境使用。因为加上去了除了使用写琐(手动释放),加了LOCK_X锁,表数据读写操作就全部禁止了。加表锁的时候也并不知道有没有行级锁的事务操作。

为了解决这种场景,让表级锁和行级锁共存,mysql还特意提供了意向共享锁(IS)意向互斥锁(IX),类似于注册,这样加表锁的时候就不用全表扫描是否当前表有事务加行级锁了。

意向共享锁(IS)
事务想获得行级共享锁,需要先在表上加意向共享锁

意向互斥锁(IX)
事务想获得行级互斥锁,需要先在表上加意向互斥锁

X(表写锁)S(表读锁)IX(意向互斥锁)IS(意向共享锁)

表级加锁总的来看还是比较繁琐,表最好还是设计的时候索引字段什么的考虑的全面点。

表的自增锁

这是一种特殊的表级锁,自动模式(innoDB默认选项)中,自增序列在执行sql的时候就计算好了行数(如果是未知行数,则自动转为传统模式--加表级锁,insert结束后释放),然后释放掉,自增过程并不受事务控制,2个并发的事务中自增序列也会依次排列,不会出现冲突,但是如果前一个事务rollback,则那个事务加的数据对应自增序列就会丢掉,导致整个表的自增序列出现不连续。

innoDB对事务模型的支持

读未提交:直接读行记录,不管隐藏字段有什么信息。

读已提交:读行记录,如果发现最新记录的事务id未提交,则根据回滚段找到undo日志,去读取上一个版本的数据。

可重复读(mysql默认事务隔离级别):只关心自己事务id对应的数据,如果发现当前记录的事务版本不对,则通过回滚段找到undoLog去读取跟自己事务版本一致的数据。
select读取的时候会先生成一个read-view快照,这个快照只存放当前事务id版本和以前版本的记录,事务过程中就算有别的事务update提交了,也不会影响到快照。通过这种方式实现了可重复读。

快照只适用于select读的事务,如果是update操作则会实时读最新记录,从而数据结果集不一致,解决办法是先加间隙锁select...for update,这样事务结束之前其他事务想操作是成功不了的。通常幻读并不会影响数据update数据,所以我们通常可以忍受,这时候就要考虑一定要利用id或者索引,减少不必要的阻塞。

串行化:串行化的原理其实还是利用了间隙锁,默认加了读锁,自然写锁就申请不到只能等着。所谓串行化并不是严格事务部分情况必须一个一个串行执行。

几个案例

  1. 多事务并发insert导致的死锁现象

1)3事务同时insert记录的时候,假设都包含一个唯一字段并且同名。

2)因为唯一键,只会使用单记录锁,并且先加读锁去查询有没有,然后才能加写锁插入。

3)第一个事务执行中途callback了,此时事务2和事务3都加了读锁(读锁不互斥),但是没办法加写锁,因为需互相等对方释放读锁,但是读锁的释放需要写操作完成即写锁释放。死锁产生。

如果第一个事务成功提交,则不会出现死锁,因为后面的事务读操作就会发现唯一键冲突。
mysql针对这种情况会主动让一个事务报错,退出事务,这时另一个事务就能操作成功。

相关文章

  • MS汇总

    数据库相关[MS-关于锁(乐观锁,悲观锁,行锁、表锁,共享锁,排他锁)Mysql索引优化Mysql查询优化Mysq...

  • mysql锁

    1. 相关概念 按锁的粒度分: 表锁、页锁、行锁 行锁是锁表粒度最小、最细的一种锁,能大大减少数据库冲突的概率,当...

  • 基础2 事务与锁

    最初,接触到事务中的锁概念是从数据库原理开始的。其中,锁相关的类型概念也比较多,如共享锁,排他锁,意向锁,悲观锁,...

  • 浅析mysql的锁

    目录:1.锁的定义与分类(表、行、页)2.锁相关的语句(查看锁)3.mysql事务4.乐观锁和悲观锁5.数据库死锁...

  • mysql进阶-行级锁、表级锁、乐观锁、悲观锁

    从应用的角度来看数据库锁可分为悲观锁、乐观锁从数据库(InnoDB)的角度看,数据库锁可以分为行级锁和表级锁 1....

  • 两端锁协议

    1、数据库锁 数据库锁粒度划分:行锁、页锁、表锁。共享锁:读锁、S锁。事务T可以对A进行读取,其他事务只能读取而不...

  • MySQL05

    数据库高级对象,锁,权限管理 视图 索引 触发器 存储过程 悲观锁与乐观锁 行级锁、表级锁、页锁 数据库权限管理 ...

  • 数据库如何加锁?锁是用来干嘛的?

    数据库中的共享锁与排它锁 共享锁(S锁),又称为读锁,如果数据对象加上共享锁之后,则该数据库对象可以被其他事务查看...

  • 秒杀系统技术方案演变过程

    前言:秒杀系统需要保证商品库存不能出现超卖现象。一、数据库锁机制(悲观锁、乐观锁)实现秒杀(1)悲观锁:数据库本身...

  • mysql全局锁和表锁

    MySQL中的锁可以分为三类:全局锁,表级锁和行锁。 全局锁 全局锁是对整个数据库加锁,可以让整个数据库处于只读状...

网友评论

      本文标题:数据库锁相关

      本文链接:https://www.haomeiwen.com/subject/rlrstltx.html