个人公众号『码农札记』,欢迎关注,查看更多精彩文章。

背景

数据库是一个多用户并发使用的共享资源。当多个并发读写数据时,在数据库中就会产生多个事务同时读写同一数据的情况。 若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性。

这个时候就要引入「锁」的概念,来应对上面所说的并发情况。

简介❝ 锁是在执行多线程或者协程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足。

本文将按照下图对MySQL锁具体讲解。

实现机制

悲观锁

总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加(悲观)锁。一旦加锁, 不同线程同时执行时,只能有一个线程执行,其他的线程在入口处等待,直到锁被释放。

MySQL里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。 Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现

乐观锁(Optimistic Lock)

每次读写数据的时候都认为别人不会修改该数据,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。 乐观锁「适用于读多写少的应用场景,这样可以提高吞吐量」。

实现方式

「数据版本(Version)记录机制实现」

何谓数据版本?

即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 version 字段来实现。当读取数据时,将version字段的值一同读出, 数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对, 如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。

MySQL的mvcc的实现就是此种方式,了解mvcc可以查看我的文章 从源码分析 MySQL的多版本控制(MVCC)

「时间戳(timestamp)」

和数据版本实现方式类似,同样是在需要乐观锁控制的table中增加一个字段, 字段类型使用时间戳(timestamp), 和上面的version类似,在更新提交的时候检查当前数据库中数据的时间戳 和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

锁粒度

相对于其他的数据库而言,MySQL的锁机制比较简单,最显著的特点就是不同的存储引擎支持不同的锁机制。 根据不同的存储引擎,MySQL中锁的特性可以大致归纳如下引擎表锁行锁页

InnoDB支持支持

MyISAM支持

BDB支持支持

行锁❝ 开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

❞行锁的锁定颗粒度在MySQL中是最细的,应用于InnoDB存储引擎,只针对操作的当前行进行加锁。 并发情况下,产生锁等待的概率较低,支持较大的并发数,但开销大,加锁慢,而且会出现死锁。

在InnoDB中使用行锁有一个前提条件:「检索数据时需要通过索引!因为InnoDB是通过给索引的索引项加锁来实现行锁的。」

在不通过索引条件查询的时候,InnoDB会使用表锁,这在并发较大时,可能导致大量的锁冲突。此外,行锁是针对索引加锁, 存在这种情况,虽然是访问的不同记录,但使用的是同一索引项,也可能会出现锁冲突。

提示:不一定使用了索引检索就一定会使用行锁,也有可能使用表锁。因为MySQL会比较不同执行计划的代价, 当全表扫描比索引效率更高时,InnoDB就使用表锁。因此需要结合SQL的执行计划去分析锁冲突。行锁会产生死锁,因为在行锁中,锁是逐步获得的,主要分为两步:锁住主键索引,锁住非主键索引。 如:当两个事务同时执行时,一个锁住了主键索引,在等待其他索引;另一个锁住了非主键索引,在等待主键索引。这样便会发生死锁。 InnoDB一般都可以检测到这种死锁,并使一个事务释放锁回退,另一个获取锁完成事务。

表锁

表级锁为表级别的锁定,会锁定整张表,可以很好的避免死锁,是 MySQL 中最大颗粒度的锁定机制。

一个用户在对表进行写操作(插入、删除、更新等)时,需要先获得写锁,这会阻塞其它用户对该表的所有读写操作。 没有写锁时,其它读取的用户才能获得读锁,读锁之间是不相互阻塞的。

表级锁最大的特点就是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。 当然,锁定颗粒度大带来最大的负面影响就是出现锁定资源争用的概率会很高,致使并发度大打折扣。

不过在某些特定的场景中,表级锁也可以有良好的性能。例如,READ LOCAL 表级锁支持某些类型的并发写操作。 另外,写锁也比读锁有更高的优先级,因此一个写锁请求可能会被插入到读锁队列的前面(写锁可以插入到锁队列中读锁的前面,反之读锁则不能插入到写锁的前面)。

