MySQL加锁规则

项目编写涉及到数据持久化一般选择使用MySQL。由于时间原因,数据库使用我选择了无脑三板斧:1. 建立了索引加速查询、2. 关闭自动提交事务、3. 在需要确保原子性的数据库操作之间手动创建和提交事务

这么一看,仿佛即使是实际开发也与你此前听闻的一些MySQL相关名词:读写锁间隙锁多版本并发控制redo logbin logundo log毫不相干,在讲本文的主题之前,我先引入一个真实场景。

某次不够规范的小组开发过程中,开发成员选择测试程序的方式比较原始,大家共享一个测试数据库,各自使用测试账号进行接口的测试,这就意味着数据库中的记录在某一时刻有可能被多个事务访问,甚至在其他人测试的同时,某张数据表的结构被另一位同学修改。

多事务并发访问,反映到开发者这边,就是查询接口有时速度很慢。如果你是直接使用数据库管理工具操作数据库表数据/结构,对应的就是Navicat不时的陷入较长时间的无响应状态。

当然导致数据库访问速度变慢的原因有很多:sql语句编写不规范、数据库服务器的性能差、网络状况不佳等,但是本文所侧重的点在于探究MySQL锁机制,在其中发挥了什么作用。

相信在完成本文的阅读之后,你会明白上面的场景的发生,可能是MySQL的锁机制从中作祟。

MySQL的锁有哪几种

全局锁

MySQL可以通过显式命令对整个数据库实例加全局读锁:

此时整个数据库处于只读状态,所有数据记录的更新、数据库/表结构的改动提交都会被阻塞,这可以用于全库的数据备份。

表级锁

表锁

表锁可以通过以下显式命令实现对一个表添加读/写锁:

如果A线程为t1表添加了读锁,为t2表添加了写锁。则其他线程将只能读t1,写t1被阻塞;读/写t2都会被阻塞。而A线程在执行unlock tables之前,也只能执行读t1、读/写t2的操作。

元数据锁(metadata lock)

MDL锁不需要显式使用,在访问一个表的时候会被自动加上,并且当事务完成提交时释放。当对一个表数据做CRUD操作的时候,自动加MDL读锁;当对该表结构作出改动的时候,自动加MDL写锁。

  • 读锁之间不互斥,因此多个线程才可以同时访问一张数据表。
  • 读写锁之间、写锁之间是互斥的(被读锁占用时,加写锁的线程被阻塞/被写锁占用时,加读锁/写锁的线程都被阻塞),这也是为了确保表结构的修改和表的数据的操作不发生冲突。

这里展示一个多线程并发操作同一个数据表的案例:

这里线程B会因为线程A的事务还没有提交,而添加列的操作需要获取MDL写锁因此被阻塞,同时线程C申请MDL读锁的请求又被阻塞在了线程B申请MDL写锁的请求之后,此时表t在线程A事务提交之前,完全丧失了读写能力

或许此时你已经对于为什么多人调试程序时数据库访问不时出现卡顿有了一些自己的想法,当然这只是锁机制的冰山一角

行级锁

通过上面的讲解,我们明白了,所谓的读写锁并不是单指一个锁叫读锁/写锁,而是指不同粒度的锁有读锁和写锁两种状态,允许的并发程度也有所不同。行级锁也是如此(针对记录行的锁,锁粒度进一步缩小),行锁的存在也使得事务并发访问数据库的性能进一步的提高,并且依旧有读写锁之分,下面介绍。

但区别于全局锁和表级锁,MySQL行锁是由各个存储引擎自己实现的,并不是所有的存储引擎都支持行锁(MyISAM不支持),由于现在MySQL用户大多选择使用InnoDB存储引擎,所以本文将以InnoDB引擎为默认选择

两阶段锁协议

在InnoDB事务当中,行锁在需要的时候添加,并且直到事务提交才释放(锁的添加和释放分两个阶段进行),举个例子:

事务A(线程A)在提交之前,占有id=1这条行记录的写锁,事务B(线程B)修改同一行的操作将被阻塞。

死锁与检测

死锁原本是操作系统当中的概念,意思是多个线程都在等待其他线程释放自己需要的资源,使得这些线程陷入无限制的等待。

