【MySQL】MySQL锁(二)表锁与行锁测试

MySQL锁(二)表锁与行锁测试

上篇文章我们简单的了解了一大堆锁相关的概念,然后只是简单的演示了一下 InnoDB 和 MyISAM 之间 表锁 与 行锁 的差别。相信大家还是意犹未尽的,今天我们就来用代码说话,实际地操作一下,看看如何进行手动的加 表锁 与 行锁 ,并进行一些相关的实验测试。

手动锁表

首先来看 锁表 相关的操作。一般来说,我们手动锁表大部分情况下是为了增加从库或者进行数据迁移的时候来使用。在这些业务场景中,我们要保证从库在建立同步的时候,主库不会出现新的数据,因此,往往用得最多的就是直接 读锁 。这样就可以保证在主库可读的情况下不会有新的数据写入。

-- 客户端1
mysql> LOCK TABLES test_user2 READ;
Query OK, 0 rows affected (0.00 sec)-- 客户端2
mysql> LOCK TABLES test_user2 READ;
Query OK, 0 rows affected (0.00 sec)-- 客户端2
mysql> LOCK TABLES test_user2 WRITE;
-- 等待

在上面的测试语句中,我们让 客户端1 锁住了一张表,这个时候,客户端2 也是可以再加一个读锁的,还记得之前讲过的吗,S 锁是可以共享的。但是,接着我们又让 客户端2 加一个 写锁 ,这个时候就无法正常加了。也就是说,S 与 X 是互斥的,有一个拿到读锁之后,写锁就没办法再加上了,只能等 客户端1 的锁释放之后才能进行操作。这时你也可以试试更新、删除、插入一条数据,看看能不能成功。要注意,我们现在是锁的整表哦。

接下来,我们就来试试为整张表锁上 写锁 。

-- 客户端1 
mysql> LOCK TABLES test_user2 WRITE;
Query OK, 0 rows affected (0.00 sec)-- 客户端2
UPDATE test_user2 SET username = 'fff' WHERE id = 1212121;
-- 等待
mysql> LOCK TABLES test_user2 READ;
-- 等待

当我们上了 写锁 之后,更新肯定是不行了,然后再尝试上一个 读锁 也是不行的哦。要查看表上锁的情况,我们可以通过下面这个命令查看

mysql> SHOW OPEN TABLES WHERE In_use > 0;
+-----------+------------+--------+-------------+
| Database  | Table      | In_use | Name_locked |
+-----------+------------+--------+-------------+
| blog_test | test_user2 |      1 |           0 |
+-----------+------------+--------+-------------+
1 row in set (0.00 sec)

In_user 字段大于 1 的,表示的就是这张表正在使用,也就是有事务或者客户端锁定了这张表。解锁语句就不用我多说了吧,把 LOCK 换成 UNLOCK 就可以啦。但是 UNLOCK 不能针对某一张表,而是使用 UNLOCK TABLES; MySQL 会自动进行解锁释放。

全局锁

除了单独锁一张表之外,我们还可以锁一个库中所有的表。很简单,就是上面锁表的语句不加表名即可。这个大家可以自己尝试一下,我们接着说另一个全局锁的功能,它锁的是整个 MySQL 实例,也就是说连库都包进去了。

FLUSH TABLES WITH READ LOCK;

一般这种锁就是做全量的数据备份或者迁移时会使用。不过在备份的时候我们其实还可以通过别的方式,不用加锁来实现,这个我们将来学习备份相关的内容时再说。

行锁及意向锁

上篇文章中,我们已经介绍过 意向锁 相关的知识,也了解到在加 行锁 的时候也会为整个表加一个 意向锁 ,真实情况是怎样的呢?我们用例子来看下。

