SQL进阶理论篇(十):数据库中的锁

文章目录

  • 简介
  • 按照锁的粒度进行划分
  • 从数据库管理的角度进行划分
  • 从程序员的角度进行划分
  • 为什么共享锁会发生死锁?
  • 参考文献

简介

索引和锁,是数据库中的两个核心知识点。

索引的相关知识点,在之前的几章里我们已经介绍的差不多了。接下来我们会重点讲解一下锁的相关知识。

事务的隔离级别,在底层就是通过锁来实现的。而加锁的目的,就是为了保证数据的一致性。

本节我们将重点描述以下几个问题:

  • 锁有哪些划分方式?
  • 为什么共享锁会发生死锁?
  • 乐观锁和悲观锁的思想是什么?乐观锁有哪两种实现方式?
  • 多个事务并发,发生死锁时该如何解决?如何降低死锁发生的概率?

按照锁的粒度进行划分

锁是用来对数据进行锁定的。我们可以根据被锁定对象的粒度大小来对锁进行划分,即:行锁、页锁和表锁

行锁,按照行粒度对数据进行锁定。由于锁定力度小,所以发生锁冲突的概率低,理论上可以实现的并发度很高。但是按行加锁,对资源的消耗太大了,而且加锁也比较慢,容易出现死锁现象。

页锁,就是在页的粒度上对数据进行锁定。因为一个页上可以有很多数据行,所以在使用页锁的时候,很容易会出现数据浪费的情况(即使只是想锁小部分数据,也得锁上一整页),但是这种浪费有限,顶多就是浪费个几页。页锁的开销介于行锁和表锁之间,会出现死锁,并发度一般。

表锁,就是对数据表进行锁定。其锁定粒度很大,出现锁冲突的概率也很高,对并发的影响较大。好处是加锁的开销小,加锁很快。

以上三类锁是数据库中相对常见的三种锁,除此之外其实还有区锁和数据库锁,分别针对区和数据库的粒度。

不同的数据库或者不同的引擎支持的锁粒度并不相通。以MySQL为例,InnoDB支持行锁和表锁,但MyISAM只支持表锁,BDB引擎则支持页锁和表锁。Oracle支持行锁和表锁,SQLServer同时支持行锁、页锁和表锁。教程里整理的图如下:

在这里插入图片描述

需要注意,在实际使用中,每个层级的锁数量是有限的,因为锁会占用内存空间,所以锁空间的大小是有限的。

当某个层级的锁数量超过了这个层级的阈值时,就会进行 锁升级。所谓的锁升级,就是将多个细粒度的锁升级成一个更大粒度的锁。比如说在InnoDB中,将多个行锁换成一个表锁,从而减少锁空间的内存占用,当然,代价是并行度降低了。

从数据库管理的角度进行划分

从数据库管理的角度来划分的话,就是我们经常会见到的两种锁:共享锁和排它锁

共享锁,也叫做读锁或者S锁。共享锁锁定的数据可以被其他事务读取,但是不能修改。

在进行select的时候,就会把对象进行共享锁锁定,待到数据读取完毕后,才释放共享锁。这样子可以保证数据在读取时不会被修改。

我们也可以手动给某个对象加锁。

比如说给一个表加共享锁,可以使用:

LOCK TABLE product_comment READ;

这时候这张表就是只读模式了,如果此时再执行update语句,会提示:

ERROR 1099 (HY000): Table 'product_comment' was locked with a READ lock and can't be updated

解锁的话,可以使用:

UNLOCK TABLE product_comment;

如果是想给某一行加上共享锁,则可以写成这样:

SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 LOCK IN SHARE MODE

排它锁,也叫做独占锁、写锁或者X锁。其锁定的数据只允许进行锁定的事务使用,其他事务无法对已锁定的数据进行读取或者修改。

比如给一个表添加排它锁,可以这么写:

lock table product_comment write;

此时,其他事务就不能在这张表上读或者更新了,有兴趣可以开两个MySQL客户端试一下。

释放锁的话,则是执行:

unlock table;

同样的,想在某个数据行上添加排它锁,可以写成这样:

SELECT comment_id, product_id, comment_text, user_id FROM product_comment WHERE user_id = 912178 FOR UPDATE;

