数据库中的几种锁

news2025/5/24 10:33:22

数据库锁

image-20240103202955494

1.数据库锁的种类

以 mysql innoDB 为例,数据库的锁有 排他锁,共享锁,意向锁,自增锁,间隙锁,锁的范围有包括,行锁,表锁 ,区间锁。

从应用研发的视角,我们需要关注的主要是 排他锁,共享锁,以及锁的范围。其他如意向锁,自增所,间隙锁是mysql 内部在处理某些逻辑是自己处理的锁。

平时还有提到的悲观锁,乐观锁,在数据库层面上没有这个锁的概念,如果要做简单映射,悲观锁可以映射成排他锁,乐观锁是由应用层面保障的,和 DB 的锁概念无关。

排他锁

2.1. 介绍

排他锁是应用研发中用的最多的数据库锁了,收单、支付、金融,几乎任何一个系统都会使用到排他锁。

我们平常锁说的加锁,悲观锁,说的也就是数据库中的排他锁,排他锁也称为 X 锁(共享锁是 S 锁)如

<operation name="lock" paramtype="primitive" multiplicity="one" rawsql="true">
  <sql><![CDATA[
            select  xxx 
             from table where id = ? for update
        ]]></sql>
</operation>

select * from table where ? for update 会对于一条记录 / 一个区间 / 整表进行加锁。

在加锁之前,必须要开启事务,只有在事务中的 for update 语句才是有效的,对于一个事务,如果成功的获取了锁,那意味其他事务无法对于这条记录记录进行加排他锁、共享锁,无法更新,删除操作。不过可以做无锁查询。

所以如收单对于 iaqc_base 进行加锁之后,正常没有 for update 的查询是依旧可以进行的。

2.2. 使用场景

排他锁的使用场景在于,保证一张表的一致性,举例说明,以账务为例,用户A余额有 200 块,用户A支付了100块,余额 -100,与此同时,用户B给用户A转账50块,余额 +50,期望用户余额是 200 - 100 + 50 = 150。

期望是

如果在没有锁的情况下,结果是不确定的,可能是 250元,可能是 100元,也可能是 150 元。

事务1 用户支付 100 元,余额 -100线程3 用户收到转账 50 元,余额 + 50
select * from account where user = A返回用户余额为 200 元
判断余额是否足够,内存计算 200 - 100 = 100select * from account where user = A返回用户余额为 200 元
update account set balance = 100 where user = A更新余额为 100 元内存计算 200 + 50 = 250
commit;update account set balance = 250 where user = A更新余额为 250 元
commit;

如果增加了悲观锁,可以保证结果的一致性。

事务1 用户支付 100 元,余额 -100线程3 用户收到转账 50 元,余额 + 50
select * from account where user = A for update返回用户余额为 200 元
判断余额是否足够,内存计算 200 - 100 = 100select * from account where user = A for update
update account set balance = 100 where user = A更新余额为 100 元等待
commit;等待
返回用户余额为 100 元
内存计算 100 + 50 = 150
update account set balance = 150 where user = A更新余额为 150 元
commit;

悲观锁除了这个案例意外,在收单,支付等各种系统中都有使用,比如

  1. 收单:一个用户创建了一笔单据,进行了支付,支付回执比较慢,用户等的不耐烦了,关闭了订单。
  2. 支付:用户某一时刻对于一笔订单发起了多次退款

3.共享锁

3.1. 介绍

共享锁,S 锁,相对于悲观锁来说,是低一级的锁,若有事务对于某一条数据加了共享锁后,其他事务依旧可以增加共享锁,但是不能增加排他锁。

select * from table lock in share mode
共享锁(S)排它锁(X)
共享锁(S)允许不允许
排它锁(X)不允许不允许

3.2. 使用场景

共享锁的使用场景不多,在蚂蚁我没有找到使用共享锁的代码,只要是数据修改都是排他锁。

在 mysql 的官方文档中,排他锁是用来保证一张表中数据的一致性的,那共享锁是用来保证主从表的一致性的,举例:有用户表和用户联系人表,我们可以认为用户表示主表,联系人表示从表,在写入联系人的时候需要对用户表进行加锁,以免在写入的时候,联系人主记录被删除了。(在 ipay 用的排他锁)

这里排他锁和共享锁最大的区别在于如果对于用户表加的是排他锁,表示同一时刻只能一个事务写入联系人表,如果是共享锁,则同时可以多个事务写入联系人表。

共享锁案例:用户张三写入联系人李四和王五的两条记录。

事务1 写入联系人 李四写入联系人王五删除用户张三
select * from user lock in share mode where user id = ‘张三’select * from user lock in share mode where user id = ‘张三’delete from user where user_id = ‘张三’
insert into user_relation values(‘李四’)insert into user_relation values(‘王五’)等待
commit;commit;等待
commit;

