MySQL锁

一.前言

锁主要是用来在多线程或多并发时候限制数据的相关增删改查操作,进而保证数据的一致性和有效性。

MySQL中的锁是在服务器层或者存储引擎层实现的,保证了数据访问的一致性与有效性。

锁的分类:

二.锁粒度区分--全局锁,表锁,行锁

1.全局锁--针对整个数据库的锁。

最常用的全局锁是读锁和写锁。
1.读锁(共享锁):简单说就是阻止其他线程更新数据,但可以读取数据。
2.写锁(排他锁):简单说就是阻止其他线程读取和更新数据。


操作:在MySQL中,可以使用FLUSH TABLES WITH READ LOCK(FTWRL)语句来添加全局读锁,这将阻止其他线程进行更新操作。使用UNLOCK TABLES语句来释放锁定。

缺点:由于锁住了整个数据库的操作,所以全局锁的开销很大,尽量避免在生成环境中使用全局锁。

应用场景:

1.备份全库:使用全局锁可以确保在备份过程中,数据库的所有表都保持一致的状态。例如,可以使用FLUSH TABLES WITH READ LOCK命令在备份期间阻止任何写操作,以确保备份数据的一致性。
2.整体数据迁移:如果你需要将整个数据库从一个服务器迁移到另一个服务器,那么在迁移过程中,你可能希望阻止任何写操作,以确保所有的数据都被正确地迁移到新的服务器。
3.全库只读:在某些情况下,你可能希望将整个数据库设置为只读模式。例如,如果你在进行一些可能破坏数据完整性的操作,或者在进行系统维护时,可能需要将数据库设置为只读模式。在这种情况下,可以使用全局读锁

2.表级锁--对表加锁。

表锁有两种模式:
1表共享读锁(Table Read Lock):简单说就是不允许其他线程更新表,只允许读取表。
2表独占写锁(Table Write Lock):简单说就是不允许其他线程更新和读取表。

使用场景:

1.读密集型和写操作不频繁的应用:如果你的应用主要进行读取操作,很少进行写入操作,那么使用表级锁可能是一个好选择。另外不需要频繁地进行写操作,或者可以容忍写操作的延迟,那么使用表级锁可能是可行的。【读多写少

2.全表更新或者删除:在某些情况下,可能需要对一张表进行全表的更新或者删除操作,例如,删除表中的所有记录,或者更新表中所有记录的某个字段的值。在这种情况下,使用表级锁是合适的。

3.表中数据量不大

InnoDB发生表级锁的命令:
1.被动加锁
不基于主键或者唯一索引的UPDATE,INSERT,DELETE,TRUNCATE【全表删除】。
2.主动加锁

【1】LOCK TABLES命令 【这个命令可以显式地为一个或多个表加上读锁或写锁】    
【2】FLUSH TABLES WITH READ LOCK(FTWRL)命令 
【这个命令可以给所有表加上全局读锁,其他会话在此期间不能对数据进行修改】

在MySQL中,MyLSAM只有表锁而没有行锁。
【1】对MyISAM引擎表全表或者大范围扫描的读操作,会自动加上表读锁,对MyISAM表的写操作,会自动加上表写锁。
【2】InnoDB引擎在必要情况下会使用表锁,但主要是使用行锁来实现多版本并发控制(MVCC),它能提供更好的并发性能和更少的锁冲突。

简单说对于MyISAM引擎大范围select命令也会加表锁,而InnoDB引擎只有修改数据的命令会加锁,而且会根据情况选择加表锁或者行锁。

3.行锁--对数据库表中的单独一行加锁

InnoDB支持两种类型的行级锁:共享锁(S锁)和排他锁(X锁)。
1.共享锁(S锁):简单说就是不允许其他线程更新该行,只允许读取该行。
2.排他锁(X锁):简单说就是不允许其他线程更新和读取该行。

MySQL那些命令会导致发生行锁?
1.被动加锁
基于主键或者唯一索引的UPDATE,INSERT,DELETE
2.主动加锁       

SELECT ... LOCK IN SHARE MODE【select选择一行加上共享锁,即读锁】       
SELECT...FOR UPDATE【select选择一行加上写锁】 
     

行锁的问题

1.死锁:当两个或更多的事务相互等待对方释放资源时,就会发生死锁。例如,事务1锁定了行A并试图锁定行B,同时事务2锁定了行B并试图锁定行A,这就形成了死锁。MySQL会检测到死锁并终止其中一个事务,但这仍可能导致性能问题和事务失败。
2.锁升级:如果一个事务试图锁定的行过多,InnoDB可能会将锁从行级升级为表级,这就可能导致更多的锁冲突。
3.性能下降和资源消耗:加锁毫无疑会影响并发的效率,另外加锁本身这个行为也要消耗资源。
4.事务隔离级别:不同的事务隔离级别会影响锁的行为和性能,可能需要根据具体的应用场景来调整事务隔离级别。