在这个例子当中,线程A的事务和线程B的事物分别占有id=1id=2这两条记录的写锁,使得两个线程在试图获取其他线程占用的锁资源时陷入死锁。

InnoDB存储引擎默认开启了死锁检测,每个新来的被阻塞的线程,都会主动判断是否是自己的加入导致死锁(检测逻辑就是判断自己需要的行资源是否被别的线程的事务占有),时间复杂度O(n),一旦检测到,则回滚当前线程的事务,确保其他线程可以得到执行。

这里你会发现,如果同时有多个线程修改同一条记录,一旦并发度很高,则需要消耗O(n^2)时间去完成死锁检测,就会消耗大量CPU资源在死锁检测上,而使得数据库IO的性能下降。

此时你是否又对我最初给出的小组开发时访问数据库慢的场景有了自己的思考,其实在高QPS情况下,发生死锁检测的概率是大大高于小组开发场景的

因此控制热点记录的并发访问数量,是提升数据库IO性能的重要前提。

多版本并发控制(MVCC)

上面讲述了InnoDB的update操作会占用行记录的写锁,那么你自然而然就想到,select查询操作是否就占用了行记录的读锁呢?不完全正确,这就不得不提及MySQL的InnoDB引擎的用于控制事务隔离级别的多版本并发控制机制

简言之就是每条行记录值的变化是由一个链式的结构组织的,存放在undo log文件当中,undo log在事务发生回滚的时候,用于回溯事务对行记录的修改过程。

而InnoDB存储引擎默认的事务隔离级别是可重复读(Read Repeatable),简单来说:就是当事务A启动期间,普通的select查询将无法访问到其他事务在此期间对表记录的改动。

关于多版本并发控制(MVCC)这里我没有过多深入讲解,详情给出我的另一篇文章:一文搞懂MySQL事务的隔离性如何实现|MVCC - 掘金

快照读

对于普通的查询操作,你大致了解InnoDB引擎管理的表的行记录变更是链式组织的,那么每一条记录就相当于一个个的快照,因此普通的select查询操作被称为快照读,会读取到自己可见的最近一个版本(但不一定是最新版本),快照读并不加锁(也就是没有获取读锁)。

至于具体读到哪个版本的快照,在上面链接给出的文章中有详细讲解。

当前读

这里给出了两种不同的当前读方式,当前读可以读取到undo log版本链上的最新记录,不同之处在于,第一条sql获取了id=1这条行记录的读锁(在其他事务已经持有id=1行记录的写锁时将被阻塞);第二条select查询虽然也是当前读,但是获取了id=1这条记录的写锁(在其他事物已经持有id=1行记录的读/写锁时将被阻塞)。

上面讲解死锁检测的时候我用更新语句获得了行记录的写锁,而这里,通过增加for update后缀,可以使得当前读操作也获取行记录的写锁

间隙锁

间隙锁的出现解决了幻读问题,那么先简述一下幻读的概念,以及幻读有什么问题。

幻读概述

  • InnoDB引擎的可重复读隔离级别下,普通查询是快照读,不会看到其他并发事务插入的数据,因此幻读在当前读情况下才会出现。
  • 幻读指当前读场景下,查询到了其他并发事务新插入的行(读到其他事务对行记录的修改,并不属于幻读,因为当前读就是会读取到行记录的最新版本)。

幻读的问题

这里用一张表t的操作来描述幻读带来的问题。

 

以下的分析建立在没有间隙锁的情况下(只是为了分析所作的假设):

  • 事务A的第一个sql查询c=1的记录,获得(1,1),此时添加了for update,从语义上就是希望锁住所有c=1的行记录。
  • 并且在RR隔离级别下,所有扫描到的行数据都会加行锁,因为c字段没有索引,比较c=1的操作需要全表扫描,因此事务A的第一条sql在当前读的情况下,为整张表的3条行记录都添加了写锁。
  • 此时事务B并发插入了一条(2,1)的记录,并且成功。
  • 事务A的第二个sql依旧查询c=1的记录,获得(1,1)、(2,1)两条记录,从语义上违背了第一条sql的目的。(原本打算锁定所有c=1的记录,但是突然又冒出一条记录)

