【实践举例】一文搞懂Mysql常见锁!!

1.1 从粒度上分

全局锁

  • 共享锁(读锁):会阻塞写锁

  • 排他锁(写锁):会阻塞读锁,写锁

使用场景:全库导出,全库备份,需要保证整个数据库一致性

用法:

flush tables with read lock 添加读锁

Unlock tables 释放全局锁

另外:-single-transaction 可以通过将导出操作封装到一个事务中,使得导出的数据是一个一致性快照(需要在支持MVCC的场景)

表锁

  • 表共享读锁

  • 表独占写锁

在MyISAM引擎中,读写操作会分别自动加上读写的表锁。在写操作多的情况下,性能很差。

Mysql会触发表锁的指令

Alter table:更改表结构

Drop table 和 truncate table:删除整个表/删除表中所有数据

Lock table table_name write/read:显示加锁

全表扫描

行锁

  • 共享锁(S锁):select...for share

  • 排他锁(X锁):select...for update,insert,update,delete

查看数据库中的锁:select * from performance_schema.data_locks;

注意:

  1. 行锁只在事务中有效,事务提交之后才会解锁;

  2. 行锁是通过对索引加锁实现的,而不是对表记录加锁,也就是如果没有命中索引就会锁全表;

  3. 直接select不加锁,写锁阻塞的是读写锁 而不是读写操作,如下面这种场景,事务2是能读到数据的

表结构(后面的举例都复用该表):

id

name

age

1

zhangsan

14

3

lisi

20

事务1

事务2

Start transation

Start transation

Select * from user where id = 1 for update

Select * from user where id = 1

commit

commit

1.2 从模式上区分

乐观锁

一般通过在表中加一个version列记录版本号,修改的时候进行CAS

悲观锁

select...for share:加读锁,无法在改行再加写锁

select...for update:加写锁,无法在该行再加读锁或者写锁

1.3 其他

意向锁(表锁)

当事务A持有行锁时,mysql会为该加意向锁;事务B如果想申请整个表的写锁,就不用遍历每一行判断是否有行锁存在,而是直接判断有没有意向锁即可

间隙锁,记录锁,临键锁(行锁)

间隙锁锁的是不存在的数据,记录锁锁的是具体的某一行,在innodb的可重复读隔离级别下,间隙锁和记录锁组成临键锁,下面这种场景,临键锁锁的就是[10,20](age中小于20的第一个数到20)

事务1

事务2

Create idx_age on user(age)

Start transation

Start transation

Select * from user where age=20 for update

insert into user value(2,'wangwu',15);

【插入被阻塞】

commit

commit

注意:

只有非唯一索引上存在临键锁,在唯一索引列不存在临键锁(包括主键列),在下面场景中,事务二是能正常插入的,这时候的幻读是靠MVCC解决的

事务1

事务2

Start transation

Start transation

Select * from user where id = 3 for update

insert into user value(2,'wangwu',30);

【能正常插入】

Select * from user

【读不到id为2的数据】

commit

commit

参考视频:https://www.bilibili.com/video/BV1po4y1M7k5/?p=3&spm_id_from=pageDriver&vd_source=986015d7427e83da4b4f898ee289dd8b

你可能感兴趣的:(知识碎片,mysql)