三.权限区分--排他锁(写锁)和共享锁(读锁)

排他锁,即写锁,其他线程不能读和写。
共享锁,即读锁,其他线程只读不能写。

附:

如果你的事务隔离级别是READ UNCOMMITTED,那么LOCK IN SHARE MODE可能不会按照你期望的方式工作,因为在这个隔离级别下,事务可以读取其他事务未提交的修改。在READ COMMITTED,REPEATABLE READ,和SERIALIZABLE隔离级别下,LOCK IN SHARE MODE能正常工作。

四.应用上的区分--乐观锁和悲观锁

乐观锁:乐观,认为线程安全问题不一定会发生。只在更新数据时去判断有没有其他线程对数据做了修改。

悲观锁:认为线程安全问题一定会发生。因此在对更新数据的一系列操作之前先获取锁,确保线程串行执行。

总结而言一个比较乐观,在更新数据时候才加锁,一个比较悲观,在更新数据前就加锁。

1.乐观锁

在MySQL中,乐观锁并没有内置的实现,但是可以通过一些编程技巧来实现。一种常见的实现方式是使用版本号(或时间戳)字段。每当一条记录被修改时,就增加版本号(或更新时间戳)。在更新记录时,先检查版本号(或时间戳)是否和读取记录时的版本号(或时间戳)一致,如果一致则执行更新并增加版本号(或更新时间戳),否则就拒绝更新。这样就可以保证只有当记录没有被其他事务修改时,当前事务的更改才会被提交。

乐观锁的优点在于,由于大部分时间都不需要锁定,所以在冲突较少的情况下可以获得较高的并发性能。

缺点:1.锁本身的开销和性能
2.如果冲突较多,那么乐观锁可能会导致大量的事务回滚,从而影响性能。因此,选择使用乐观锁还是其他锁定技术,需要根据实际的并发情况和性能需求来决定。
3.增加代码复杂性

乐观锁适合于一下的应用场景:

2.悲观锁

实现(在事务开启前加锁):

【1】加上表锁

LOCK TABLES Orders WRITE;//加上写锁
-- 在这里执行你需要的操作
UNLOCK TABLES;

【2】加上行锁排他锁(写锁)

START TRANSACTION;
SELECT * FROM Orders WHERE OrderID = 1 FOR UPDATE;
-- 修改你需要的数据然后提交事务
UPDATE Orders SET Quantity = 10 WHERE OrderID = 1;
COMMIT;

【3】使用行锁共享锁(读锁)

START TRANSACTION;
SELECT * FROM Orders WHERE OrderID = 1 LOCK IN SHARE MODE;
-- 你可以在这里安全地读取数据
COMMIT;

五.加锁取锁的底层优化--意向锁

意向锁是 MySQL 自带的一种锁机制。

意向锁是一种在数据库中用于控制并发访问的机制。当一个事务需要访问某个资源(如数据行、表)时,会先尝试获取意向锁,然后再获取实际的锁。

这样做可以减少死锁的概率,并提高并发访问效率。

简单说意向锁就是在整个事务开始的时候先声明要锁定哪些资源。
当两个或更多的事务相互等待对方释放资源时,就会发生死锁。例如,事务1锁定了行A并试图锁定行B,同时事务2锁定了行B并试图锁定行A,这就形成了死锁。MySQL会检测到死锁并终止其中一个事务,但这仍可能导致性能问题和事务失败。

而如果在事务1开始时先加上意向锁声明要锁定行A和行B,则可以避免死锁的发生。而要获取锁的时候不是直接去请求锁,而是去请求意向锁。

另外,意向锁是表级锁,只需要判断一次即可知道有没数据行被锁定,而不用遍历判断每一行数据,提升性能。

六.其他特殊的锁--间隙锁和临键锁

1.间隙锁--所定两个索引之间的区间

间隙锁(Gap Locks)是MySQL InnoDB存储引擎提供的一种锁定机制。它锁定的不是具体的行记录,而是两个索引之间的间隙(或者说区间),这样可以防止新的记录插入到该间隙,确保数据的一致性和事务的隔离性。

间隙锁常常与记录锁(Record Locks)一起使用,共同形成Next-Key锁,保护索引记录的范围查询和扫描操作。
以下是间隙锁的主要类型:
1 区间-区间间隙锁:锁定两个索引键之间的间隙,或者是第一个索引键之前的间隙。
2 区间-记录间隙锁:锁定一个索引键和一个记录之间的间隙。
3 记录-区间间隙锁:锁定一个记录和一个索引键之间的间隙。