3.3. 共享锁升级导致死锁

3.3.1. 共享锁升级

共享锁前面提到主要的使用场景在保持主表和从表之间的一致性,所以不建议在获得共享锁之后对于获得锁的数据进行更新操作,如果有更新操作的话,共享锁会升级成排他锁,可能会导致死锁。

3.3.2. 共享锁升级导致死锁

因为共享锁是可能会被多个事务同时获得的,如果在获得之后同时进行 update 则会产生死锁,举例:

2个事务同事获得用户余额的共享锁,并且进行金额操作

事务1 用户支付 100 元,余额 -100线程3 用户支付 50 元,余额 -50
select * from account where ? lock in share mode返回用户余额为 200 元
判断余额是否足够,内存计算 200 - 100 = 100select * from account where ? lock in share mode返回用户余额为 200 元
update account set balance = 100 where user = A更新余额为 100 元判断余额是否足够,内存计算 200 - 100 = 100
尝试锁升级成排他锁 等待事务 T2 共享锁释放update account set balance = 150 where user = A更新余额为 100 元
等待尝试锁升级成排他锁 等待事务 T1 释放
等待检测出死锁,事务失败,roll back
获得锁成功,更新成功
commit

3. 意向锁

意向锁 intention lock,分为 IS 共享意向锁 和 IX 排他意向锁。意向锁表示该表中有某一条记录被锁了,如果某条记录被加了排他锁,则该表上有 IX 锁,如果某条记录被加了共享锁,则该表上有 IS 锁,需要注意,意向锁是表级别的锁。

3.1. 意向锁要解决的问题

因为mysql 支持行锁和表锁,假设一张表中有一条记录被 T1 事务加了排他锁,T2 事务来加表锁的时候,应该是被阻塞。如果没有意向锁的情况下,mysql 需要循环该表的每一条记录,判断是否有加锁,最后才能得出能否加表锁,这个效率非常低。所以就有了意向锁。

当某一条记录被加锁的时候,会在表上先加意向锁,代表这个表中的某条记录被加锁了,那当其他事务来对表进行加锁的时候,只需要判断表上是否有意向锁,就可以判断出是否可以对表进行加锁。

3.2. 意向锁的互斥性

意向锁的互斥性IX 意向排他锁IS 意向共享锁
IX 意向排他锁兼容兼容
IS 意向共享锁兼容兼容

因为意向锁是代表这个表中的记录是否有增加排他锁和共享锁,所以对于意向锁之间是不会互斥的。

举例:

-- 在 user 表上增加 IX 锁 意向排他锁
-- 在 1 的聚簇索引上增加 X锁 排他锁
select * from user where id = 1 for update

-- 在 user 表上增加 IX 锁 意向排他锁
-- 在 2 的聚簇索引上增加 X锁 排他锁
select * from user where id = 2 for update
意向锁的互斥性IX 意向排他锁IS 意向共享锁
X 排他锁 表级别阻塞阻塞
S 共享锁 表级别阻塞兼容

4. 间隙锁

4.1. 介绍

间隙锁,主要是解决 mysql 在可重复读的级别下部分幻读的问题。

间隙锁在范围查询加锁,或者查询不存在值加锁的时候会使用,用于锁定一定范围内的数据,防止其他事务写入,以来解决幻读的问题。

4.2. 间隙锁解决幻读原理

场景:假设有如下数据

1100105110120200

当我们开启事务 T1 执行 sql select * from table where id = 102 for update; 的时候,如果没有间隙锁,此事务不会加锁,如果此时 T2 事务写入了 id = 102 数据,T1 可能会出现如下问题

  1. 在写入同样的 id 会出现主键冲突(但是之前明明查过了没有数据)
  2. 在T2事务提交之后,再次执行查询会查到数据

这些都是幻读的体现。

mysql 默认在可重复读下通过间隙锁解决了这个问题,当执行上述 sql 时,会对 102 这个间隙加锁,加锁是左开右闭,为 (100, 105] (新版本的mysql 好像优化了这个细节,是左开右开,即 (100, 105) ) . 加了区间锁之后,其他的事务则无法写入或者修改这个区间之内的数据。举例:

在我们执行 select * from table where id = 102 for update; 之后,查看数据库中的锁,产生间隙锁,这个时候锁的范围 (100, 105)

在这里插入图片描述

产生间隙锁之后,我们期望是 (100, 105),所以 100 和 105 不会加锁,范围内的会加锁,下面的例子操作了 105 的 update 和 103 的写入,可以看到 105 的 update 可以成功,103 的写入会阻塞。

在这里插入图片描述

5. 自增锁

5.1. 介绍