-- 共享锁及意向共享锁
mysql> begin;
mysql> SELECT * FROM test_user2 WHERE id = 1212121 LOCK IN SHARE MODE;-- 查看锁信息
mysql> SELECT object_schema,object_name,index_name,lock_type,lock_mode,lock_data FROM performance_schema.data_locks;
+---------------+-------------+------------+-----------+---------------+-----------+
| object_schema | object_name | index_name | lock_type | lock_mode     | lock_data |
+---------------+-------------+------------+-----------+---------------+-----------+
| blog_test     | test_user2  | NULL       | TABLE     | IS            | NULL      |
| blog_test     | test_user2  | PRIMARY    | RECORD    | S,REC_NOT_GAP | 1212121   |
+---------------+-------------+------------+-----------+---------------+-----------+

在这里,为了演示方便,我们直接上了一个 读锁 ,也就是使用 LOCK IN SHARE MODE 来在事务中启用一个 共享锁 。然后我们就可以查询 performance_schema.data_locks 这个系统表中相关的信息。可以看到返回的信息中有两行数据,第一条数据中的 lock_mode 字段显示的是就是一个 IS ,lock_type 字段显示的是 TABLE ,也就是表级别的一个 意向共享锁 。没错吧,确实是在上锁的时候会加 意向共享锁 吧。

第二条数据中,lock_type 的 RECORD 表示的这是一条记录锁,也就是 行锁 ,后面的 lock_mode 中有两个内容,S 表示共享锁,REC_NOT_GAP 表示是没有 GAP 锁,这个东西我们放到 间隙锁 的文章中再进行说明。

对于上面的 意向共享 S 锁 来说,我们可以继续加表锁,不过只能加 读锁 ,无法加 写锁 。

-- 可以加读锁
mysql> LOCK TABLES test_user2 READ;
Query OK, 0 rows affected (0.00 sec)-- 无法加写锁,等待
mysql> LOCK TABLES test_user2 WRITE;

接下来我们再看看 排它锁 的加锁情况,你可以继续使用 SELECT ... FROM FOR UPDATE 这种形式,也可以直接使用 UPDATE 语句,在这里我们就使用 UPDATE 语句来演示。

-- 排它锁及意向排它锁
mysql> begin;
mysql> UPDATE test_user2 SET name = 'fff' WHERE id = 1212121;-- 锁情况
mysql> SELECT object_schema,object_name,index_name,lock_type,lock_mode,lock_data FROM performance_schema.data_locks;
+---------------+-------------+------------+-----------+---------------+-----------+
| object_schema | object_name | index_name | lock_type | lock_mode     | lock_data |
+---------------+-------------+------------+-----------+---------------+-----------+
| blog_test     | test_user2  | NULL       | TABLE     | IX            | NULL      |
| blog_test     | test_user2  | PRIMARY    | RECORD    | X,REC_NOT_GAP | 1212121   |
+---------------+-------------+------------+-----------+---------------+-----------+-- 无法加锁
mysql> LOCK TABLES test_user2 READ;

很明显,加了 排它锁 之后,意向锁也就变成了 IX ,行锁也是显示为 X 锁了。这个时候给整个表加任何锁都不行了。

行锁更新两条不同的数据

行锁的优势是什么?当然就是可以同步地更新不同的行记录,这一点也是比 MyISAM 之类的表锁引擎强大的地方。我们先来看看更新同一条数据会怎么样。

-- 事务1
mysql> begin;
update test_user2 set name = 'fff' where id = 1212121;-- 事务2
mysql> begin;
mysql> update test_user2 set name = 'fff' where id = 1212121;
-- 阻塞

在两个事务中更新同一条数据,就会遇到锁的情况,这是因为什么呢?事务隔离级别的自动加锁呀,相信大家还没有忘掉事务隔离级别吧。对于 UPDATE 语句来说,都会自动加上 排它锁 ,同时更新一行当然是不可以的咯,但是我们可以同时更新不同的行数据。

--1
mysql> begin;
mysql> update test_user2 set name = 'fff' where id = 1212121;--2
mysql> begin;
mysql> update test_user2 set name = 'fff' where id = 1212122;
-- 正常

行锁升级到表锁

之前我们提到过,InnoDB 的行锁是在一些情况下会升级到表锁的,除了 DDL 时会加的 元数据锁 之外(下回我们讲它),还有一种情况就是如果不走索引,也会让行锁变成表锁。