间隙锁的存在,主要是为了解决幻读问题。所谓幻读,是指在一个事务内读取某个范围的记录时,另外一个事务在该范围内插入了新的记录,当第一个事务再次读取该范围的记录时,会发现有些原本不存在的记录,这就是幻读。
举例来说,假设我们有一个存储学生信息的表,有一个事务A要查询年龄在10-20之间的学生,它在查询前会对这个区间加锁。此时如果有另一个事务B想要插入一个年龄为15的学生,由于这个年龄的范围已经被事务A锁定,所以事务B必须等待,直到事务A完成,释放锁。这样就避免了幻读的产生。
值得注意的是,由于间隙锁会锁定范围,如果并发事务较多且涉及的数据范围有交集,可能会引发性能问题,甚至死锁。因此,在设计数据库和选择隔离级别时,需要综合考虑数据一致性和并发性能。

2.临键锁

Next-Key 可以理解为一种特殊的间隙锁,也可以理解为一种特殊的算法。通过临建锁可以解决幻读的问题。 每个数据行上的非唯一索引列上都会存在一把临键锁,当某个事务持有该数据行的临键锁时,会锁住一段左开右闭区间的数据。

需要强调的一点是,InnoDB 中行级锁是基于索引实现的,临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。

总结:根据非唯一索引列上的值锁住一段左开右闭区间的数据。

id

age

name

1

10

张三

3

24

李四

5

32

王五

7

45

赵六

该表中 age 列潜在的临键锁有:

(-∞, 10],

(10, 24],

(24, 32],

(32, 45],

(45, +∞],

3.记录锁

就是我们普通的锁,只是锁定一行。

总结:间隙锁和临键锁可以通过相应的算法锁定多行,而记录锁是能够锁定一行。

### MySQL 机制详解 MySQL 中的机制是为了保障数据库在高并发环境下的数据一致性稳定性而设计的重要功能之一。以下是关于 MySQL 机制的具体解析: #### 1. 的分类 MySQL 主要分为两种类型的:表级行级。 - **表级 (Table-Level Lock)** 表级是最简单的定策略,适用于 MyISAM 存储引擎。它会在整个表上施加,无论是操作还是操作都会影响到整张表。对于 `SELECT` 操作,MyISAM 会自动给涉及的所有表加上;而对于 `UPDATE`, `INSERT`, `DELETE` 则会自动加上[^3]。 - **行级 (Row-Level Lock)** 行级由 InnoDB 存储引擎实现,提供更高的并发能力。只有涉及到具体记录的操作才会触发行级。例如,在执行 `SELECT ... FOR UPDATE` 或者 `SELECT ... LOCK IN SHARE MODE` 时,InnoDB 只会对符合条件的特定行加而不是封整个表[^2]。 #### 2. 不同存储引擎的特性 不同的存储引擎有不同的行为: - **MyISAM**: 默认使用的是表级别的,这意味着即使是一个小范围内的更新也会阻塞其他线程对该表任何部分的访问[^3]。 - **InnoDB**: 支持事务以及更细粒度的行级,这使得它可以更好地处理复杂的多用户场景下的并发请求[^4]。 #### 3. 常见类型及其作用 除了基本的表级与行级区分外,还有几种具体的形式用于满足不同需求: - **共享 (Shared Lock, S-Lock)** 当一个客户端通过命令如 `LOCK TABLES table_name READ` 获取了一个表上的共享之后,其它客户也可以获得该表上的共享,但不能再获取排他直到当前持有共享的所有连接释放它们为止[^2]。 - **排他 (Exclusive Lock, X-Lock)** 排他允许独占式的修改权限。如果某个事务已经获得了某条记录或者某些列上的排他,则在此期间不允许别的事务再对此同一组资源申请任何形式的新——既包括另外的排他也包括新的共享[^2]。 - **GAP ** 这种特殊的用来阻止新纪录插入到现有两条连续记录之间的空隙(gap)里去。比如当我们运行下面这条SQL语句的时候就会用到gap lock:`SELECT * FROM table_name WHERE id > 10 FOR UPDATE;` - **Next-Key Lock** 是一种组合了索引记录本身(record)其前面那个区间(gap)两者一起保护起来的一种复合型模式。主要用于解决可重复(repeatable read)隔离级别下可能出现的幻影问题(phantom problem)[^4]。 #### 4. 死现象及预防措施 死是指两个或更多事务相互等待对方持有的资源从而进入僵局的状态。为避免这种情况发生可以采取以下方法: - 尽量按照固定的顺序访问资源; - 减少单次事务持续时间; - 设置合理的超时参数让系统能够及时发现并终止潜在的死循环状况等等[^4]。 ```sql -- 示例:如何手动解表 UNLOCK TABLES; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值