锁机制:数据库为了保证数据的一致性,在共享的资源被并发访问时变得安全有序所设计的一种规则
MySQL中的锁,按照锁的粒度分,分为以下三类:
全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态,后续的DML的写语句,DDL语
句,已经更新操作的事务提交语句都将被阻塞。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整
性。
flush tables with read lock ;
unlock tables ;
**特点 **
数据库中加全局锁,是一个比较重的操作,存在以下问题:
在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致
性数据备份。即在事务执行过程中,可以读取一个一致性的数据库快照。在备份过程中,通过使用快照读取,可以读取备份时一致性的数据,而不受其他并发事务的影响。
mysqldump --single-transaction -uroot –p123456 itcast > itcast.sql
、
表级锁,每次操作锁住整张表。锁定粒度大,发生锁冲突的概率最高,并发度最低。应用在MyISAM、
InnoDB、BDB等存储引擎中。注意MyISAM只支持表级锁。
对于表级锁,主要分为以下三类:
对于表锁,分为两类:
加锁命令:读锁:所有连接只能读取数据,不能修改。 写锁:其他连接不能查询和修改数据
-- 读锁
LOCK TABLE table_name READ;
-- 写锁
LOCK TABLE table_name WRITE;
-- 将当前会话所有的表进行解锁
UNLOCK TABLES;
lock tables xxx read / write | SHARED_READ_ONLY / SHARED_NO_READ_WRITE | |
---|---|---|
select 、 select … lock in share mode | SHARED_READ | 与SHARED_READ、 SHARED_WRITE兼容,与 EXCLUSIVE互斥 |
insert 、update、 delete、select … for update | SHARED_WRITE | 与SHARED_READ、 SHARED_WRITE兼容,与EXCLUSIVE互斥 |
alter table … | EXCLUSIVE | 与其他的MDL都互斥 |
为了避免DML在执行时,加的行锁与表锁的冲突,在InnoDB中引入了意向锁,使得表锁不用检查每行
数据是否加锁,使用意向锁来减少表锁的检查。
假如没有意向锁,客户端一对表加了行锁后,客户端二如何给表加表锁呢。
有了意向锁之后 :
分类
兼容性如下所示: S、X分别是行级共享锁和行级排他锁
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。
可以通过以下SQL,查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
行级锁,每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在
InnoDB存储引擎中。
InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的
锁。对于行级锁,主要分为以下三类:
RC、RR隔离级别下都支持
务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。
在RR隔离级别下支持、
**介绍 **
InnoDB实现了以下两种类型的行锁:
共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排它锁。
排他锁(X):允许获取到排他锁的事务更新数据,阻止其他事务获得相同数据集的共享锁和排他锁。
两种行锁的兼容情况如下:
常见的SQL语句,在执行时,所加的行锁如下:
SQL | 行锁类型 |
---|---|
INSERT、UPDATE、DELETE | 排他锁 |
SELECT… | 不加锁 |
SELECT … LOCK IN SHARE MODE | 共享锁 |
SELECT … FOR UPDATE | 排它锁 |
特点
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 next-key 锁进行搜
索和索引扫描,以防止幻读。
InnoDB 会对间隙(GAP)进行加锁,就是间隙锁 (RR 隔离级别下才有该锁)。间隙锁之间不存在冲突关系,多个事务可以同时对一个间隙加锁,但是间隙锁会阻止往这个间隙中插入一个记录的操作
InnoDB 加锁的基本单位是 next-key lock(临键锁),该锁是行锁和 gap lock 的组合(X or S 锁),但是加锁过程是分为间隙锁和行锁两段执行
几种索引的加锁情况:
间隙锁优点:RR 级别下间隙锁可以解决事务的一部分的幻读问题,通过对间隙加锁,可以防止读取过程中数据条目发生变化。一部分的意思是不会对全部间隙加锁,只能加锁一部分的间隙
间隙锁危害:
当涉及到索引上的不同类型的查询时,以下是一些具体的例子,展示了在不同情况下锁住的范围:
举例说明
索引上的等值查询(唯一索引):
假设有一个表users
,有一个唯一索引id
,用于存储用户的ID。通过以下查询示例加锁:
SELECT * FROM users WHERE id = 100 FOR UPDATE;
如果ID为100的记录存在,InnoDB会使用行级锁来锁定该行记录。如果ID为100的记录不存在,此时InnoDB会优化为间隙锁。对于该示例,间隙锁会锁住在ID为99和ID为101之间的间隙,以防止其他事务在该范围内插入数据。
索引上的等值查询(非唯一普通索引):
假设有一个表orders
,有一个普通索引customer_id
,用于存储订单的顾客ID。通过以下查询示例加锁:
SELECT * FROM orders WHERE customer_id = 200 FOR UPDATE;
如果有多个订单关联到顾客ID为200,InnoDB会使用行级锁来锁定这些相关订单的行记录。这里的锁的范围是所有满足查询条件的行记录。
索引上的范围查询(唯一索引):
假设有一个表products
,有一个唯一索引product_id
,用于存储产品的ID。通过以下查询示例加锁:
SELECT * FROM products WHERE product_id BETWEEN 100 AND 200 FOR UPDATE
InnoDB会遍历并锁定在ID为100到ID为200的记录范围内的行记录。这个范围包括ID为100和ID为200的行记录。对于这个示例,锁的范围是一个范围区间。
锁是数据库系统区别于文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问。下面我们以
MySQL数据库的InnoDB引擎为例,来说明锁的一些特点。
锁的类型:
InnoDB存储引擎实现了如下两种标准的行级锁:
如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即获得行r的共享锁,因为读取并没有改变行r的数据,称这种情况为锁兼容。但若有其他的事务T3想获得行r的排他锁,则其必须等待事务 T1、T2释放行r上的共享锁,这种情况称为锁不兼容。下图显示了共享锁和排他锁的兼容性,可以发现X 锁与任何的锁都不兼容,而S锁仅和S锁兼容。需要特别注意的是,S和X锁都是行锁,兼容是指对同一记录(row)锁的兼容性情况。
锁的粒度:
InnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在。为了支持 在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。
InnoDB存储引擎支持意向锁设计比较简练,其意向锁即为表级别的锁。设计目的主要是为了在一个事 务中揭示下一行将被请求的锁类型。其支持两种意向锁:
由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。故表 级意向锁与行级锁的兼容性如下图所示。
锁的算法:
InnoDB存储引擎有3种行锁的算法,其分别是: Record Lock:单个行记录上的锁。
Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
Next-Key Lock∶Gap Lock+Record Lock,锁定一个范围,并且锁定记录本身。
Record Lock总是会去锁住索引记录,如果InnoDB存储引擎表在建立的时候没有设置任何一个索引,那么这时InnoDB存储引擎会使用隐式的主键来进行锁定。Next-Key Lock是结合了Gap Lock和Record Lock的一种锁定算法,在Next-Key Lock算法下,InnoDB对于行的查询都是采用这种锁定算法。采用Next-Key Lock的锁定技术称为Next-Key Locking,其设计的目的是为了解决Phantom Problem(幻 读)。而利用这种锁定技术,锁定的不是单个值,而是一个范围,是谓词锁(predict lock)的一种改进。
关于死锁:
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力 作用,事务都将无法推进下去。
解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈 值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph 要求数据库保存以下两种信息:
锁的信息链表; 事务等待链表;
通过上述链表可以构造出一张图,而在这个图中若存在回路,就代表存在死锁,因此资源间相互发生等 待。这是一种较为主动的死锁检测机制,在每个事务请求锁并发生等待时都会判断是否存在回路,若存 在则有死锁,通常来说InnoDB存储引擎选择回滚undo量最小的事务。
锁的升级:
锁升级(Lock Escalation)是指将当前锁的粒度降低。举例来说,数据库可以把一个表的1000个行锁升级为一个页锁,或者将页锁升级为表锁。
InnoDB存储引擎不存在锁升级的问题。因为其不是根据每个记录来产生行锁的,相反,其根据每个事 务访问的每个页对锁进行管理的,采用的是位图的方式。因此不管一个事务锁住页中一个记录还是多个 记录,其开销通常都是一致的。
InnoDB存储引擎有3种行锁的算法,间隙锁(Gap Lock)是其中之一。间隙锁用于锁定一个范围,但不包含记录本身。它的作用是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产 生。
InnoDB行级锁是通过给索引上的索引项加锁来实现的。只有通过索引条件检索数据,InnoDB才使用行 级锁,否则,InnoDB将使用表锁。
当表中锁定其中的某几行时,不同的事务可以使用不同的索引锁定不同的行。另外,不论使用主键索 引、唯一索引还是普通索引,InnoDB都会使用行锁来对数据加锁。
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待的现象。若无外力 作用,事务都将无法推进下去。下图演示了死锁的一种经典的情况,即A等待B、B等待A,这种死锁问 题被称为AB-BA死锁。
解决死锁问题最简单的一种方法是超时,即当两个事务互相等待时,当一个等待时间超过设置的某一阈值时,其中一个事务进行回滚,另一个等待的事务就能继续进行。
除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB存储引擎也采用的这种方式。wait-for graph 要求数据库保存以下两种信息:
通过上述链表可以构造出一张图,而在这个图中若存在回路,就代表存在死锁,因此资源间相互发生等 待。这是一种较为主动的死锁检测机制,在每个事务请求锁并发生等待时都会判断是否存在回路,若存在则有死锁,通常来说InnoDB存储引擎选择回滚undo量最小的事务。