MySQL系列---锁的种类和概念

10 阅读 作者:谈胖胖 2020-04-13

在mysql当中,关于innodb的锁类型总共可以分为四种,包含了行锁和表锁,分别是

  • 基本锁 - [ 共享锁(Shared Locks:S锁)和排它锁(Exclusive Locks:X锁)]
  • 意向锁 - [ intention lock,分为意向共享锁(IS锁)和意向排他锁(IX锁)]
  • 行锁 - [ record Locks、gap locks、next-key locks、Insert Intention Locks ]
  • 自增锁 - [ auto-inc locks ]

下面是各种锁之间的对应兼容情况(ps:在某一篇博客上看到,忘了是哪一篇,觉得好就截下来了嘻嘻):

InnoDB三种行锁的算法:

  • Record Lock:单个行记录上的锁,只锁定记录本身
  • Gap Lock:间隙锁,锁定一个范围,但不包括记录本身。 目的是为了防止同一个事物的两次当前读,出现幻读的情况
  • Next-Key Lock:1+2,锁定一个范围,并锁定记录本身。目的:解决幻读

共享锁

共享锁shared locks(S锁)也称读锁,允许其他事物再加S锁,不允许其他事物再加X锁

加锁方式:

select...lock in share mode复制代码

注意:

  • 对于使用共享锁的事务,其他事务只能读,不可写
  • 如果执行了更新操作则会一直等待,直到当前事务commit或者rollback
  • 如果当前事务也执行了其他事务处于等待的那条sql语句,当前事务将会执行成功,而其他事务会报死锁
  • 并且允许其他锁共存

排它锁

排它锁Exclusive Locks(X锁)也称写锁,不允许其他事务再加S锁或者X锁

加锁方式

select ... for update

→ for update:InnoDB默认是行级别的锁,当有明确指定的主键时,使用的是行锁;否则使用的是表锁。使用情况详细如下:

  • 明确指定主键,并且由此记录,行级锁。例:
    select name,age from tb_user where id = '1' for update(id是主键)
  • 明确指定主键/索引,若查无记录,无锁。例:
    select name,age from tb_user where id = '1' for update(id是主键,但不存在id = 1的数据)
  • 无主键/索引,表级锁。例:
    select name,age from tb_user where age = 12 for update(age是普通字段)
  • 主键/索引不明确,表级锁。例:
    select name,age from tb_user where age = 12,id = '1' for update
    (id是主键,age不是,但数据库有此数据)

注意:

  • 对于排它锁的事务,其他事物可读,但不可进行更新操作
  • for update仅使用与InnoDB,并且必须开启事务,在begin和commit之间才生效
  • 当一个事务进行for update的时候,另一个事务也有for update时会一直等待,直到之前的事务commit或rollback或断开连接释放锁才拿到锁进行后面的操作(排它锁不能共存)
  • innoDB引擎.默认对update,delete,insert加排他锁,select语句默认不加锁

乐观锁

读取出记录,并将此版本一同读出,执行更新操作并对记录的版本号+1。此时,将待提交记录的版本号与数据库对应表的记录版本好进行对比,如果大于数据库原有版本号的话,予以更新;否则认为是过期数据,更新失败。目的是为了用于解决并发问题。

例:A、B两个人同时修改同一条记录,设数据库原有金额是100元,A对金额+100,B往数据库-50,正常结果是150,但由于并发,结果有可能是200,或者50

解决:A B同时读取出数据版本为1,A对金额+100,并修改数据版本为2,提交数据,此时数据版本为1,更新成功。B读取数据版本1,对金额-50,此时结果为50,并修改数据版本为2,提交数据,对比数据库原版本2,没有比原版本高,更新失败

间隙锁

间隙锁是在索引记录之间的间隙的锁定,或在最后一个索引记录之前或之后的间隙上的锁定

使用唯一索引搜索唯一一行的一句不需要间隙锁锁定(不包括搜索条件包含多列唯一索引的某些列的情况,查询出的多条记录,会发生间隙索引),详细例子如下:

  • 前提:(id是主键索引)由于搜索结果是唯一的一条记录,所以不会使用间隙锁
    select id,name,age from tb_user where id = '1'
  • 前提:(id是主键索引、age是非索引字段)由于搜索结果可能不止一条记录,所以会使用间隙锁select id,name,age from tb_user where id = '1' and age = 13

演示:
数据结构:

