当前位置: 首页 > news >正文

mysql锁机制详解

锁是计算机协调多个进程或线程并发访问某一资源的机制。

在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

锁分类

从性能上分为乐观锁(用版本对比或CAS机制)和悲观锁,乐观锁适合读操作较多的场景,悲观

锁适合写操作较多的场景,如果在写操作较多的场景使用乐观锁会导致比对次数过多,影响性能。

从对数据操作的粒度分,分为表锁、页锁、行锁。

从对数据库操作的类型分,分为读锁和写锁(都属于悲观锁),还有意向锁。

读锁

又称(共享锁,S锁(shared))。若事务T对数据A(某一资源)加上S锁,则事务T可以读A但不能修改A。其它事务也只能读A但不能修改A。并且,其他事务只能再对A加S锁,不能加X锁,除非T释放A上的S 锁。简单的说,自己只能读,别人也只能读。

写锁

又称(排它锁、独占锁,X锁(exclusive))。若事务T对数据A(某一资源)加上X锁,事务T可以读A也可以修改A。其他事务不能再对A加任何锁(共享锁或排他锁),直到T释放A上的锁。简单的说,自己可读可写,别人可以无锁读但是不能加读锁,不可写,因为写操作默认加排它锁。

意向锁(Intention Lock)

又称I锁,针对表锁,主要是为了提高加表锁的效率,是mysql数据库自己加的。当有事务给表的数据行加了共享锁或排他锁,同时会给表设置一个标识,表示已经有行锁了,这时如果其他事务要想对表再加表锁时,就不必逐行判断有没有行锁可能跟表锁冲突了,直接读这个标识就可以确定自己该不该加表锁。特别是表中的记录很多时,逐行判断加表锁的方式效率很低,而这个标识就是意向锁。

意向锁主要分为:

意向共享锁,IS锁,对整个表加共享锁之前,需要先获取到意向共享锁。

意向排他锁,IX锁,对整个表加排他锁之前,需要先获取到意向排他锁。

表锁

每次操作锁住整张表,开销小,加锁快。不会出现死锁,锁定粒度大,发生锁冲突的概率最高,并发度最低,一般用在整表数据迁移的场景。

 准备测试数据

测试表锁

给表加读锁

lock table test read; 

然后插入一条数据发现会阻塞,无法插入。

删除表锁

unlock tables;

查看表上加过的锁

show open tables;

页锁

只有BDB存储引擎支持页锁,页锁就是在页的粒度上进行锁定,锁定的数据资源比行锁要多,因为一个页中可以有多个行记录。当我们使用页锁的时候,会出现数据浪费的现象,但这样的浪费最多也就是一个页上的数据行。页锁的开销介于表锁和行锁之间,会出现死锁。锁定粒度介于表锁和行锁之间,并发度一般。

行锁

每次操作锁住一行数据。开销大,加锁慢(因为要扫描数据),会出现死锁,锁定粒度最小,发生锁冲突的概率最低,并发度最高。

InnoDB相对于MYISAM的最大不同有两点:

InnoDB支持事务(TRANSACTION)

InnoDB支持行级锁

注意,InnoDB的行锁实际上是针对索引加的锁(在索引对应的索引项上做标记),不是针对整个行记录加的锁。并且该索引不能失效,否则会从行锁升级为表锁。(RR(REPEATABLE READ)级别会升级为表锁,RC(READ COMMITTED)级别不会升级为表锁)。

比如我们在RR级别执行如下sql:

select * from test where age= 18 for update;

where条件里的name字段无索引

行级锁操作

加共享锁:select ... lock in share mode

加排它锁:select ...for update

几个注意的点

mysql InnoDB引擎的数据库,增删改操作默认都会加排他锁,而查询不会加任何锁。
自己对某一资源加共享锁,别人也可以再继续加共享锁,即多个共享锁可以共存。
数据库同一资源上,共享锁和排他锁不能共存,排他锁和排他锁不能共存。
同一资源上,要么不存在任何锁,要么存在单个或多个共享锁,要么存在单个排它锁。

测试LOCK IN SHARE MODE 共享锁

现在我们对id=1的数据行进行共享锁查询,这里会使用BEGIN开启事务,而不去COMMIT提交事务,这样做是为了保证锁不被释放,因为提交事务或回滚事务都会释放锁。

测试一个事务中加了共享锁,在其他事务查询中直接查询、共享锁、排它锁的情况。