自增锁是一种特殊的表级别锁(table-level lock),专门针对事务插入AUTO_INCREMENT 自增类型的列。最简单的情况,如果一个事务正在往表中插入记录,所有其他事务的插入必须等待,以便第一个事务插入的行,是连续的主键值。

自增锁的分配有传统模式,连续模式,和交叉模式

image-20240103203611898

5.2. 自增锁可能不连续的情况

举例:有 user 表,有2个字段,1. id 自增列 2. name varchar

事务1事务2
begin;
insert into user values (‘A’);begin;
select * from user; | id | name|| 1 | A |insert into user values (‘B’);
insert into user values (‘C’);comit;
select * from user; | id | name || 1 | A || 3 | C |
commit;
select * from user;| id | name | | 1 | A || 2 | B || 3 | C |select * from user;| id | name | | 1 | A || 2 | B || 3 | C |

6.行级锁

行级锁是一种排他锁,防止其他事务修改此行;在使用以下语句时,Oracle 会自动应用行级锁:

  1. INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];
  2. SELECT … FOR UPDATE 语句允许用户一次锁定多条记录进行更新
  3. 使用 COMMIT 或 ROLLBACK 语句释放锁。

7.表级锁

表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分 MySQL 引擎支持。最常使用的 MYISAM 与 INNODB 都支持表级锁定。表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。

8.页级锁

页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折衷的页级,一次锁定相邻的一组记录。BDB 支持页级锁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1356004.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Gromacs WARNING问题

上述示例中&#xff0c;NA 是对系统净电荷进行中和的阳离子。请根据您的系统特性和仿真需求调整这些值。 总体而言&#xff0c;这个警告是为了提醒您关于电荷中性化的问题&#xff0c;确保您的模拟结果更加物理可信。 收敛性未达到预期精度&#xff1a; 警告指出&#xff0c;优…

普中STM32-PZ6806L开发板(HAL库函数实现-访问多个温度传感器DS18B20)

简介 我们知道多个DS18B20的DQ线是可以被挂在一起的, 也就是一根线上可以访问不同的DS18B20而不会造成数据错乱, 怎么做到的&#xff0c;其实数据手册都有说到&#xff0c; 就是靠64-bit ROM code 进行识别, 也可以理解成Serial Number进行识别, 因为主要差异还是在Serial Numb…

成为一名合格的软件测试工程师,得掌握什么技能?

在这个信息时代&#xff0c;软件行业的需求空前增长&#xff0c;而软件测试工程师作为软件开发过程中的重要角色&#xff0c;也越来越受企业的重视。那么&#xff0c;成为一名合格的软件测试工程师需要掌握什么技能呢&#xff1f;我结合多年的教学经验为大家总结出以下几点。 …

Fast and flexible X-ray tomography using the ASTRA toolbox

使用ASTRA工具箱进行快速灵活的X射线断层扫描 论文链接&#xff1a;http://dX.doi.org/10.1364/OE.24.025129 项目链接&#xff1a;https://astra-toolboX.com/indeX.html Abstract 从一系列投影图像中重建物体&#xff0c;如在计算机断层扫描(CT)中&#xff0c;是许多不同应…

nuxt3 env文件、全局变量处理

有两种方向 通过配置nuxt.config.ts Nuxt提供的钩子函数&#xff0c;实现全局变量的获取 runtimeconfig env文件往runtimeconfig放入内容 useAppConfig 通过env文件配置来获取服务端全局变量&#xff0c;客户端通过vite.define实现 nuxt.config.ts Nuxt钩子 1. runtim…

Unity ab包如何加密

「ab包」全称为 AssetBundle &#xff0c;是Unity提供的一种资源存储压缩包。其中储存了游戏的资源&#xff0c;如图片、模型、纹理、音视频、代码等文件。 由于ab包具有灵活储存、支持热更、包体较小且便于管理等优势&#xff0c;已经成为了市面上主流的游戏资源压缩方式。 …

李沐机器学习系列4---全连接层到卷积

1 从全连接到卷积 1.1 平移不变性 从概率分布的角度来看卷积的定义, f ( τ ) f(\tau) f(τ)是概率密度&#xff0c; g ( t − τ ) g(t-\tau) g(t−τ)是在这个分布下的均值 ( f ∗ g ) ( t ) ∫ − ∞ ∞ f ( τ ) g ( t − τ ) d τ (f*g)(t)\int_{-\infin}^{\infin}f(\t…

2024/01/02 每日AI必读资讯

减少LLM幻觉的方法 这篇论文总结了减少LLM幻觉的32种方法&#xff0c;包括RAG、微调模型&#xff0c;提示词工程等。 论文&#xff1a;https://arxiv.org/abs/2401.01313E5-mistral-7b-instruct&#xff1a;使用合成数据训练的Embedding模型 亮点是仅使用LLM生成的数据即可实现…

