首页 > 其他分享 >MVCC与锁

MVCC与锁

时间:2024-09-14 23:24:27浏览次数:10  
标签:事务 加锁 记录 trx 死锁 MVCC id

MVCC与锁

锁基本原理

当事务想要改动记录时,会查看内存中有没有跟该记录相关联的锁结构

  • 没有的话就生成一个is_waiting为false的锁结构与之关联,代表获取锁成功;
  • 如果发现该记录已经有锁关联了,会生成一个is_waiting为true的锁结构,代表获取锁失败,进入等待状态;
  • 如果加锁的事务结束,将释放锁结构,查看是否有其他事务正在等待,若有则将其锁的is_waiting改为false,并唤醒其事务对应线程唤醒

锁定读的语句:

  • SELECT ... LOCK IN SHARE MODE:事务执行该语句,则为读取到的记录加S锁
  • SELECT ... FOR UPDATE :事务执行该语句,则为读取到的记录加X锁

不同类型的锁

  • 共享锁:S锁,读取记录前需要获取s锁

  • 独占锁:X锁,改动记录前需要获取X锁

  • 意向共享锁:当事务准备在某条记录加上S锁,需要在表级别加一个IS锁

  • 意向独占锁:当事务准备在某条记录加上X锁,需要在表级别加一个IX锁

    意向锁让表快速判断表中记录是否被加行锁,为表是否能加表锁提供依据,所以IX和IX,IX和IS锁都是兼容的,因为它们并不用作互斥

不同粒度的锁

全局锁:对整个数据库实例加锁

典型应用场景是:做全库逻辑备份,目的是让备份系统备份得到的库和原库是保持逻辑一致性的,代价是如果在主库上备份,期间不能做数据更新,业务停摆;如果在从库上备份,备份期间从库不能进行主从同步,导致延迟

如果引擎(innoDB)支持一致性读,推荐使用single-transaction方法

但如果有的表使用了不支持事务的引擎,就需要对全库加读锁,有以下两种方式:

  • FTWRL:flush table with read lock
  • set global readonly = true

使用FTWRL更好,一是修改global的方式影响面更大;二是如果执行FTWRL之后客户端异常断开,则mysql会自动释放全局锁,整个库回到可以正常更新的状态,但readonly而不会因为异常而取消readonly状态

表级锁:针对表加锁

分为两种:表锁 和 元数据锁MDL

表锁:lock tables .. read/write;一般在数据引擎不支持行锁的时候才会用到

  • 可以使用unlock tables释放锁,或在客户端断开时自动释放
  • 会限制其他线程 和 本线程 的读写

MDL:不需要显式使用,在访问一个表的时候会被自动加上

  • 作用:保证读写的正确性,比如禁止一个查询正在遍历表中数据,而执行期间另一个线程修改了表结构这种操作
  • MySQL5.5引入MDL,对一个表做增删改查操作的时候,加 MDL 读锁;当要对表做结构变更操作的时候,加 MDL 写锁
    • 读锁不互斥,允许多个线程同时对一张表增删改查
    • 但读写锁与写锁间互斥,保证变更表结构操作的安全性

自增锁:用于AUTO_INCREATMENT修饰的列自动递增,作用范围是单个插入语句,执行插入语句时加一个AUTO_INC锁,语句结束后释放

行锁

在innoDB事务中,行锁在需要的时候加上,但等到事务结束时才释放

所以要把最可能造成锁冲突,最可能影响并发度的锁尽量往后放

Record Locks,类型为LOCK_REC_NOT_GAP,有X锁和S锁,作用就是锁一条记录

Gap Locks间隙锁:类型为LOCK_GAP,为解决幻读问题发明的,X锁和S锁没有差别,给一条记录加gap锁:其他事务不能在这条记录前面的间隙插入新纪录

可以通过给最后一条记录A所在页面的supremum记录(该页面中最大的记录)加gap锁,来阻止在A之后的间隙插入新记录

幻读:一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行(可重复读隔离级别下,幻读在“当前读”下才会出现)

