MySQL的锁机制
mysql`锁分为全局锁、表锁和行锁
全局锁
全局锁是将整个数据库锁起来,使得其他的事务只能进行读,不能写,主要用在数据库备份时
表锁
表锁分为普通表锁,元数据锁和意向锁
-
普通表锁是为了防止事务之间对表数据的访问发生冲突;
-
元数据锁分为表级共享锁和表级排他锁,主要是防止事务访问表和事务修改表结构之间的冲突,增删改查数据会产生共享读锁或者共享写锁,这两个锁会与
update
表结构产生的排他锁产生冲突; -
意向锁分为意向共享锁和意向排他锁,主要是为了防止行锁和表锁之间的冲突,事务使用行级锁会为表生成意向共享锁或者意向排他锁,意向共享锁与表记共享锁兼容,与表级排他锁互斥;意向排他锁与表级共享锁和排他锁都互斥
行锁
行锁分为普通行锁、间隙锁和临键锁,行锁都是加在索引的数据上的,不是加在行记录上的
- 临键锁,Innodb默认的隔离级别是可重复读,使用临键锁解决幻读。增删改就不说了,肯定是行级排他锁,因为另外两个所hi用来解决幻读的,也就是说,Innodb引擎使用行级锁进行查询时
(select * from table where id=1 lock in share mode)
,这句sql
执行之后,会对行进行改行进行上锁,首先考虑临键锁next-key lock
,然后看一下情况进行退化:- 如果走的是主键或者唯一索引,临键锁退化成普通行级锁
(S,REC_NOT_GAP)
- 如果走的是逐渐或者唯一索引,并且该索引不存在,临键锁退化成间隙锁
(gap)
- 如果走的是非唯一索引,则会向右边遍历至最后一个值不满足查询需求时,退化成间隙锁
(gap)
- 如果走的是范围查询,则会产生临键锁
(S)
和其它类型的行锁
- 如果走的是主键或者唯一索引,临键锁退化成普通行级锁
总结
mysql的锁分为全局锁、表锁和行锁,
- 全局锁即锁定整个数据库,使得只可以读,不能进行写操作,一般用于数据库备份;
- 表锁分为普通表锁、元数据锁和意向锁,
- 普通表锁分为表共享读锁和表独占写锁,两个锁都需要自己写
sql
语句时进行加锁- 一个事务A加了表共享读锁,会阻塞事务B的写操作和表修改操作,但是不会阻塞表的读操作
- 一个事务A加了表独占读锁,会阻塞事务B的DQL/DDL/DML
- 元数据锁主要是为了防止表数据增删改查和表结构的改变之间的冲突,分为元数据共享锁和元数据排他锁
- 元数据共享锁在进行增删改查时自动产生,手动加的表共享读锁和表独占写锁与增删改查产生的元数据共享锁之间是兼容的
- 元数据排他锁在进行表结构更改时自动产生,其与其他所有的表锁都是不兼容的
- 意向锁,在一个表加了行锁之后,另一个事务就不能给该表加表锁了,为了提高判断该表是否具有行锁的效率,在加行锁的时候为该表创建一个意向锁,所以意向锁是在创建行锁的时候而创建的,意向锁分为意向共享锁和意向排他锁
- 意向共享锁能和其他表的共享锁或者读锁兼容
- 意向排他锁不能和其他的所有表锁兼容
- 普通表锁分为表共享读锁和表独占写锁,两个锁都需要自己写
- 行级锁分为普通行锁、间隙锁和临键锁,行锁是建立在索引上的,不是表记录
- mysql的Innodb引擎默认是使用的可重复读RR隔离级别,并且使用临键锁作为行锁解决幻读问题,意思就是我们创建的行锁其实都是临键锁,只不过是在不同的情况下退化成了普通行锁以及间隙锁
- 在进行主键索引或者唯一索引时,临键锁退化成普通行锁,仅锁某一行数据,在索引B+树上即仅一个叶子节点
- 在进行主键索引或者唯一索引,并且该索引不存在时,临键锁退化为间隙锁,锁住搜索值的右边结点的左侧间隙
- 在进行普通(非唯一)索引时,退化为间隙索引,锁住该索引左侧间隙,该索引行数据,以及右侧第一个不等于搜索值的值的左侧间隙
- 在进行范围搜索时,会存在临键锁
- 普通行锁,即锁定一行数据,分为共享锁和排他锁,增删改的操作会为行数据添加排他锁,使得其他的事务对该行数据无法读写操作;普通的
select
语句不加任何锁,可以手动为查询添加共享锁或者排他锁,共享锁和共享锁是可以兼容的,也就是行数据加了共享锁之后其他事务也可以为该行数据加共享锁。注意,由于行锁时加在索引数据上的,即无索引行锁会自动升级为表锁,比如说:主键为id
,但是通过name
进行update
,但是name
没有建立索引,这时候进行全表扫描,会将整张表锁住。
- mysql的Innodb引擎默认是使用的可重复读RR隔离级别,并且使用临键锁作为行锁解决幻读问题,意思就是我们创建的行锁其实都是临键锁,只不过是在不同的情况下退化成了普通行锁以及间隙锁