【MySQL】数据库中的哪些“锁”事
【MySQL】数据库中的哪些“锁”事
在Java中,锁就是提供多线程并发访问的控制工具。那么在开发多用户、数据库驱动的应用时,最大的难点就是最大程度地利用数据库的并发访问,另外一方面就是要确保每个用户能以一致的方式读取和修改数据。
一、什么是锁?
锁机制用于管理对共享资源的并发访问,这里的共享资源不仅仅是一条行记录哦。
InnoDB可以再数据库许多地方上锁:
- 行记录
- 缓冲池中的LRU列表,增删改查LRU列表中的元素,必须有锁的介入。
对于MyISAM引擎,锁是表锁,并发情况下读没有问题,但是并发插入的时候性能就要差一些了。
lock与latch的区别
lock与latch都可以称为数据库中的“锁”。
latch一般称为闩锁(轻量级的锁)
- 要求锁定时间必须非常短,如果持续时间长,则性能会很差。
- InnoDB引擎中,latch分为mutex(互斥量)和rwlock(读写锁)
- 目的是保证并发线程操作临界资源的正确性。
lock一的对象是事务,用来锁定的是数据库中的对象,比如表、页、行。
- 并且一般lock的对象仅在事务commit或rollback后进行释放(不同的事务隔离级别释放的时间可能不同)
- lock是具有死锁机制的
二、InnoDB存储引擎中的锁
1、锁的类型
SHOW ENGINE INNODB STATUS查看当前锁请求的信息
InnoDB存储引擎实现了下面两种标准的行级锁
- 共享锁(S Lock),允许事务读一行数据
- 排它锁(X Lock),允许事务删除或者更新一行数据
锁的兼容性
如果一个事务T1已经获得了行r的共享锁,事务T2可以立即获得行r的共享锁,因为读取并没有改变r的数据,称这种情况为锁兼容。
但若有其他的事务T3想获取r的排它锁,则必须等待事务T1、T2释放r上的共享锁,称为锁不兼容。
锁的粒度
InnoDB支持多粒度锁定,允许事务在行级别上的锁和表级别上的锁同时存在。
意向锁
为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称为意向锁(Intention Lock)
意向锁将对象分为多个层次。
层次结构说明:
对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先要对粗粒度的对象上锁。
例如,如果需要对页上的记录r进行行上X锁,分别需要对数据库A、表、页上意向锁IX,最后对记录r上X锁。
如果任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。
2、一致性非锁定读
-
定义:一致性非锁定读指的是InnoDB存储引擎通过多版本控制的方式来读取当前执行时间数据库中行的数据。
-
特性:如果读取的行正在执行DELETE或者UPDATE操作,这时读取操作不会因此去等待行上锁的释放,而是会去读取行的一个快照数据(快照读)
-
解释:之所以称为非锁定读,因为不需要等待访问的行上X锁的释放。
快照数据是什么
-
定义:快照数据是指该行的之前版本的数据,该实现是通过undo段来完成的。而undo用来在事务中回滚数据,因此快照数据本身是没有额外的开销。
-
特性:读取快照数据是不需要上锁的、因为没有事务需要对历史的数据进行修改操作。
-
优点:极大的提高了数据库的并发性,InnoDB默认设置下,这是默认的读取方式。
-
不同:在不同事务隔离级别下,读取的方式不同,并不是每个事务隔离级别下都是采用非锁定的一致性读。
- 此外,即使都是采用非锁定的一致性读,但是对于快照数据的定义也各不相同。
MVCC初探
通过上面的学习可以知道,一个行记录可能有不止一个快照数据,一般称这种技术为行多版本技术,由此带来的并发控制,称之为多版本并发控制(Multi Version Concurrency Control,MVCC)
不同隔离级别下快照数据不同
在事务隔离级别READ COMMITTED和REPEATABLE READ下,InnoDB都是用非锁定的一致性读。然而,对于快照数据的定义却不相同。
- RC隔离级别:
- 对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。
- RR隔离级别:
- 对于快照数据,总是读取事务开始时的数据版本。
3、一致性锁定读
在上面的小节中,我们知道,默认配置下,事务的隔离级别为REPEATABLE READ模式下,InnoDB引擎的SELECT操作使用一致性非锁定读。但是在某些情况下,用户需要显式地对数据库读取操作进行加锁以保证数据逻辑的一致性。这就要求数据库支持加锁语句,即使是对于SELECT的只读操作。
两种一致性锁定读操作
- SELECT..... FOR UPDATE
- 对读取的行记录加一个X锁,其它事务再不可以加锁。
- SELECT.....LOCK IN SHARE MODE
- 对读取的行加一个S锁,其他事物可以加S锁,不能加X锁,会被阻塞。
当事务提交了,锁也就释放了。
使用的时候,关闭事务自动提交
BEGIN
START TRANSACTION
或
AUTOCOMMIT=0
三、锁的算法
1、行锁的三种算法
InnoDB有3种行锁算法
- Record Lock:单个行记录上的锁
- Gap Lock:间隙锁,锁定一个范围,但不包含记录本身
- Next-Key Lock:GapLock + Record Lock,锁定一个范围,并且锁定记录本身。
- 可以解决幻读问题
Record Lock
总是去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这个时候InnoDB存储引擎会使用隐式的主键来进行锁定。
Next-Key Lock
结合了两种锁的一个锁定算法,InnoDB对于行的查询都是采用这种锁定算法。
Phantom Problem问题
Phantom Problem是指在同一个事务下,连续执行两次同一的SQL语句可能导致不同的结果,第二次的SQL语句可能会返回之前不存在的行。
比如:
- 事务A查询年龄大于2的数据,有一行,年龄是5
- 此时事务B插入一行年龄为4的数据并提交
- 事务A再次查询,查找到了两条记录,4和5
这违反了事务的隔离性,即当前十五能够看到其它事务的结果。
解决Phantom Problem问题
InnoDB采用Next_Key Locking的算法避免幻读问题
对于
SELECT * FROM t WHERE a > 2 FOR UPDATE
锁住的不是5这个单个值,而是对(2,正无穷)这个范围加了X锁。因此任何对于这个范围的插入都是不被允许的,从而避免了Phantom Problem。
2、不同隔离级别的算法不同
InnoDB采用的默认事务隔离级别是REPEATABLE READ,在这个隔离级别下,采用Next-Key Locking的方式来加锁。
在事务隔离级别READ COMMITTED下,其仅采用Record Lock。因此,上述的案例,需要将事务的隔离级别设置为READ COMMITTED。
三、锁问题
通过锁机制确实可以实现事务的隔离性要求,使得事务可以并发的工作。但是会存在潜在的问题,不过好在因为事务隔离性的要求,锁只会带来三种问题,如果可以防止这三种情况的发生,那将不会产生并发异常。
1、脏读
在理解脏读之前,需要理解脏数据的概念。
脏数据
脏页和脏数据是两种不同的概念。
- 脏页:指的是在缓冲池中已经被修改的页,但是还没有刷新到磁盘中,即数据库实例内存中的页和瓷盘中的页是不一致的,当然在刷新到磁盘之前,日志都已经被写入到了redolog(重做日志)文件中。
- 脏数据:是指事务对缓冲池中行记录的修改,并且还没有被提交。
对于脏页的读取是非常正常的,脏页是因为数据库实例内存和磁盘的异步造成的,这并不影响数据的一致性(两者最终会达到一致性,即当脏页都刷回到磁盘)。
脏数据却截然不同,脏数据是指未提交的数据,如果读到了脏数据,即一个事务可以读取到另外一个事务中未提交的数据,则显然违反了数据库的隔离性。
脏读就是可以读到脏数据,违反了事务的隔离性。
2、不可重复读
不可重复读是指:在一个事务内,多次读取同一数据集合。在这个事务还没有结束的时候,另外一个事务也访问该同一数据集合,并做了一些DML操作。因此,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读取到的数据可能是不一样的。这种情况称为不可重复读。
和脏读的区别:
- 脏读读取到的是未提交的数据
- 不可重复读读取到的却是已经提交的数据,违反了数据库事务一致性的要求。
一般来说,不可重复读的问题是可以接受的,因为其读到的是已经提交的数据,不会带来很大的问题。
附加:区别
https://www.cnblogs.com/balfish/p/8298296.html
3、丢失更新
丢失更新是另一个锁导致的问题,简单来说其就是一个事务的更新操作,会被另一个事务的更新操作所覆盖,从而导致数据的不一致。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)