CREATE TABLE `tb_user` (
  `id` int(10) NOT NULL,
  `name` varchar(255) NOT NULL DEFAULT '',
  KEY `index_id` (`id`),
  KEY `index_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

初始化数据:

ps:我现在使用的是数据库默认的隔离级别:repeatable read,并在本地开启两个客户端进行测试。

→ 客户端1:开启事务,在客户端中修改id为1-6之间的数据,此时id=2这行记录是不存在的

→ 客户端2:开启事务,往数据库中添加id为2的记录时会发现该操作会被阻塞!

-->上述情况就说明了有间隙锁的存在

--> 接下来我修改了隔离级别为read commited,可以发现上述添加操作,即id =2 的记录会添加成功,说明read commited的隔离级别不会使用间隙锁。

注意:

  • 间隙锁在InnoDB的唯一作用就是防止其它事务的插入操作,以此来达到防止幻读的发生,所以间隙锁不分什么共享锁与排它锁。
  • 如果InnoDB扫描的是一个主键/唯一索引,那么InnoDB只会采用行锁(Record Lock)方式来加锁,而不会使用间隙锁(Next-Key Lock)的方式。
  • 间隙锁只是阻止其他事物插入到间隙当中,并不阻止不同的事物在同一间隙上获得间隙锁。
  • 将隔离级别设置为read_commited或启用innodb_locks_unsafe_for_binlog系统变量(现已被弃用)可明确禁止使用间隙锁

MVCC(Snapshot read vs current read)

MVCC,基于多版本的并发控制协议,最典型的是读不加锁,读写不冲突,其包含两种读操作,即快照读(snapshot read)与当前读(current read)。

  • 快照读:读取记录的可见版本,不加锁。
  • 当前读:读取记录的最新版本,当前读返回的记录,都会加锁,保证其他事物不会再修改这条记录

那具体哪些操作为当前读,哪些操作又是快照读呢,让我们来看一下:

→ 快照读:简单的读操作,属于快照读,不加锁。(不过有些会有点小例外)

例:select * from tb_user where ?

→ 当前读:特殊的读操作,属于当前读,需要加锁。

  • select * from table where ? lock in share mode;

  • select * from table where ? for update;

  • insert into table values (…);

  • update table set ? where ?;

  • delete from table where ?;

所有以上的语句,都属于当前读,读取记录的最新版本。并且,读取之后,还需要保证其他并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其他的操作,都加的是X锁 (排它锁)。

原文地址:https://blog.csdn.net/zhuyanlin09/article/details/101290819
广告一下
热门教程
PHP7报A non well formed numeric value encountered 0
Linux系统下关闭mongodb的几种命令分享 0
mongodb删除数据、删除集合、删除数据库的命令 0
Git&Github极速入门与攻坚实战课程 0
python爬虫教程使用Django和scrapy实现 0
libnetsnmpmibs.so.31: cannot open shared object file 0
数据结构和算法视频教程 0
redis的hash结构怎么删除数据呢? 0
C++和LUA解析器的数据交互实战视频 0
mongodb errmsg" : "too many users are authenticated 0
C++基础入门视频教程 0
用30个小时精通C++视频教程可能吗? 0
C++分布式多线程游戏服务器开发视频教程socket tcp boost库 0
C++培训教程就业班教程 0
layui的util工具格式时间戳为字符串 0
C++实战教程之远程桌面远程控制实战 1
网络安全培训视频教程 0
LINUX_C++软件工程师视频教程高级项目实战 0
C++高级数据结构与算法视频教程 0
跨域问题很头疼?通过配置nginx轻松解决ajax跨域问题 0
相关文章
【译】JavaScript数据结构(3):单向链表与双向链表 16
10个JavaScript难点 16
【译】苹果拒绝支持PWA,有损Web的未来 16
iView 一周年了,同时发布了 2.0 正式版,但这只是开始... 16
nodejs+mongodb构建一个简单登录注册功能 16
【译】JavaScript数据结构(4):树 16
组件化开发与黑箱 16
TypeScript - 不止稳,而且快 16
webpack3+anujs+ReactCSSTransitionGroup 16
原生js实现图片放大镜效果 16
WEB缓存探究第二弹——实战 16
纯笔记:vfork 的一些使用场景(顺便讲一下 fork 的原理) 16
Android APP 内部捐赠实现(支付宝&微信) 16
WKWebView 的一些小总结 16
模型评价(一) AUC大法 16
开始使用GraphQL 16
Webpack模块化原理简析 16
gulp使用问题记录 16
使用Angular4动画为页面添彩 16
Python27 Matplotlib (win64 python2.7) 安装及简单使用 16