当我们在对数据进行更新的时候,就是insert、delete或者update的时候,数据库就会自动使用排它锁,避免其他事务对该数据资源进行操作。

当我们想要获取某个数据表的排它锁的时候,需要先看下这张表里有没有已经上了排它锁。如果这个数据表中的某个数据行被上了行锁,我们就无法获取排它锁。这时候就引出来意向锁。

意向锁(Intent Lock),就是给更大一级的空间示意里面是否已经上过锁。在实际场景中,如果我们给某个数据行加上了排它锁,那么数据库会自动给更大一级的空间(比如说数据页或者数据表)加上一个意向锁,用来告知其它事务,这个数据页或数据表里已经有人上过排它锁了。其他事务再不需要一行一行去查看到底这个表里有没有锁。

于是,如果事务想要获取某些记录的共享锁,那么就会给整个表添加 意向共享锁 。同理,如果事务想要获取某些记录的排它锁,就会给整个表添加 意向排它锁。意向锁会告诉其他事务,有人已经锁定了部分记录,你无权进行某些全表扫描的操作了。

从程序员的角度进行划分

从程序员的角度来看待锁的话,可以把锁分为乐观锁和悲观锁。这两种锁实际上是两种不同的看待数据并发的思维方式,它们并不是锁。这个简单了解下就行。

乐观锁(Optimistic Locking),认为对同一数据的并发操作是属于小概率事件,可以忽略,因此不用每次都对数据进行加锁,也就是不采用数据库自身的锁机制,而是通过程序,采用版本号机制或者时间戳机制来实现。

什么是版本号机制呢?

就是在表里增加一个version字段,事务里第一次读的时候会先获取version字段的取值,接下来如果需要对数据做update,则会执行UPDATE ... SET version=version+1 WHERE version=刚刚的version取值。如果没有其他事务对这条数据做过修改,那么本次update就成功了,反之,本次update失败(因为version已经被其他事务修改过了,你保存的这个version值已经找不到数据了)。

什么是时间戳机制呢?

跟版本号一样,只不过添加的是一个时间戳字段,更新的时候判断时间戳字段跟之前读到的是不是一样,一样就成功更新,否则就失败。

所以乐观锁实际上就是程序员自己控制数据并发操作的权限,自行判断数据是否被并发修改过。

悲观锁(Pessimistic Locking),也是一种思想,对数据会被并发修改持保守态度,指代的是通过数据库本身的锁机制来保证数据一致性

与行锁、共享锁等的关系如图:

在这里插入图片描述

乐观锁和悲观锁的适用场景:

  • 乐观锁适合读操作多的场景,相对的写操作很少。其优点是不存在死锁问题。但是要完全排掉相关的数据库操作。
  • 悲观锁则适合写操作多的场景。因为写操作的排它性,可以有效防止读写和写写的冲突。

为什么共享锁会发生死锁?

简单的说一个场景,就是事务A和B都对指定数据行进行了select查询,从而分别获取了对指定数据行的读锁,接着不提交事务,都各自对这条数据进行update。

对事务A来讲,其update会因为事务B持有读锁而失败,然后它会提示超时,重新执行事务。

而对事务B来讲,其update则会因为事务B持有读锁而失败,同样提示超时,重新执行事务。两边这就陷进死循环了,死锁发生。

当死锁发生的时候,只能让其它事务进行回滚,指定一个事务获取锁完成事务,然后将锁释放掉,再换下一个事务。

可以采取什么方式避免死锁的发生呢?

  • 如果事务涉及多个表,各部分操作比较复杂,那么可以考虑一次性锁定所有资源,而不是逐步获取。比如说MyISAM引擎就是这样,总是一次性获取全部的锁,要么全部满足可以执行,要不就全部等待;
  • 如果需要更新表中大部分数据,可以考虑使用表锁来代替行锁,即使用锁升级;

避免死锁,其实就是破坏产生死锁的四个必要条件:

  • 互斥条件:同一时刻,资源只能被一个对象使用;
  • 占有且等待条件:对象占有资源,同时在等待被其他对象占有的资源;
  • 不可剥夺条件:已经分配的锁不能强制剥离,只能有持有该锁的事务主动释放;
  • 循环等待条件:对象A占有对象B需要的资源,对象B占有对象A需要的资源。