指令流水线的计算

我们需要知道以下公式&#xff1a; 设指令的条数为 n&#xff0c;指令执行需要 m 个阶段&#xff0c;时钟周期为 t 不采用流水线执行的时间&#xff1a;T1 n x m x t&#xff1b; 采用流水线执行的时间&#xff1a;T2 m x t (n - 1) x t&#xff1b; 加速比 S T1 / T2&…

PTA——逆序的三位数

程序每次读入一个正3位数&#xff0c;然后输出按位逆序的数字。注意&#xff1a;当输入的数字含有结尾的0时&#xff0c;输出不应带有前导的0。比如输入700&#xff0c;输出应该是7。 输入格式&#xff1a; 每个测试是一个3位的正整数。 输出格式&#xff1a; 输出按位逆序…

MacOS M1/M2 Go Debug 配置

前言 换电脑&#xff0c;Go 环境带来一些麻烦&#xff0c;耽误很多时间&#xff0c;稍作记录。 原始电脑是 Mac 旧款&#xff0c;CPU x86 构型&#xff0c;新电脑 M2&#xff0c;因为旧电脑里本地文件很多&#xff0c;为了简化搬迁&#xff0c;还是用了 Mac 自带的迁移&#x…

LeetCoed刷题:21. 合并两个有序链表

题目&#xff1a; 是否独立解出&#xff1a;否 解题时的思路与想法&#xff1a;解题时有几个问题&#xff1a; 1.怎么遍历两个数组&#xff0c;嵌套两个while循环不能实现&#xff08;后面通过看题解知道list1&#xff01;null&&list2&#xff01;null&#xff09; …

旋转图像(LeetCode 48)

文章目录 1.问题描述2.难度等级3.热门指数4.解题思路参考文献 1.问题描述 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在「原地」旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。 示…

知虾分析——深入了解Shopee平台的知虾分析工具

Shopee是一家知名的电商平台&#xff0c;为了帮助卖家提高业务表现&#xff0c;他们提供了一款强大的数据分析工具——知虾分析。这个工具可以帮助卖家监控店铺的运营状况&#xff0c;优化销售策略&#xff0c;并提高整体的业务表现。本文将深入解析知虾分析的关键功能和用途&a…

LINUX加固之命令审计

一、前言 在LINUX安全范畴中&#xff0c;安全溯源也是很重要的一个环节。对主机上所有曾操作过的命令详细信息需要有一份记录保存&#xff0c;当系统遭受破坏或者入侵&#xff0c;拿出这份记录&#xff0c;可以帮助定位一些可疑动作。 很多系统通常都会配置安全堡垒机&#xff…

亚马逊站内广告位置在哪设置?怎么设置广告位置?-站斧浏览器

亚马逊站内广告位置在哪设置&#xff1f; 亚马逊提供了多种广告类型&#xff0c;包括&#xff1a; Sponsored Products&#xff08;赞助产品&#xff09;&#xff1a;在搜索结果和商品详情页中展示。 Sponsored Brands&#xff08;赞助品牌&#xff09;&#xff1a;在搜索结…

git提交操作(不包含初始化仓库)

1.进入到本地的git仓库 查看状态 git status 如果你之前有没有成功的提交&#xff0c;直接看第5步。 2.追踪文件 git add . 不要提交大于100M的文件&#xff0c;如果有&#xff0c;看第5步 3.提交评论 git commit -m "你想添加的评论" 4.push (push之前可以再…

weblogic中间件安装

1.下载jdk Java Archive Downloads - Java SE 6 下载jdk-6u45-linux-x64.bin 2.配置防火墙和SELINUX Redhat7操作系统配置防火墙&#xff0c;开放应用端口&#xff0c;例如7001&#xff1b; # firewall-cmd --permanent --add-port7001/tcp # firewall-cmd --reload 关闭selinu…

jmeter使用心得(一)

jmeter作为接口测试的常用工具之一&#xff0c;在我们的测试中经常会用到&#xff0c;往期的文章中&#xff0c;我们也分享过jmeter的各种功能和用法&#xff0c;基本覆盖了方方面面&#xff0c;可以满足各种接口测试的需求。但实际测试中我们也会发现&#xff0c;jmeter这么强…

设计模式——装饰模式(Decorator Pattern)

概述 装饰模式可以在不改变一个对象本身功能的基础上给对象增加额外的新行为&#xff0c;装饰模式是一种用于替代继承的技术&#xff0c;它通过一种无须定义子类的方式来给对象动态增加职责&#xff0c;使用对象之间的关联关系取代类之间的继承关系。在装饰模式中引入了装饰类&…