产生幻读的一种原因:行锁只能锁住行,但新插入记录这个动作,更新的是记录的”间隙“,所以只好引入新的锁解决这个问题:间隙锁,它在可重复读隔离级别下才有效

跟间隙锁存在冲突关系的,是往这个间隙中插入一个记录这个操作,而间隙锁之间不存在冲突关系

间隙锁+行锁,合称为next-key lock,每个next-key lock都是前开后闭区间(间隙锁是开区间,加上一个行锁后 就变成前开后闭)

但间隙锁的引入,可能会导致同样的语句锁住更大的范围,影响并发度

间隙锁的加锁规则:

  1. 原则 1:加锁的基本单位是前开后闭区间的 next-key lock。
  2. 原则 2:查找过程中访问到的对象都会加锁。(范围查询会继续往后访问,访问到哪加锁到哪)
  3. 优化 1:索引上的等值查询,给唯一索引加锁的时候,next-key lock 退化为行锁。
  4. 优化 2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
  5. 一个 bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止

插入意向锁:如果在被gap锁锁住的区域想插入记录,该事务就会为gap锁锁住的记录加上插入意向锁,等待gap锁释放,它的作用仅限于此,不会阻止其他事务获取任何类型的锁

隐式锁:INSERT语句一般不加锁,但可以通过事务id,为新插入的记录加隐式锁

死锁和死锁检测

死锁:并发系统中不同线程出现循环资源依赖,导致这几个线程都进入无限等待的状态

出现死锁后,有两种策略:

  • 直接进入等待,直到超时,超时时间由:innodb_lock_wait_timeout决定,默认是50s,即当出现死锁后,第一个锁住的线程要过50s才会超时退出,后续的线程才有可能执行

  • 发起死锁检测,发现死锁后主动回滚死锁链条中的某个事务,让其他事务得以继续执行,将innodb_deadlock_detect设置为on开启

    • 负担:每当一个事务锁住时,都需要判断会不会由于自己的加入导致死锁,这是一个O(N)的操作
    • 如果能确保业务一定不会出现死锁,可以临时关闭死锁检测;

查看死锁:show engine innodb status里的LATESTDETECTED DEADLOCK 记录最后一次死锁信息

MVCC版本控制

当我们在改动一条记录时,该记录的隐藏列roll_pointer指向undo日志版本链的头节点,trx_id记录该版本链对应的事务id,这个版本链在MVCC多版本并发控制中发挥了很大的作用

对于READ UNCOMMITTED来说,脏读是允许发生的,所以每次读取数据时直接读取最新版本即可

而对于READ COMMITTED和REPEATABLE READ来说,需要用到Readview来帮助进行版本控制,保证每次满足不脏读或可重复读的需求

Readview:获取当前系统活跃(尚未commit)的事务id列表、min_trx_id(最小的事务id),max_trx_id(系统应该分配给下一个事务的id),creator_trx_id(生成该Readview事务的id)

不脏读即事务不能读取到其他未提交事务修改的数据,观察被访问记录当前版本的trx_id

  • trx_id == creator_trx_id:当前版本记录就是当前事务,可以访问该版本
  • trx_id < min_trx_id:小于当前活跃事务的最小id,说明该版本已经commit,可以访问
  • trx_id > max_trx_id:说明当前事务执行时,该版本还没有commit,不能访问
  • 如果max_trx_id > trx_id > min_trx_id:查看事务id列表里有没有当前记录版本事务id,没有就说明当前事务已经commit

就这样顺着版本链以此判断当前版本是否对当前事务可见,就像是在生成 ReadView 的那个时刻做了一 次时间静止(就像用相机拍了一个快照),查询语句只能读到在生成 ReadView 之前已提交事务所做的更改

而READ COMMITTED和REPEATABLE READ区别在于

  • 后者只会在第一次读取数据时生成Readview,这就使得它在后续判断版本可见性时用的都是最开始的Readview数据,所以即使在两次读取数据之间,有其他事务commit了,对于当前事务来说,那些事务commit的数据版本依旧是不可见的,这就实现了可重复读的需求;
  • 前者则会每次读取数据时,都生成一个新的Readview

