1.锁
1.1概述
- 全局锁:锁定数据库中的所有表。
- 表级锁:每次操作锁住整张表。
- 行级锁:每次操作锁住对应的行数据。
2.全局锁
2.1 介绍
- 在进行数据备份时,先备份了tb_stock库存表。
- 然后接下来,在业务系统中,执行了下单操作,扣减库存,生成订单(更新tb_stock表,插入 tb_order表)。
- 然后再执行备份 tb_order表的逻辑。
- 业务中执行插入订单日志操作。
- 最后,又备份了tb_orderlog表。
2.2 语法
A. 加全局锁
flush tables with read lock ;
B. 数据备份
mysqldump -uroot –p1234 itcast > itcast.sql
unlock tables ;
2.3 特点
- 如果在主库上备份,那么在备份期间都不能执行更新,业务基本上就得停摆。
- 如果在从库上备份,那么在备份期间从库不能执行主库同步过来的二进制日志(binlog),会导致主从延迟。
mysqldump --single-transaction -uroot –p123456 itcast > itcast.sql
对了,忘了介绍命令里面有个 > 符号,这个符号是linux的一个重定向符号,用于将命令的输出重定向到文件,而不是默认显示在终端上。
这部分可以后续关注我的linux部分详细讲解!
3. 表级锁
3.1 介绍
- 表锁
- 元数据锁(meta data lock,MDL)
- 意向锁
3.2 表锁
3.2.1 介绍
- 表共享读锁(read lock)
- 表独占写锁(write lock)
3.2.2 语法
- 加锁:lock tables 表名... read/write。
- 释放锁:unlock tables / 客户端断开连接 。
3.2.3 特点
左侧为客户端一,对指定表加了读锁,不会影响右侧客户端二的读,但是会阻塞右侧客户端的写。
B. 写锁
左侧为客户端一,对指定表加了写锁,会阻塞右侧客户端的读和写。
结论 : 读锁不会阻塞其他客户端的读,但是会阻塞写。写锁既会阻塞其他客户端的读,又会阻塞其他客户端的写。也就是常言道:读读共享,读写互斥,写写互斥。
3.3 元数据锁
3.3.1 介绍
这里兼容互斥的意思就是,兼容是不影响两个事务的操作,不阻塞,互斥就是影响两个事务的操作,后来者阻塞。下面给个实例进行演示一下兼容互斥的情况:
3.3.2 语法
我们可以通过下面的SQL,来查看数据库中的元数据锁的情况:
select object_type,object_schema,object_name,lock_type,lock_duration from
performance_schema.metadata_locks ;
我们在操作过程中,可以通过上述的SQL语句,来查看元数据锁的加锁情况。
这里面的字段分别是:
object_type :锁的级类,这里是表级锁,因为MDL锁是表级锁的一种嘛。
objext_schema:作用的数据库,我上锁上的是的db01这个数据库。
objext_name:作用的表,db01这个表。
lock_type :锁的类型,这里是共享写锁。
lock_duration:锁的作用时间,这里是一个事务的周期。
3.3.3 作用
这就是元数据锁MDL的作用,我在操作数据的时候,给表自动加一个MDL锁,这时不可以修改表结构,这是mysql5.5版本之后的特性。保证了表结构的一致性。
3.4 意向锁
3.4.1 介绍与作用
- 分类意向共享锁(IS): 由语句select ... lock in share mode添加 。 与表锁共享锁 (read)兼容,与表锁排他锁(write)互斥。
- 意向排他锁(IX): 由insert、update、delete、select...for update添加 。与表锁共 享锁(read)及排他锁(write)都互斥,意向锁之间不会互斥。
意向锁属于表级锁,但是他是在加行锁的时候自动加意向锁,用来判断该表要在加表锁的时候,是否有行锁的存在。
一旦事务提交了,意向共享锁、意向排他锁,都会自动释放。
可以通过以下SQL,查看意向锁及行锁的加锁情况:
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
兼容冲突就不再详细演示,还是那句话,只有读读共享,其余互斥。
接下来重点讲解一下行级锁。
4. 行级锁
4.1 介绍
- 行锁(Record Lock):锁定单个行记录的锁,防止其他事务对此行进行update和delete。在 RC、RR隔离级别下都支持。
- 间隙锁(Gap Lock):锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持。
- 临键锁(Next-Key Lock):行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。
4.2 行锁
4.2.1介绍
4.2.1.1 共享锁(Shared Lock, S Lock)
-
共享锁 允许多个事务同时读取相同的数据,但不允许修改数据。当某个事务对数据加上共享锁时,其他事务可以继续对该数据加共享锁,但不能加排它锁。因此,多个事务可以并发读取数据,而不互相阻塞。
共享锁的特点:
- 共享锁用于读操作,允许多个事务并发地读取数据,但不允许任何事务修改该数据。
- 如果一个事务持有共享锁,其他事务也可以获得共享锁(读取数据),但不能获得排它锁(写数据)。
- 共享锁可以确保数据在多个读操作之间保持一致性。
4.2.1.2 排它锁(Exclusive Lock, X Lock)
排它锁(也叫写锁)用于写操作,即修改数据。当一个事务对数据加上排它锁时,其他事务不能再获得该数据的任何类型的锁(包括共享锁和排它锁)。排它锁确保只有当前事务能够修改该数据,避免其他事务的读或写操作导致数据不一致。
排它锁的特点:
- 排它锁用于写操作,当一个事务对数据加上排它锁时,其他事务不能再对该数据加任何锁,保证事务能够安全地修改数据。
- 一个事务持有排它锁时,其他事务既不能读(因为无法加共享锁),也不能写(因为无法加排它锁)。
排它锁的使用场景:
- 排它锁在事务需要对某条记录进行更新、插入、删除时使用,确保在修改过程中没有其他事务能够读取或修改同一数据,从而保证数据的一致性和正确性。
这两种我应该提前介绍,我放在了这里介绍。有点晚。
前面提到过,读读共享,其他互斥。
4.2.2 演示
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from
performance_schema.data_locks;
B. select...lock in share mode,加共享锁,共享锁与共享锁之间兼容。
lock_mode:是行级锁的类型,这里是 S,REC_NOT_GAP,就是说是共享行锁,在前面的行级锁介绍中,有英文的名字介绍,其实就是显示的共享锁的简写英文,S是 S lock 就是共享锁,GAP就是 GAP lock 就是排它锁,REC_NOT_GAP就是record not GAP 就是行锁不是间隙锁,只是单独锁的这一行的数据,不加间隙锁。
lock_type:之前讲过,是锁的类级别:这里是record翻译过来就是行级锁。
其他的都在表锁的时候讲过,这里有一个特殊的没见过的lock_data:这里就是指,我行级锁锁的是哪一行或者哪一间隙(间隙后续会有讲解)。
共享锁与排他锁之间互斥
D. 无索引行锁升级为表锁
我们在两个客户端中执行如下操作:
4.3 间隙锁&临键锁
- 索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁 。
- 索引上的等值查询(非唯一普通索引),向右遍历时最后一个值不满足查询需求时,next-key lock 退化为间隙锁。
- 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。
间隙锁和临键锁(next-key)在行级锁的介绍部分有介绍,忘了可以回顾。
这里还要叮嘱一句,在RR和串行化隔离级别下,只要使用行级锁,在特定条件下间隙锁和临键锁自动添加,默认是临键锁,根据上述的条件,退化成间隙锁。
接下来我来详细演示介绍这几句话的具体作用。
注意:间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。(就是说可以重入,在锁的层面属于重入锁)。
4.3.1 演示
这里,行级锁的间隙锁lock_data显示的是8,就证明我间隙锁锁的是8之前的间隙,8之前的间隙都不允许被插入数据。
C. 索引上的范围查询(唯一索引)--会访问到不满足条件的第一个值为止。
到这里可能会有人很多混乱,什么混乱呢,就是S,X,REC_NOT_GAP,GAP到底都是什么啊,我来详细讲解:
S :就是共享锁的英文简称,同时,我前面提到过,在满足条件的情况下,加行级锁,默认和临键锁同时添加,单独的一个S也就代表了共享锁和临键锁,只有在S,REC_NOT_GAP同时出现时,才是单独的行锁。
X:排它锁的英文简称,也和S同理
GAP:间隙锁的英文简称
REC_NOT_GAP:不是间隙锁。
有没有发现,行锁+间隙锁=临键锁,对没错!
接下来继续讲解:
总结
锁的级别分类有:全局锁、表级锁、行级锁。
锁的颗粒度分类:
表级锁有:表锁、元数据锁、意向锁。
行级锁有:行锁、临键锁、间隙锁。(临键锁=间隙锁+行锁)
锁的大类分类:共享读锁、排它写锁。
1.1. 全局锁
- 作用:全局锁会锁定整个数据库。这种锁通常用于全局操作,例如备份数据库时使用的 FTWRL(Flush Tables with Read Lock)命令。
- 优点:可以确保数据库在备份期间不被修改,数据一致性得到保证。
- 缺点:并发性极差,锁定期间所有事务(包括读写操作)都会被阻塞,导致数据库停滞。
- 应用场景:数据库的备份或迁移操作,保证数据一致性时。
1.2. 表级锁
- 表级锁的粒度较大,锁住整张表。
- 表锁:锁定整张表。
- 元数据锁:InnoDB 中用来防止表结构的修改,确保在执行
ALTER TABLE
等操作时,表的结构不会被并发修改。 - 意向锁:用来标记事务即将在表内对某些行加锁,允许多个事务同时对同一个表加行锁。
- 优点:管理简单,适用于较大范围的操作,避免冲突。
- 缺点:并发性能差,表锁会导致整个表不能被其他事务修改,容易造成锁竞争。
- 应用场景:用于批量数据操作、大量删除、插入时,或在没有高并发需求的情况下。
1.3. 行级锁
- 行级锁的粒度最细,只锁定某一行数据,因此允许更多的并发操作。行级锁主要有:
- 行锁:锁定单行数据,允许其他事务并发访问其他行。
- 临键锁:锁定当前行及其前后的间隙,防止幻读(Next-Key Lock)。
- 间隙锁:只锁定行与行之间的间隙,防止在间隙中插入新行。
- 优点:并发性能好,锁定粒度小,事务可以并行操作不同行的数据。
- 缺点:锁的开销较大,且容易引发死锁。
- 应用场景:适用于高并发的环境,尤其是对特定行进行修改时,如 OLTP(在线事务处理)系统。
1.4. 共享锁(读锁)
- 作用:允许多个事务同时读取数据,但不允许修改。持有共享锁的事务可以读取数据,但不能对其进行修改。
- 优点:允许多个事务并发读取数据,提升并发性能。
- 缺点:无法进行修改,数据只能在事务提交之后被更新。
- 应用场景:适用于只读操作的事务,或事务隔离级别较高的场景,如
SELECT ... LOCK IN SHARE MODE
。
1.5. 排它锁(写锁)
- 作用:排它锁只允许一个事务修改数据,其他事务不能读或写被锁定的数据。持有排它锁的事务可以读取和修改数据。
- 优点:保证数据的一致性,修改数据时不被其他事务干扰。
- 缺点:并发性能较差,因为同时只能有一个事务对数据进行操作。
- 应用场景:更新、删除或插入数据时,尤其是事务隔离级别较高的环境。