-- 事务1
mysql> begin;
mysql> update test_user2 set username = 'ffff' where username ='fff';-- 事务2
mysql> update test_user2 set username = 'gggg' where id = 1212122;
-- 阻塞

在上面的测试代码中,我们更新时的条件是 username ,这个字段并没有索引,在这种情况下,整个更新语句会扫全表,同时锁也会变成 表锁 ,因此,下面针对某条单行数据的更新语句就会阻塞。这就是 行锁 升级或者说是退化为 表锁 的情况。

你可以尝试为 username 加上一个索引之后,再试试上面的效果,就会发现 行锁 生效了。

总结

通过今天的学习,相信大家对锁的概念会有更深的理解了吧。不过上一篇文章中的概念性的内容真的非常重要,否则看今天的内容也会是一脸懵逼的。在文章中,我可能有时候会说 读锁 ,有时候会说 共享锁 ,这么做的目的也是为了能够加深大家对这些名词的印象。这样在面试的时候,不管面试官问的是 读锁 还是 共享锁 或者 S 锁,你都能很快明白它们是一个意思。

下篇文章我们将继续学习 元数据锁 以及 间隙锁 相关的知识和概念,关于锁的内容知识点非常密集,千万别错过哦!

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

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

相关文章

ES链接报connection reset by peer

这里写自定义目录标题 ES链接报connection reset by peer解决方法主要是删除node.lock文件重启es服务问题解决 ES链接报connection reset by peer 问题描述服务端报错connection reset by peer 在服务器上去curl返回的也是connection reset by peer 链接重置,说明e…

「GO基础」文件名规范、关键字与标识符

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

一键升级 package.json 下所有依赖的版本

命令: npx npm-check-updates -u这个命令会生成一个新的package.json文件,其中包含了所有依赖项的最新版本。然后,你可以运行npm install来安装这些更新后的依赖项。 请注意,在更新依赖项之前,最好备份你的项目&…

元数据管理Atlas

文章目录 一、Atlas概述1、Atlas入门2、Atlas架构原理 二、Atlas安装1、安装环境准备1.1 安装Solr-7.7.31.2 Atlas2.1.0安装 2、Atlas配置2.1 Atlas集成Hbase2.2 Atlas集成Solr2.3 Atlas集成Kafka2.4 Atlas Server配置2.5 Kerberos相关配置2.6 Atlas集成Hive 3、Atlas启动 三、…

ARM的学习

键控灯 mykey.h #ifndef __MYKEY_H__ #define __MYKEY_H__ #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_gic.h" #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_exti.h" void key1_int_config(); void key2_int_config(…

用海外云手机高效率运营TikTok!

很多做国外社媒运营的公司,想要快速引流,往往一个账号是不够的,多数都是矩阵养号的方式,运营多个TikToK、Facebook、Instagram等账号,慢慢沉淀流量变现,而他们都在用海外云手机这款工具! 海外云…

使用EasyExcel和POI操作Excel实现文件上传和下载

使用easyExcel实现文件读写 实现流程 1.导入依赖 2.定义数据模型 3.定义监听器 4.读取或写入数据 5.释放资源 实现 导入依赖 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.3</…

知识管理系统(KMS):一文扫盲,能和chatGPT相融吗?

一、什么是KMS&#xff0c;有什么作用 KMS&#xff08;Knowledge Management System&#xff09;知识管理系统是一种用于组织、存储、共享和利用知识的软件系统或平台。它旨在帮助组织有效地管理和利用内部和外部的知识资源&#xff0c;以支持决策、创新和持续学习。 KMS知识管…

华为云CodeArts IDE For Python 快速使用指南

CodeArts IDE 带有 Python 扩展&#xff0c;为 Python 语言提供了广泛的支持。Python 扩展可以利用 CodeArts IDE 的代码补全、验证、调试和单元测试等特性&#xff0c;与多种 Python 解释器协同工作&#xff0c;轻松切换包括虚拟环境和 conda 环境的 Python 环境。本文简要概述…

数据治理——元数据管理实施步骤

