前言:
MySQL作为世界上最流行的关系型数据库管理系统之一,广泛应用于各种规模和类型的应用程序中。在处理高并发和大规模数据操作时,确保数据的一致性和完整性是至关重要的。而MySQL的锁机制正是在这样的背景下发挥着重要作用。
MySQL锁机制涉及到数据库并发控制的核心概念,它可以保证多个会话(连接)对数据库中数据的读写操作不会相互干扰,从而确保了数据的正确性和可靠性。然而,锁的引入也带来了一定的复杂性和性能开销,特别是在高并发环境下,不合理的锁使用可能导致数据库性能下降甚至出现死锁等问题。
本文将深入探讨MySQL中的各种锁类型及其工作原理,包括共享锁、排他锁、行级锁和表级锁等。我们将从理论和实践两个角度出发,介绍MySQL锁机制的基本概念、常见问题和解决方案,并提供一些优化建议和最佳实践,帮助读者更好地理解和应用MySQL锁,提升数据库系统的性能和可靠性。
目录
前言:
锁:
全局锁:
表级锁:
表锁:
元数据锁:
意向锁:
行级锁:
总结:
锁:
锁是计算机协调多个进程或者线程并发访问某一资源的机制,在数据库中除了传统的对计算资源的争抢之外,数据也是供许多用户共享的一种资源,如何保证多线程下的数据一致性是数据库必须解决的一个问题
在MySQL中,按照锁的粒度可以分为:
- 全局锁:锁住数据库中的所有表。
- 表级锁:锁住数据库中的整张表
- 行级锁:锁住对应的行数据
并不是锁的范围越大越好,当锁的范围越大时候,资源的并发性就越差。我们不断的优化MySQL的锁,实际上就是在权衡安全性和效率之间的关系。
全局锁:
全局锁就是对整个数据库实例加锁,加锁之后整个实例就处于只读状态,后续DML的写语句,DDL语句,已经更新操作的事务提交语句都将被阻塞。
典型的使用场景就是做全库的逻辑备份,需要对所有的表进行锁定,从而获取一致性视图,保障数据的完整性。
逻辑备份:把数据库的数据文件备份成一整个SQL文件。
如何加全局锁:
flush tables with read lock;
如何解全局锁:
unlock tables;
全局锁的缺点:
如果在主库上备份的时候,那么在备份期间都不能执行更新,业务基本就要停摆。
如果在从库上备份,那么备份期间从库不能执行主库同步过来的二进制日志,会导致主从延迟。
在InnoDB引擎中,我们可以在备份的时候添加上参数-----single-transaction参数来完成不加锁的一致性数据备份。
表级锁:
表级锁会在每次操作的时候锁住整张表,锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM,InnoDB,BDB等存储引擎中。
对于表级锁,主要分为以下三类:
- 表锁
- 元数据锁
- 意向锁
表锁:
对于表锁而言,我们又可以分为两类:
- 表共享锁(read lock)
- 表独占写锁(write lock)
语法:
1.加锁
lock tables 表名... read/write
2.释放锁
unlock tables
元数据锁:
元数据锁在加锁的过程中是系统自动控制的,无需显式使用。在访问一张表的时候会自动加上。元数据锁主要作用是维护表元数据的数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。
在MySQL5.5中,当我们对一张表进行增删改查的时候,加DML读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)。
意向锁:
MySQL 中的意向锁(Intention Locks)是一种锁定级别,用于在事务中管理表级锁。它们是为了在使用表级锁时提供更好的性能和并发性而引入的。
意向锁分为两种类型:意向共享锁(Intention Shared Locks)和意向排他锁(Intention Exclusive Locks)。它们用于表示事务在特定表上的意向锁定意图。
-
意向共享锁(IS 锁):当一个事务想要在某个表上获取行级别的共享锁(例如 SELECT 查询时),它会先请求意向共享锁。意向共享锁不会阻止其他事务获取共享锁,但会阻止其他事务获取排他锁。当一个事务持有意向共享锁时,其他事务可以并行地获取共享锁,但不能获取排他锁。
-
意向排他锁(IX 锁):当一个事务想要在某个表上获取行级别的排他锁(例如 INSERT、UPDATE 或 DELETE 操作时),它会先请求意向排他锁。意向排他锁会阻止其他事务获取任何类型的锁(共享锁或排他锁)。但是,它不会阻止其他事务获取意向共享锁。
意向锁的引入主要是为了提高并发性能。通过引入意向锁,MySQL 可以更有效地管理表级锁,减少了在并发环境下的锁冲突,从而提高了系统的并发处理能力。
在没有意向锁之前,如果一张表里面已经有行锁了,此时我们再添加表锁,为了防止表锁和行锁发生冲突,表锁就需要遍历整个表中的数据检查是否有行锁。为了优化表锁检索行锁的过程,我们引入意向锁。
行级锁:
在 MySQL 中,行级锁是一种锁定级别,用于控制对表中特定行的访问。它们允许事务独立地锁定和操作表中的单个行,而不会阻塞其他事务对其他行的访问,从而提高了数据库的并发性能。
MySQL 中的行级锁有两种主要的实现方式:
-
共享锁(Shared Locks):也称为读锁(Read Locks),用于读取数据操作。当一个事务获取了某一行的共享锁时,其他事务也可以获取相同行的共享锁,但不能获取排他锁。这意味着多个事务可以并发地读取相同行的数据,但不能进行写操作。
-
排他锁(Exclusive Locks):也称为写锁(Write Locks),用于修改或删除数据操作。当一个事务获取了某一行的排他锁时,其他事务不能获取相同行的任何类型的锁,包括共享锁和排他锁。这意味着只有一个事务可以修改或删除该行的数据,其他事务必须等待该事务释放锁后才能访问该行。
行级锁在 MySQL 中是基于存储引擎的实现的,不同的存储引擎可能有不同的行级锁实现方式和特性。例如,InnoDB 存储引擎使用了行级锁来实现高度的并发性和事务隔离级别。
InnoDB的数据是基于索引组织的,InnoDB行锁是通过对索引上的索引项来加锁实现的,而不是对记录加锁。
对于InnoDB的行级锁,主要分为以下三类:
1.行锁(Record Lock):锁定单个行记录的锁,防止其他食物对此进行update和delete。在RC,RR隔离级别下都支持。
innoDB为我们提供了两种行锁:
1.共享锁:允许一个事务去读一行,阻止 其他事务获得相同数据集的排他锁。
2.排他锁:允许获取排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
SQL | 行锁类型 | 说明 |
INSERT | 排他锁 | 自动加锁 |
UPDATE | 排他锁 | 自动加锁 |
DELETE | 排他锁 | 自动加锁 |
SELECT(正常) | 不加任何锁 | |
SELECT .... LOCK IN SHARE MODE | 共享锁 | 需要手动在SELECT 之后加上LOCK IN SHARE MODE |
SELECT.... FOR UPDATE | 排他锁 | 需要手动在SELECT 之后加FOR UPDATE |
默认情况下,InnoDB 在 RR 事务隔离级别运行 ,InnoDB使用next-key 锁进行搜索和索引扫描,以此来防止幻读
InnoDB的行锁是针对索引加的锁,不通过索引条件检索数据的时候,InnoDB就会对表中的所有记录加锁,此时就会升级成为表锁。
2. 间隙锁(Gap Lock):锁定索引记录间隙,确保该索引记录间隙不变,防止其他事务在这个间隙之间进行insert,产生幻读。
间隙锁唯一的目的就是为了防止其他事务插入间隙,间隙锁可以共存。一个事务采用的间隙锁不会阻止另外一个事务在同一间隙上采用间隙锁。
3.临键锁(Next_Key Lock):可以简单理解为行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙GAP。
我们用图来表示一下间隙锁:
间隙锁不包含两边的记录,例如(6,12)这个范围的间隙锁,他不锁6和12的数据。
我们用图来表示一下临建锁:
在默认情况下,InnoDB在RR的隔离级别下运行,InnoDB使用next-KEY锁进行扫描和索引扫描。以此来防止幻读。
- 索引上的等值查询(唯一索引),给不存在的记录加锁的时候,会优化成为间隙锁。
- 索引上的等值查询(普通索引),向右遍历的时候,最后一个至如果不满足查询需求,临键锁就会变为间隙锁。
- 索引上的范围查询(唯一索引),会访问到不满足条件的第一个值为止。
总结:
MySQL锁机制是数据库并发控制的重要组成部分,它确保了多个会话(连接)对数据库中数据的读写操作不会相互干扰,从而保证了数据的一致性和完整性。通过引入各种类型的锁,MySQL提供了灵活的并发控制手段,能够满足不同应用场景下的需求。
在本文中,我们深入探讨了MySQL锁的各种类型,包括共享锁、排他锁、行级锁和表级锁等。我们了解了它们的工作原理、应用场景以及优缺点,并探讨了如何在实际应用中合理选择和使用不同类型的锁,以及如何优化锁的性能。
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!