数据库
首页 > 数据库> > Mysql 中的锁

Mysql 中的锁

作者:互联网

一、概述

锁是计算机协调多个进程或线程并发访问某一资源的机制.在数据库中,除传统的计算资源 (CPU、RAM、I/O) 的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素.从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂,MySQL中的锁,按照锁的粒度分,可以分为以下三类

全局锁 : 锁定数据库中的所有表

表级锁 : 每次操作锁住整张表

行级锁 : 每次操作锁住对应的行数据

 

二、全局锁

全局锁就是对整个数据库实例加锁,加锁后该数据库实例下的所有表就处于只读状态,后续的 DML 的写操作,DDL 操作,已经更新操作的事务提交操作都将会被阻塞.全局锁最典型的使用场景就是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的一致性和准确性

// 加锁语法
flush tables with read lock;
// 释放锁语法
unlock tables;
时间节点 客户端 1 客户端 2 客户端 3
t1  flush tables with read lock    
t2  select * from user (成功)    
t3    select * from user (成功)  
t4      select * from fruit (成功)
t5      

alter table fruit ... (阻塞)

t6   update ... (阻塞)  
t7

update ... (报错)
ERROR 1223 (HY000): Can't execute the query
because you have a conflicting read lock

   
 t8  unlock tables    
 t9    update ... (成功)  
 t10      alter table fruit ... (成功)

总结 : 全局锁加锁之后,所有客户端只能读,其它操作均不支持

 

三、表级锁

表级锁,每次操作锁住整张表.锁定粒度大,发生锁冲突的概率最高,并发度最低.应用在 MyISAM、InnoDB、BDB 等存储引擎中,对于表级锁主要分为三类,表锁、元数据锁(metadata lock / MDL)、意向锁

3.1、表锁

表锁分类两类

表共享读锁(read lock)、表独占写锁(write lock)

// 加锁语法
lock tables 表名 read
// 解锁语法
unlock tables;
时间节点 客户端 1 客户端 2 客户端 3
t1 lock tables user read    
t2 select * from user (成功)    
t3   select * from user (成功)  
t4     select * from user (成功)
t5

update user set ... (报错)
ERROR 1099 (HY000): Table 'user' was
locked with a READ lock and can't be updated

   
t6   update user set ... (阻塞)  
t7     delete from user ... (阻塞)
t8 unlock tables    
t9   update user set ... (成功)  
t10     delete from user ... (成功)

总结 : 给表加上共享读锁之后,所有客户端对该表都只能读,当前客户端写入时会报错,其它客户端的写入操作会被阻塞

时间节点 客户端 1 客户端 2 客户端 3
t1 lock tables user write    
t2 select * from user (成功)    
t3   select * from user (阻塞)  
t4     select * from user (阻塞)
t5 update user set age = 28 where id = 4 (成功)    
t6 unlock tables    
t7   age = 28 (id=4)  
t8     age = 28 (id=4)

总结 : 给表加上写锁之后,只能在当前客户端进行操作,其它客户端的操作将会被阻塞

3.2、元数据锁(metadata lock)

当表上有活动事务的时候,Mysql 会自动的为该表加上元数据锁,加上了元数据锁之后就不可以对元数据进行写入操作(元数据可以简单的理解为一张表的表结构),它的主要作用是为了避免 DDL 与 DML 冲突,也就是说当表上有活动事务时不能修改表结构

// 查看元数据锁情况
select object_type,object_schema,object_name,lock_type,lock_duration from performance_schema.metadata_locks;

没有执行任何 Sql 语句的时候,只能查询到 performance_schema 库 metadata_locks 表的默认信息,执行 select * from user 语句后,可以看到为 user 表加上了 SHARED_READ 类型的元数据锁,执行 update user set age = 25 where id = 4 后为 user 表加上了 SHARED_WRITE 类型的元数据锁

再看下一个例子

客户端 1 开启事务,执行 select * from user 语句,为 user 表加上元数据锁 SHARE_READ

客户端 2 执行 alter table user change birthday birthdate timestamp default CURRENT_TIMESTAMP not null comment '出生日期' 语句,被阻塞

下面列举不同的 Sql 对应的元数据锁

Sql 语句 锁类型 说明
select、select ... lock in share mode SHARED_READ 与 SHARED_WRITE 兼容,与 EXCLUSIVE 排斥
insert、update、delete、select ...for update SHARED_WRITE 与 SHARED_READ 兼容,与 EXCLUSIVE 排斥
alter ... EXCLUSIVE 与其它的 MDL 都排斥

3.3、意向锁

为了避免 DML 在执行时加的行锁与表锁的冲突,在 InnoDB 中引入了意向锁,使得表锁不用检查每行数据是否加锁,从而减少对锁的检查次数

假设有一张 user 表,表数据如下

如果没有意向锁,当客户端 1 执行 update user set age = 30 where id = 2 这条 Sql 语句为 id = 2  的记录加了行锁之后,如果此时客户端 2 想给 user 加上表锁,那么客户端 2 就要针对 user 表的每一条记录逐行检查,查看有没有行锁,这样的效率是很慢的

有了意向锁之后,客户端 1  执行 update user set age = 30 where id = 2 为 id = 2 的记录加上行锁之后还会为 user 表加上意向锁,如果此时客户端 2 想为 user 加上表锁,那么客户端 2 不需要逐行检查,只需要根据 user 表上意向锁的类型来判断能否给 user 表加上表锁

意向锁分为两类

意向共享锁(IS)

意向排它锁(IX)

可以通过下面 Sql 来查看表的意向锁情况

// 查看意向锁情况
select object_schema,object_name,index_name,lock_type,lock_mode,lock_data from performance_schema.data_locks;

下面就演示一下意向共享锁(IS)、意向排它锁

Sql 语句 意向锁类型 说明
select * from user lock in share mode IS IS 与 lock tables user read 兼容,与 lock tables user write 排斥
insert、update、delete、select * from user for update IX IX 与 lock tables user read、lock tables user write 均排斥

 

四、行锁

行锁单独分开讲

 

标签:tables,lock,update,user,Mysql,select,客户端
来源: https://www.cnblogs.com/xiaomaomao/p/16353444.html