使用表级锁的主要是 MyISAM,MEMORY,CSV 等一些非事务性存储引擎。

尽管存储引擎可以管理自己的锁,MySQL 本身还是会使用各种有效的表级锁来实现不同的目的。 例如,服务器会为诸如 ALTER TABLE 之类的语句使用表级锁,而忽略存储引擎的锁机制。

页锁

页级锁是 MySQL 中比较独特的一种锁定级别,在其他数据库管理软件中并不常见。

页级锁的颗粒度介于行级锁与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力同样也是介于上面二者之间。另外,页级锁和行级锁一样,会发生死锁。

页级锁主要应用于 BDB 存储引擎。

应用场景

从锁的角度来说,表级锁适合以查询为主,只有少量按索引条件更新数据的应用,如 Web 应用。 而行级锁更适合于有大量按索引条件,同时又有并发查询的应用,如一些在线事务处理(OLTP)系统。

「MySQL 这 3 种锁的特性可大致归纳如下」

表级锁行级锁页级锁

开销小大介于表级锁和行级锁之间

加锁快慢介于表级锁和行级锁之间

死锁不会出现死锁会出现死锁会出现死锁

所粒度大小介于表级锁和行级锁之间

并发低高一般

兼容性

概念

InnoDB实现了以下两种类型的行锁。

「共享锁(S)」:又称读锁 (read lock),是读取操作创建的锁。其他用户可以并发读取数据, 但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁。当如果事务对读锁进行修改操作,很可能会造成死锁。

「排他锁(X)」:exclusive lock(也叫writer lock)又称写锁。 若某个事物对某一行加上了排他锁,只能这个事务对其进行读写,在此事务结束之前, 其他事务不能对其进行加任何锁,其他进程可以读取,不能进行写操作,需等待其释放。 「排它锁是悲观锁的一种实现」。

另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention Locks),这两种意向锁都是表锁。

「意向共享锁(IS)」:事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。

「意向排他锁(IX)」:事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。

意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。

「InnoDB行锁模式兼容性列表」请求锁模式

是否兼容当前锁模式XIXSIS

X冲突冲突冲突冲突

IX冲突兼容冲突兼容

S冲突冲突兼容兼容

IS冲突兼容兼容兼容

如果一个事务请求的锁模式与当前的锁兼容,InnoDB就将请求的锁授予该事务;反之,如果两者不兼容,该事务就要等待锁释放。

意向锁是InnoDB自动加的,不需用户干预。对于UPDATE、DELETE和INSERT写操作,InnoDB会自动给涉及数据集加排他锁(X); 对于普通SELECT语句,InnoDB不会加任何锁;事务可以通过以下语句显示给记录集加共享锁或排他锁。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。

排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE。

用SELECT ... IN SHARE MODE获得共享锁,主要用在需要数据依存关系时来确认某行记录是否存在, 并确保没有人对这个记录进行UPDATE或者DELETE操作。但是如果当前事务也需要对该记录进行更新操作, 则很有可能造成死锁,对于锁定行记录后需要进行更新操作的应用,应该使用SELECT... FOR UPDATE方式获得排他锁。

InnoDB在事务执行过程中,使用两阶段锁协议:

随时都可以执行锁定,InnoDB会根据隔离级别在需要的时候自动加锁;

锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放。

举个栗子

「共享锁」

「排它锁」

算法

「Record Lock」: 单个行记录上的锁

锁总会锁住索引记录,锁住的是key。

如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB会使用隐式的主键进行锁定。

如果要锁的没有索引,则会进行全表记录加锁。

「Gap Lock」 :间隙锁,锁定一个范围,但不包含记录本身

锁定索引记录间隙,确保索引记录的间隙不变

间隙锁时针对事务隔离级别为可重复读或以上级别而配的

Gap Lock在InnoDB的唯一作用就是防止其他事务的插入操作,以此防止幻读