一、元数据管理概述 1.1 数据管理面临的问题 数据治理的概念是对数据数据管理的管理&#xff0c;在数据管理的过程中遇到的问题有&#xff1a; 1.1.1 数据不可理解 海量数据&#xff0c;标准不统一&#xff0c;各系统、各部门对统一指标和概念的解释不一致&#xff0c;统计口…

python机器学习库中Scikit-learn和TensorFlow如何选择?

在Python机器学习库中&#xff0c;Scikit-learn和TensorFlow是两个非常流行的选择&#xff0c;但它们各自有不同的特点和适用场景。以下是根据搜索结果的一些考虑因素&#xff0c;帮助你做出选择&#xff1a; 1. 项目需求&#xff1a; 如果你的项目主要涉及传统的机器学习算…

OpenCV基本图像处理操作(三)——图像轮廓

轮廓 cv2.findContours(img,mode,method) mode:轮廓检索模式 RETR_EXTERNAL &#xff1a;只检索最外面的轮廓&#xff1b;RETR_LIST&#xff1a;检索所有的轮廓&#xff0c;并将其保存到一条链表当中&#xff1b;RETR_CCOMP&#xff1a;检索所有的轮廓&#xff0c;并将他们组…

魔方网表 存在 mailupdate.jsp接口 任意文件上传漏洞

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 魔方网表mailupdate.jsp接口存在任意文件上传漏洞 …

数据仓库—维度建模—事实表设计

事实表 事实表是数据仓库中的核心表,用于记录与业务过程相关的事实信息,是进行数据分析和挖掘的主要数据来源。 在ER模型中抽象出了有实体、关系、属性三种类别,在现实世界中,每一个操作型事件,基本都是发生在实体之间的,伴随着这种操作事件的发生,会产生可度量的值,…

ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写教程

原文链接&#xff1a;ChatGPT深度科研应用、数据分析及机器学习、AI绘图与高效论文撰写https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247601506&idx2&sn5dae3fdc3e188e81b8a6142c5ab8c994&chksmfa820c85cdf58593356482880998fc6eb98e6889b261bf621e1d…

Git 实用技巧2——新建空白分支 | 重命名分支 | 回退到历史 commit

git version 2.39.2.windows 1. 新建空白分支 参考&#xff1a;Git - git-switch Documentation: https://git-scm.com/docs/git-switch/zh_HANS-CN 使用 --orphan 参数&#xff0c;创建一条不基于任何现有提交的空白分支。其初始提交&#xff08;即 HEAD&#xff09;不指向任…

箭头函数多个函数体

当箭头函数需要多个语句组成函数体时&#xff0c;需要使用花括号 {} 将多个语句包裹起来&#xff0c;并且需要显式地使用 return 关键字返回值。下面是一个包含多个函数体语句的箭头函数示例&#xff1a;‘ var greet name > { var greeting "Hello, "; …

了解在 Docker 和 Kubernetes 中运行Go程序的影响

根据 2021 年对 Go 开发人员的调查,使用 Go 编写服务是最常见的用途。同时,Kubernetes 是部署这些服务的最广泛使用的平台。了解在 Docker 和 Kubernets 中运行 Go 的含义非常重要,可防止出现 CPU 节流等常见情况。 GOMAXPROCS 变量定义了负责同时执行用户级代码的操作系统线…

python生成二维码

要在Python中生成二维码&#xff0c;可以使用第三方库qrcode。首先&#xff0c;确保已经安装了qrcode库&#xff1a; pip install qrcode然后&#xff0c;使用以下代码生成二维码&#xff1a; import qrcodedata "https://mp.csdn.net/mp_blog/creation/editor?spm100…

c++ std::map 介绍

在 C 中&#xff0c;字典通常指的是 std::map&#xff0c;它是一个关联容器&#xff0c;用于存储键-值对&#xff0c;并且按照键的顺序进行排序。除了 std::map&#xff0c;C 标准库还提供了其他一些类似字典的容器&#xff0c;比如 std::unordered_map&#xff08;无序字典&am…