在数据库中,有各种各样的锁,按锁的粒度划分,可分为全局锁、表级锁、行级锁和页级锁。
在InnoDB中,有全局锁、表级锁、行级锁,但是是不支持页级锁的。
全局锁,是一种影响整个MySQL实例的锁。
例如,<font style="color:rgb(38, 38, 38);">FLUSH TABLES WITH READ LOCK</font>
命令会锁定整个数据库实例的所有表,主要用于全局备份等操作。这个命令是全局读锁定,执行了命令之后库实例中的所有表都被锁定为只读。
表级锁,顾名思义,就是对整个表加的锁。表级锁有以下几种:
首先就是意向锁,当一个事务请求获取一个行级锁或表级锁时,MySQL会自动获取相应的表的意向锁。
这种锁是MySQL自动加的,不需要我们显示的加,当我们尝试向记录上添加共享锁的时候,就会自动给这张表添加一个意向共享锁。当我们尝试向记录上添加排他锁的时候,就会自动给这张表添加一个意向排他锁。
AUTO-INC 锁是一种特殊的表级锁,由插入带有 AUTO_INCREMENT 列的表的事务获取。在最简单的情况下,如果一个事务正在向表中插入值,任何其他事务都必须等待,以便执行它们自己的插入操作,这样第一个事务插入的行就会接收到连续的主键值。
字典锁,英文名叫做MetaData Lock,也叫做MDL锁,它是一种用于管理元数据的锁机制,而不是数据本身的锁。
MDL锁用于控制对数据库对象的元数据的并发访问,数据库会在执行DDL(Data Defination Language)操作时加上字典锁。字典锁的主要目的是保护数据库中的元数据对象,如表、列、索引、视图等,以确保在DDL操作期间,不会出现数据一致性问题和竞争条件。
InnoDB中,一般我们会做的就是两种操作,即DDL和DML。
DML中。我们日常的对数据库表结构的SELECT、INSERT、UPDATE以及DELETE都不会添加表级别的共享锁及排他锁。而是使用默认的并发控制方式——行级锁。
那除了增删改查以外,还有一些其他的操作,比如ALTER、DROP等对表机构改变的动作,他们加锁的过程添加的是MDL锁,即字典锁。
所以,InnoDB中的表级锁并不是没用,而是因为他划分的太细了,意向锁、AUTO-INC锁、字典锁等。而剩下的普通的排他锁和共享锁,确认很少才能用得上。我找了很多资料,也没有明确的看到具体是啥时候,在《MySQL是怎样运行的》这本书中提到过一句:比如在崩溃恢复时。
当然,我们可以自己通过SQL语句来添加表级锁。可以使用LOCK TABLES
手动添加表级锁,但这会阻塞其他所有访问该表的操作,直到执行 UNLOCK TABLES
。
LOCK TABLES还可以分为排他和共享:
LOCK TABLES table READ:这就是添加表级别的共享锁
LOCK TABLES table WRITE:这就是添加表级别的排他锁
还有就是,Innodb会在倾向于选择行级锁来进行并发控制,但是如果在一些极端情况下, 比如说UPDATE操作需要扫描整个表且对表中许多行进行更新,InnoDB可能会评估行级锁的成本过高,而采用更粗粒度的锁定策略,比如表级锁。然而,这种情况在InnoDB中是非常罕见的,因为InnoDB设计上是倾向于尽可能地使用行级锁。
相信大家看到过的很多资料中都有过类似的描述“innodb 的 update语句中,如果where条件中没有索引,就不是行级锁了,而是锁表了,就是表级锁”。
我一直也都有这个印象,最开始是从哪看来的,也无从考究了,确实很长一段时间都是这么认为的。但是我发现并不对。
确实,mysql的行级锁锁的是索引,但是当update语句的where条件中没有用到索引的话,他会做全表扫描,但是也不是全部都锁定。而是把符合条件的记录锁住。
锁啥呢?锁主键索引。没有主键呢?会自动创建隐式主键锁住。