「Next-Key Lock」:临键锁,Gap Lock + Record Lock,锁定一个范围,并且包含记录本身

当查询的索引含有唯一属性时,InnoDB存储引擎会对Next-Key Lock 进行优化,将其降级为 Record Lock,即仅锁住索引本身,而不是范围。

当查询的索引为辅助索引时,默认使用Next-Key Locking技术进行加锁,锁定范围是前一个索引到后一个索引之间范围。

例如一个索引有10,11,13,20这四个值,那么该索引可能被Next-Key Locking的区间为:(-∞,10】

(10,11】

(11,13】

(13,20】

(20,+∞)

在Next-Key Lock 算法下,InnoDB对于行的查询都是采用这种锁定的算法。可以有效的防止幻读。

间隙锁在InnoDB的唯一作用就是防止其它事务的插入操作,以此来达到防止幻读的发生,所以间隙锁不分什么共享锁与排它锁。 默认情况下,InnoDB工作在Repeatable Read隔离级别下,并且以Next-Key Lock的方式对数据行进行加锁,这样可以有效防止幻读的发生。

要禁止间隙锁的话,可以把隔离级别降为Read Committed,或者开启参数innodb_locks_unsafe_for_binlog。

对于快照读来说,幻读的解决是依赖mvcc解决。而对于当前读则依赖于gap-lock解决。   「当前读」:读取的是最新版本, 并且对读取的记录加锁, 阻塞其他事务同时改动相同记录,避免出现安全问题。

「快照读」简单的select操作(不包括 select ... lock in share mode, select ... for update)。

Read Committed隔离级别:每次select都生成一个快照读。

Read Repeatable隔离级别:开启事务后第一个select语句才是快照读的地方,而不是一开启事务就快照读。

小结

本文主要讲解了锁的实现机制,MySQL中常见的锁类型、兼容方式和算法。表锁,行锁在不同的场景下有各自的优势,具体看业务选型。

共享锁和互斥锁要合理应用,否则会导致死锁。

MySQL四种隔离级别,除了串行化是不能解决幻读的,固引入了临键锁,间隙锁解决此问题。

「感谢阅读,加个关注,欢迎点赞和转发!!!」

mysql 锁机制及实现原理_MySQL-深入浅出锁分类及实现原理相关推荐

  1. SQL SERVER的锁机制(二)——概述(锁的兼容性与可以锁定的资源)

    二.完整的锁兼容性矩阵(见下图) 对上图的是代码说明:见下图. 三.下表列出了数据库引擎可以锁定的资源. 名称 资源 缩写 编码 呈现锁定时,描述该资源的方式 说明 数据行 RID RID 9 文件编 ...

  2. Linux 锁机制(3)之自旋锁

    Linux 锁机制(3)之自旋锁 1. 自旋锁 1.1 两种锁 1.2 自旋锁 1.3 自旋名字来源:自旋锁一直循环等待,直到获取锁为止. 1.4 自旋锁优点: 2 自旋锁特点/使用: 2.1 临界区 ...

  3. 回文数c语言北理,2019届高考数学(北师大版理)大一轮复习配套练习:第十章 计数原理 第1讲 分类加法计数原理与分步乘法计数原理 .doc...

    2019届高考数学(北师大版理)大一轮复习配套练习:第十章 计数原理 第1讲 分类加法计数原理与分步乘法计数原理 .doc 第1讲分类加法计数原理与分步乘法计数原理一.选择题1.从集合0,1,2,3, ...

  4. mysql 表级别的锁和行级别的_MySQL 表锁和行锁机制

    案例分析 目前,MySQL常用的存储引擎是InnoDB,相对于MyISAM而言.InnoDB更适合高并发场景,同时也支持事务处理.我们通过下面这个案例(坑),来了解行锁和表锁. 业务:因为订单重复导入 ...

  5. mysql大表修改表名原理_MySQL修改大表工具pt-online-schema-change原理

    MySQL修改大表工具pt-online-schema-change的使用限制: 1).如果修改表有外键,除非使用 –alter-foreign-keys-method 指定特定的值,否则工具不予执行 ...

  6. mysql的锁机制(读锁,写锁,表锁,行锁,悲观锁,乐观锁,间隙锁)

    读锁和写锁 介绍 MyISAM表锁中的读锁和写锁 读锁(共享锁S): 对同一个数据,多个读操作可以同时进行,互不干扰.加锁的会话只能对此表进行读操作,其他会话也只能进行读操作.MyISAM的读默认是加 ...

  7. mysql锁描述正确的是_MySQL表锁详解

    锁是计算机协调多个进程或纯线程并发访问某一资源的机制.而表锁由MySQL Server 实现,一般在执行DDL语句时会对整个表进行加锁,比如说ALTER TABLE等操作.在执行SQL语句时,也可以明 ...

  8. mysql锁机制(共享锁(S)、排他锁(X)、意向共享锁(IS)、意向排他锁(IX)的关系,死锁,乐观锁,悲观锁...)

    1. 锁的基础与行锁的特点 1.1 概念 在开发多用户.数据库驱动的应用时,相当大的一个难点就是解决并发性的问题,目前比较常用的解决方案就是锁机制. 锁机制也是数据库系统区别于文件系统的一个关键特性. ...

  9. MySQL数据库读现象 数据库锁机制 Innodb存储引擎行级锁

    数据库读现象 数据库管理软件的"读现象"指的是当多个事务并发执行时,在读取数据方面可能碰到的问题,包括有脏读.不可重复读和幻读. 创建数据表 # 创建数据表 create tabl ...

  10. mysql 事务隔离级别实现原理_MySQL事务隔离级别和实现原理 - 米扑博客

    开发中经常提到数据库的事务,那你知道数据库还有事务隔离的说法吗, 事务隔离还有隔离级别,那什么是事务隔离,隔离级别又是什么呢? MySQL 事务 本文所说的 MySQL 事务都是指在 InnoDB 引 ...

最新文章

  1. js、jQuery、layer实现弹出层的打开、关闭
  2. boost::hana::at_c用法的测试程序
  3. 分享web前端七款HTML5 Loading动画特效集锦
  4. 四、物理优化(1)范式化
  5. .Net Remoting(基本操作) - Part.2 (转)
  6. 详尽kmp_详尽的分步指南,用于数据准备
  7. 链表(Linked List)之双向链表
  8. Docker 网络命名空间
  9. OpenCV精进之路(零):访问图像中像素的三种方法
  10. 贪心法——LeetCode 55 跳跃游戏
  11. 【二分法】LeetCode 35. Search Insert Position
  12. 二十一天学通C++之使用try/catch捕获异常
  13. 数据结构与算法笔记一:稀疏数组的应用
  14. python入门教程(非常详细),从零基础入门到精通,看完这一篇就够了
  15. minecraftjava版光追_《我的世界》加入光追,“马赛克”游戏真有必要这样做?...
  16. ubuntu下安装opencv2
  17. 《每日一题》NO.23:谈谈对数字集成电路工艺的认识
  18. C语言练习:hackerrank十五关
  19. Win10(winser2019)关闭驱动数字签名方法
  20. 微信指纹支付原理浅析

热门文章

  1. Head First设计模式读书笔记六 第七章上 适配器模式
  2. 运用经验模态分解方法(EMD)对实测探地雷达数据进行处理
  3. 关于映射的一些理解与常见命题
  4. maven配置报错以及The JAVA_HOME environment variable is not defined correctly的解决方法
  5. LeetCode-402:移除k位数字
  6. android自带抓拍算法,Android | 超简单集成HMS ML Kit实现最大脸微笑抓拍
  7. python中str函数_python字符串str的常用函数
  8. day15 java接口在开发中的作用
  9. php限制注册频率,php如何限制某个ip提交的次数
  10. php事件检测,细说浏览器特性检测(2)-通用事件检测_jquery