事务利用MVCC进行的读取操作叫做:一致性读、一致性无锁读、快照读

所有普通的select语句在READ COMMITTED和REPEATABLE READ下都是一致性读,不会对记录做任何加锁操作,其他事务可以自由改动

标签:事务,加锁,记录,trx,死锁,MVCC,id
From: https://www.cnblogs.com/pinoky/p/18414843

相关文章

  • 什么是MVCC
    MVCC(Multi-VersionConcurrencyControl,多版本并发控制)是一种数据库管理系统中用于实现并发控制的技术。它通过在数据库中为每个数据行保存多个版本,使得不同的事务可以看到不同版本的数据,从而避免了并发事务之间的冲突,提高了数据库的并发性能。一、基本原理版本存储:MVCC为......
  • SpringBoot项目常用配置文件MybatisPlusConfig、RedisConfig、RedissonConfig、Swagge
    MybatisPlusConfig:@Configuration@MapperScan("com.yupi.usercenter.mapper")publicclassMybatisPlusConfig{@BeanpublicMybatisPlusInterceptormybatisPlusInterceptor(){MybatisPlusInterceptorinterceptor=newMybatisPlusInterc......
  • MVCC详解,深入浅出简单易懂
    转载自https://blog.csdn.net/lans_g/article/details/124232192一、什么是MVCC?mvcc,也就是多版本并发控制,是为了在读取数据时不加锁来提高读取效率和并发性的一种手段。数据库并发有以下几种场景:读-读:不存在任何问题。读-写:有线程安全问题,可能出现脏读、幻读、不可重复读......
  • MVCC详解
    1.概念1.1什么是MVCCMVCC,全称Multi-VersionConcurrencyControl,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC在MySQLInnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-......
  • 【MySQL-23】万字总结<InnoDB引擎>——【逻辑存储结果&架构(内存结构,磁盘结构,后台线程)&事
    前言大家好吖,欢迎来到YY滴MySQL系列,热烈欢迎!本章主要内容面向接触过C++的老铁主要内容含:欢迎订阅YY滴C++专栏!更多干货持续更新!以下是传送门!YY的《C++》专栏YY的《C++11》专栏YY的《Linux》专栏YY的《数据结构》专栏YY的《C语言基础》专栏YY的《单片机》专栏YY......
  • MVCC 详解
    MVCC简单理解MVCC,全称Multi-VersionConcurrencyControl,是多版本并发控制的意思。在高并发情况下操作数据库可能会出现脏写、脏读、不可重复度、幻读这四个问题。通过MVCC可以实现在不加锁的前提下避免一些问题。MVCC的实现原理多版本首先,我们引入一个概念,即行数......
  • 2024最新Mysql锁机制与优化实践以及MVCC底层原理剖析
    锁机制详解锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一......
  • 达梦数据库系列—32.多版本并发控制MVCC
            在多版本控制以前,数据库仅通过锁机制来实现并发控制。数据库对读操作上共享锁,写操作上排他锁,这种锁机制虽然解决了并发问题,但影响了并发性。        DM数据库基于物理记录和回滚记录实现行级多版本支持,数据页中只保留物理记录的最新版本,通过回滚记录......
  • MySQL 系列八:MVCC
    Author:ACatSmilingSince:2024-07-22什么是MVCCMVCC:MultiversionConcurrencyControl,多版本并发控制。顾名思义,MVCC是通过数据行的多个版本管理来实现数据库的并发控制。这项技术使得在InnoDB的事务隔离级别下执行一致性读操作有了保证。换言之,就是为了查询一些正在被另......
  • MVCC和多版本并发控制策略
    MVCC:多版本并发控制引言在现代数据库管理系统中,并发控制是确保数据一致性和完整性的关键机制之一。随着应用程序对数据库性能要求的不断提高,传统的锁机制(如悲观锁)在某些场景下可能会成为性能瓶颈。为了克服这些限制,多版本并发控制(MVCC)应运而生,它允许数据库事务在读取数据......