这里的核心问题就在于:即使所有扫描到的行记录都加上了锁,依旧无法阻止新记录的插入(因为要插入的记录不可能提前锁定),要避免幻读,就需要将记录之间的间隙锁定——间隙锁

Gap Lock

间隙锁在可重复读隔离级别下才有效,所以本文的描述都是基于RR级别(InnoDB存储引擎事务默认隔离级别),这里给出间隙锁配合行锁工作的一些规则:

  • 所有的锁是添加在索引上的
  • 加间隙锁的基本单位是next-key lock(前开后闭区间)
  • 查找过程中访问到的记录和区间才会加锁
  • 索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁
  • 索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁
  • 唯一索引上的范围查询会访问到不满足条件的第一个值为止

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是充放电振子理论?

CHAT回复:充放电振子模型(Charging-Reversal Oscillator Model)是一种解释ENSO现象的理论模型,这个模型把ENSO现象比喻成一个“热力学振荡系统”。 在这个模型中,ENSO现象由三个组成部分:充电(C…

利用CHAT写实验结论

问CHAT:通过观察放置在玻璃表面上的单个水滴,人们可以观察到水滴充当成像系统。探究这样一个透镜的放大倍数和分辨率。 CHAT回复:实验报告标题:利用玻璃表面的单一水滴观察成像系统的放大倍数和分辨率: 一、 实验目的…

你不得不知道的常用 Git 命令

最近在学习的时候发现 git 命令没有自己想象中那么简单,特此做一期 《 常用 Git 命令 》,不仅是给掘友分享,也能巩固自己学到的知识。 在此向大家推荐一个学习 git 指令的小游戏 Learn Git Branching,以通关的方式进行学习&#…

鸿蒙Harmony--状态管理器-@Observed装饰器和@ObjectLink装饰器详解

经历的越多,越喜欢简单的生活,干净的东西,清楚的感觉,有结果的事,和说到做到的人。把圈子变小,把语放缓,把心放宽,用心做好手边的事儿,该有的总会有的! 目录 一&#xff…

【壹基金儿童服务站瑞金站】寿司DIY亲子活动火热进行

1月13日下午,“大灰狼来啦....”在小朋友们一声声欢快律动的游戏里在象湖新城小区拉开了一场“寿司DIY”亲子主题活动序幕,活动由壹基金儿童服务站瑞金站(瑞金赋能公益)和爱心商家安心妈妈飞鹤奶粉联合主办。 热身游戏结束后&…

链接全域直播产业经济,天府锋巢直播产业基地10层正式起航

100㎡-400㎡多种类型的办公户型可选 精装全包 拎包入驻 【天府锋巢直播基地】 由德商产投与无锋科技联袂打造 坐落于天府新区核心区域科学城板块 包含电商直播、娱乐直播、跨境直播 多种直播业态的全域直播基地 基地【10层】于12月初全面竣工 招|商 |火&…

Jan, 一个开源 ChatGPT 替代品

Jan 是一个开源 ChatGPT 替代品,可以在您的计算机上 100% 离线运行。 Jan 可以在任何硬件上运行。从 PC 到多 GPU 集群,Jan 支持通用架构: Nvidia GPU(快速)Apple M 系列(快速)苹果英特尔Linu…

LLM之RAG实战(十五)| RAG的自动源引文验证技术

​ 在过去的一年里,检索增强生成(RAG)已经成为一种基于LLM的流行架构,旨在解决在基于知识的LLM最常见的挑战之一,可怕的幻觉。 一、RAG如何解决幻觉? RAG Pipeline包括两个关键组件:&…

基于Java SSM框架实现摄影器材租赁系统项目【项目源码+论文说明】

基于java的SSM框架实现摄影器材租赁系统演示 摘要 21世纪的今天,随着社会的不断发展与进步,人们对于信息科学化的认识,已由低层次向高层次发展,由原来的感性认识向理性认识提高,管理工作的重要性已逐渐被人们所认识&a…

Linux(Centos7)安装 jenkins(jdk11+jenkins2.375),并配置JDK,Maven,Git,GitLab

安装步骤 1. JDK11安装2. Maven安装3. git安装4. Jenkins2.375安装4.1 设置中文显示4.2 端口,用户权限修改4.3 插件下载4.4 全局工具配置4.4.1 Maven配置4.4.2 JDK配置4.4.3 Git配置 4.5 系统配置4.5.1 Gitee配置 4.6 构建测试 1. JDK11安装 #下载 yum -y install fontconfig …

怎样获取power shell 的全部可用命令?2/5(篇幅有点长,分成5份)

在power shell 窗口中,有一个获取全部可用命令的命令:get-command,获取到的命令有1640多个,够学习了吧?那么,power shell 命令有哪些类别呢? PowerShell命令可以分为以下几类: Cmdl…

高可用架构去中心化重要?

1 背景 在互联网高可用架构设计中,应该避免将所有的控制权都集中到一个中心服务,即便这个中心服务是多副本模式。 对某个中心服务(组件)的过渡强依赖,那等同于把命脉掌握在依赖方手里,依赖方的任何问题都可…

开启C++之旅(上):探索命名空间与函数特性(缺省参数和函数重载)

之前浅显的讲解了数据结构的部分内容:数据结构专栏 那么今天我们迎来了新的起点:C的探索之旅 文章目录 1.命名空间1.1引入命名冲突1.2命名空间1.2.1命名空间的定义1.2.2命名空间的使用 2.c的输入与输出3.缺省参数3.1概念3.2缺省参数分类 4.函数重载4.1概…

【深基9.例4】求第 k 小的数#洛谷(MLE)

题目描述 输入 n n n&#xff08; 1 ≤ n < 5000000 1 \le n < 5000000 1≤n<5000000 且 n n n 为奇数&#xff09;个数字 a i a_i ai​&#xff08; 1 ≤ a i < 10 9 1 \le a_i < {10}^9 1≤ai​<109&#xff09;&#xff0c;输出这些数字的第 k k k 小…

吉祥物如何解锁虚拟主持人身份,赋能品牌营销?

在互联网突破时空的整体语境下&#xff0c;一个吉祥物可以解锁虚拟主持人身份&#xff0c;结合动作捕捉技术&#xff0c;活跃于品牌线上线下营销活动场景&#xff0c;让吉祥物虚拟主持人凭借其“萌”、的特征&#xff0c;带给用户亲近感&#xff0c;快速拉近品牌与用户的距离&a…

基于Web的航空航天数字博物馆推荐系统

介绍 项目背景&#xff1a; 航空航天数字博物馆推荐系统是一个基于Web开发的应用&#xff0c;旨在为用户提供一个全面的航空航天领域的数字博物馆体验。通过展品展示、分类筛选和个性化推荐等功能&#xff0c;用户可以更好地了解航空航天知识和文化&#xff0c;并丰富参观体验…

关于git删除仓库中原本应该忽略的文件的研究

开门见山&#xff0c;先抛出一个结论&#xff1a; 任何被提交到远程仓库中的数据&#xff0c;都不能被彻底删除&#xff0c;只要提交上去了&#xff0c;就会永远留存。 任何被提交到远程仓库中的数据&#xff0c;都不能被彻底删除&#xff0c;只要提交上去了&#xff0c;就会…

centos7 arm服务器编译安装gcc 8.2

前言 当前电脑的gcc版本为4.8.5&#xff0c;但是在编译其他依赖包的时候&#xff0c;出现各种奇怪的问题&#xff0c;会莫名其妙的中断编译。本地文章讲解如何自编译安装gcc&#xff0c;替换系统自带的gcc。 环境准备 gcc 需要 8.2&#xff1a;下载地址 开始编译 1、解压gcc…

服务器 conda update 失败解决方法

1. 强制 conda update 租借一台服务器&#xff0c;发现 conda 版本是4.10.3&#xff0c;需要升级&#xff0c;使用了如下命令都没有效果&#xff0c;仍然是一样的版本 conda update conda update --all conda update -n base -c defaults conda最后强制用conda-forge通道更新…

基于Java SSM框架实现学生成绩管理系统项目【项目源码+论文说明】计算机毕业设计

基于java的SSM框架实现学生成绩管理系统演示 摘要 学生成绩是高校人才培养计划的重要组成部分&#xff0c;是实现人才培养目标、培养学生科研能力与创新思维、检验学生综合素质与实践能力的重要手段与综合性实践教学环节。而学生所在学院多采用半手工管理学生成绩的方式&#…