以上条件必须同时具备,才能产生死锁。这个了解下就行。

参考文献

  1. 30丨锁:悲观锁和乐观锁是什么?

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA简易加减法计算器设计

题目要求: (1)设计10以内的加减法计算器。 (2)1个按键用于指定加法或减法,一个用于指定加数或被加数,还有两个分别控制加数或被加数的增加或减少。 (3)设置的结果和计算的…

开发了一年的IDEA插件,接口调试的强大工具,敢说吊打所有

前言 经历了整整一年的开发,现在这款插件终于可以测试了,可以到网站http://plugin.houxinlin.com 进行下载(Idea版本仅支持2021.1及以上)。 那么,这个插件到底解决了什么问题?以至于我花了一整年时间。 每个后端程序员都需要借助…

机器学习---推荐系统案例(一)

一、推荐系统-数据处理流程 推荐系统数据处理首先是将Hive中的用户app历史下载表与app浏览信息表按照设备id进行关联,然后将关联数据使用python文件进行处理,将数据预处理为label和feature两列的临时数据,后期经过处理转换成逻辑回归 模型的…

python读取excel数据 附实战代码

在Python中,可以使用pandas库来读取Excel文件中的数据。下面是一个简单的例子: import pandas as pd# 读取Excel文件 df pd.read_excel(example.xlsx)# 显示前5行数据 print(df.head())在上面的代码中,我们首先导入了pandas库,并…

Java 内存模型(JMM)探寻原理,深度讲解

目录 一. 前言 二. 为什么会有内存模型 2.1. 硬件内存架构 2.2. 缓存一致性问题 2.3. 处理器优化和指令重排序 三. 并发编程的问题 四. Java 内存模型(JMM) 4.1. Java 运行时内存区域与硬件内存的关系 4.2. Java 线程与主内存的关系 4.3. 线程…

深入探索Spring Batch:大规模批处理的领航者

🎬 鸽芷咕:个人主页 🔥 个人专栏:《linux深造日志》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! ⛳️ 写在前面参与规则 ✅参与方式:关注博主、点赞、收藏、评论,任意评论(每人最多评论…

万文详解JUC(超详细)

生命无罪,健康万岁,我是laity。 我曾七次鄙视自己的灵魂: 第一次,当它本可进取时,却故作谦卑; 第二次,当它在空虚时,用爱欲来填充; 第三次,在困难和容易之…

SpringBoot之响应案例的详细解析

2.3 案例 下面我们通过一个案例,来加强对请求响应的学习。 2.3.1 需求说明 需求:加载并解析xml文件中的数据,完成数据处理,并在页面展示 获取员工数据,返回统一响应结果,在页面渲染展示 2.3.2 准备工作…

Pipe转token教程

打开网站:https://www.satsx.io/pipe 选择Transfer,以及pipe 选择转账数量,点击select uxto关键是可以多选 填入买家地址,以及输入对应数量即可

如何确保对称密钥管理的存储安全?

确保对称密钥管理的存储安全是保障信息安全的重要一环。以下是一些建议,以确保对称密钥管理的存储安全: 使用安全存储设备:选择使用经过验证的安全存储设备来存储对称密钥。这些设备通常具有高度的物理安全性,可以防止未经授权的访…

Acre1-6000电气火灾监控系统在工矿企业的应用——安科瑞 顾烊宇

摘要:主要介绍了电气火灾的主要原因、几种电气火灾监控系统的构成和设立意义。参照各规范,讨论了宜设立电气火灾监控系统的场所。该系统的设立可大大减少电气火灾事故的发生,对保证人们的生命财产安全具有重要意义。 关键词:电气火灾&#x…

透明之光:探讨可解释性人工智能的前沿

导言 随着人工智能技术的飞速发展,可解释性人工智能(Explainable AI, XAI)成为关注焦点。本文将深入研究可解释性人工智能的背景、技术原理以及在不同领域的应用。 1. 背景与挑战 在许多领域,人工智能模型的黑盒性引发了关于决策…