窗口1中共享锁的事务查询,且没有提交事务

窗口2中测试无锁查询:

结果:可以查询到数据,不受影响。

窗口2中测试共享锁查询:

结果:可以查询到数据,不受影响。

窗口2中测试排他锁查询:

结果:一直处于阻塞状态,没有返回查询结果集。

分析:一个事务中的查询加了共享锁,且没有提交事务,另一个事务中的无锁查询和共享锁查询是可以查到数据的,但排他锁查询不可以,因为排他锁与共享锁不能共存于同一数据上。可以这么理解,排它锁是一种独占的锁,数据只能被自己独占,所以,也就不能存在其他锁了。

测试for update 排他锁

begin;
select * from test where id=1 for update;
-- commit;

窗口1

窗口2中测试无锁查询:

结果:可以查询到数据,不受影响。

窗口2中测试共享锁查询:

结果:一直处于阻塞状态,没有返回查询结果集。

窗口2中测试排他锁查询:

结果:一直处于阻塞状态,没有返回查询结果集。
 

分析:一个事务中的查询加了排它锁,且事务没有提交。另一个事务中的排他锁查询和共享锁查询都会处于阻塞状态。这是因为id=1的数据已经被加上了排他锁,并且该锁还未释放,阻塞表明是在等待排他锁释放。另外,说明一下,第二个窗口下的查询操作并没有开启一个事务,但属于其它事务中的查询。因为在默认的自动事务提交设置下 ,select 同Update、Insert、Delete一样都会启动一个隐式的事务。窗口1中的事务是显示事务的使用。

InnoDb 锁机制

最后我们验证一下上面所说的mysql InnoDb引擎中update、delete、insert语句会自动加排他锁的问题。

窗口1,事务中进行更新操作,不提交事务

结果:数据并没有更新。

窗口2中无锁查询:

结果:数据并没有更新。

窗口2中共享锁查询:

结果:查询处于阻塞。

窗口2中排他锁查询:

结果:查询处于阻塞。

分析:一个事务中进行了增删改操作(insert、delete、update),且没有提交事务。另一个事务中的无锁查询可以查到旧数据,共享锁查询和排他锁查询处于阻塞状态。这说明mysql数据库对修改操作自动加上排他锁了。

最后小结:

1)一个事务中的查询加了共享锁,且没有提交事务。另一个事务中的无锁查询和共享锁查询可以查到数据,但排他锁查询不可以。

2)一个事务中的查询加了排它锁,且没有提交事务。另一个事务中的无锁查询可以查到数据,共享锁查询和排他锁查询都会处于阻塞状态。

3)mysql InnoDB引擎的数据库,insert、delete、update 都会自动给涉及到的数据加上排他锁,select 语句默认不会加任何锁。


http://www.mrgr.cn/news/75230.html

相关文章:

  • 关于深度学习的一些工具安装与细节
  • 23.网工入门篇--------介绍一下园区网典型组网架构及案例实践
  • 设计模式(四)装饰器模式与命令模式
  • 从0学习React(11)
  • 【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最大的数
  • 【前端】JavaScript高级教程:线程机制与事件机制
  • 刀客doc:《再见爱人4》能带动芒果TV的广告营收吗?
  • 【学习日记】notebook添加JAVA支持
  • Android Framework AMS(17)APP 异常Crash处理流程解读
  • 教你使用 Lisp 编写 ChatGPT 对话机器人
  • 解决 Mybatis-Plus 中 `updateById` 方法不更新空值、更新字段无效的问题
  • Altium Designer使用技巧(五)
  • 微服务day08
  • AUTOSAR_EXP_ARAComAPI的7章笔记(3)
  • 17-鸿蒙开发中的背景图片设置:位置、定位、单位和尺寸
  • Linux软件包管理与Vim编辑器使用指南
  • 绝对路径和相对路径的区别
  • 搜维尔科技:我们使用Xsens动作捕捉技术创建的短片
  • 行驶证 OCR 识别 API 接口的优势分析
  • Python中,处理日期和时间的库
  • GCN基于图卷积神经网络多特征分类预测(多输入单输出) Matlab代码
  • springboot039基于Web足球青训俱乐部管理系统
  • 似然函数解析
  • LeetCode 每日一题 统计满足 K 约束的子字符串数量 I
  • AI视觉小车基础--2.按键读取
  • 【MYSQL】数据库日志 (了解即可)