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

MySQL中的锁详解

1.概念

锁是计算机协调多个进程或者线程并发访问某一资源的机制。那么如何保证数据并发访问的一致性、有效性是数据库必须解决的一个问题,锁的冲突也是影响数据库并发访问性能的一个重要因素,所以数据库中锁的应用极为重要,其复杂度也更高。

锁的分类,以锁的颗粒度为三类:

  1. 全局锁:锁定数据库中的所有表。
  2. 表级锁:每次操作锁住整张表。
  3. 行级锁:每次操作都锁到对应行的数据。

接下来就分别对这几种锁做一个解释。

 数据库锁是数据库管理系统用来管理对数据库中数据的并发访问的一种机制。锁的存在主要是为了保证数据的完整性和一致性,以及支持事务的原子性。以下是全局锁、表级锁和行级锁的详细解释:

1.  全局锁(Global Lock):

•  全局锁是一种锁定数据库中所有表的锁。当数据库处于只读模式时,通常使用全局锁。
•  在这种模式下,没有写操作被允许,只能执行读操作。这可以用于数据库的备份或维护期间,以防止数据被修改。
•  全局锁的范围是最广的,它锁定了整个数据库实例,所有试图进行写操作的事务都会被阻塞。

-- 全局锁,整个数据库处于只读状态,其他操作均阻塞
FLUSH TABLES WITH READ LOCK

-- 释放全局锁
UNLOCK TABLES

2.  表级锁(Table-Level Lock):

•  表级锁是锁定数据库中特定表的锁。当一个事务对表进行写操作(如 INSERT、UPDATE、DELETE)时,通常会请求表级锁。
•  表级锁会阻止其他事务对同一张表进行写操作,但仍然允许其他事务对这张表进行读操作,这取决于锁的类型(共享锁或排他锁)。
•  表级锁的粒度比全局锁小,它只锁定涉及的特定表,其他表可以被并发访问。 

3.  行级锁(Row-Level Lock):

•  行级锁是锁定数据库表中特定行记录的锁。这种锁的粒度最细,只锁定事务操作影响到的具体行。
•  行级锁通常在执行精确的数据操作时使用,如 UPDATE 某个具体的行或 DELETE 某个具体的行。

共享锁和排他锁 

  • 共享锁(S Lock):其他事务可读,但不可写
  • 排他锁(X Lock):其他事务不能读取也不能写

 这两种行锁之间的兼容性如下:7bb394b92dc24592979e8d1fe676bdd5.png

 共享锁和共享锁可以兼容,排他锁和其它锁都不兼容。例如,事务 A 获取了一行数据的共享锁,事务 B 可以立即获得该数据行的共享锁,也就是锁兼容;但是此时事务 B 如果想获得该数据行的排他锁,则必须等待事务 A 释数据行上的共享锁,此种情况存在锁冲突。

默认情况下,数据库中的锁都可以自动获取;但是也可以手动为数据进行加锁。我们来看一个示例,首先创建一个表:

create table t(
  id int auto_increment primary key,
  c1 int,
  c2 int,
  c3 int
);
create unique index idx_t_c1 on t(c1);
create index idx_t_c2 on t(c2);

insert into t(c1,c2,c3) values (1,1,1),(2,3,4),(3,6,9);

其中,id 是主键;c1 上创建了一个唯一索引;c2 上创建了一个非唯一索引;c3 上没有索引。

接下来的示例都使用 MySQL 默认的隔离级别 Repeatable Read,除非另有说明。

然后创建两个数据库连接 T1 和 T2,先在 T1 中锁定一行数据:

-- T1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t where id = 1 for share;
+----+------+------+------+
| id | c1   | c2   | c3   |
+----+------+------+------+
|  1 |    1 |    1 |    1 |
+----+------+------+------+
1 row in set (0.00 sec)

我们在事务中使用select ... for share语句获得了数据行 id = 1 上的共享锁;对于 MySQL 8.0 之前的版本,可以使用select ... lock in share mode命令。

由于 InnoDB 中的自动提交 autocommit 默认设置为 ON,我们必须在事务中为数据行加锁;或者将 autocommit 设置为 OFF。

然后在 T2 中执行以下语句: 

-- T2
mysql> select * from t where id = 1 for share;
+----+------+------+------+
| id | c1   | c2   | c3   |
+----+------+------+------+
|  1 |    1 |    1 |    1 |
+----+------+------+------+
1 row in set (0.00 sec)

结果显示,在 T2 中成功获取改行数据上的共享锁。然后尝试获取排他锁:

-- T2
mysql> select * from t where id = 1 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

使用select ... for update命令获取排他锁,此时该命令会一直处于等待状态并且最终超时。也就是说,共享锁和排他锁不兼容。

最后,在 T1 中提交或者回滚事务:

-- T1
mysql> commit;

 

 

 


http://www.kler.cn/a/283271.html

相关文章:

  • 性能测试|JMeter接口与性能测试项目
  • 在Java中使用ModelMapper简化Shapefile属性转JavaBean实战
  • 解决表格出现滚动条样式错乱问题
  • 【MySQL从入门到放弃】InnoDB磁盘结构(一)
  • LLMs之Code:Github Spark的简介、安装和使用方法、案例应用之详细攻略
  • 券商隔夜单自动下单交易接口
  • AndroidStudio无线连接Android手机进行调试
  • 利润暴涨507%的携程,做对了什么?
  • C++/Qt 多媒体(续三)
  • 酒店管理系统小程序(包含源码C++实现)
  • 生成和应用patch
  • Redis入门篇 - CentOS 7下载、安装Redis实操演示
  • 每天学习一个基础算法之顺序查找
  • Python观察者模式:构建松耦合的通信机制
  • 深入理解归并排序
  • C++,如何写单元测试用例?
  • PHP语言有哪些优势和特点?
  • C语言通用函数 - 判断ip是否合法
  • 顺序表和链表知识点
  • 运维学习————Docker自制镜像并上传至阿里云以及Docker Compose的使用
  • vmware解决虚拟机空间占用不断增大问题
  • FFmpeg源码:ffurl_seek2、ffurl_seek、avio_size函数分析
  • 使用HTML实现贪吃蛇游戏
  • 小猫爬山 dfs/状压
  • Redis中的数据类型及应用场景(面试版)
  • macos 自定义用户目录方法, /Users/xxx 用户文件存储路径自定义方法