MySQL锁(一) 概述与分类
1.1 MySQL锁的由来
客户端发往 MySQL 的一条条 SQL 语句,实际上都可以理解成一个个单独的事务(一条sql语句默认就是一个事务)。而事务是基于数据库连接的,每个数据库连接在 MySQL 中,又会用一条工作线程来维护,也意味着一个事务的执行,本质上就是一条工作线程在执行,当出现多个事务同时执行时,这种情况则被称之为并发事务,所谓的并发事务也就是指多条线程并发执行。
多线程并发执行自然就会出问题,也就是事务中的脏写、脏读、不可重复读及幻读问题。而对于这些问题又可以通过调整事务的隔离级别来避免,那为什么调整事务的隔离级别后能避免这些问题产生呢?这是因为不同的隔离级别中,工作线程执行SQL语句时,用的锁粒度、类型不同。
1.2 锁定义
由以上可知,数据库的锁机制本身是为了解决并发事务带来的问题而诞生的,主要是确保数据库中,多条工作线程并行执行时的数据安全性。
锁是计算机协调多个进程或线程并发访问某一资源的机制。在数据库中,除传统的计算资源(CPU、RAM、I/O)的争用以外,数据也是一种供许多用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。从这个角度来说,锁对数据库而言显得尤其重要,也更加复杂。
1.3 锁分类
MySQL的锁机制与索引机制类似,都是由存储引擎负责实现的,这也就意味着不同的存储引擎,支持的锁也并不同,这里是指不同的引擎实现的锁粒度不同。但除开从锁粒度来划分锁之外,其实锁也可以从其他的维度来划分,因此也会造出很多关于锁的名词,下面先简单梳理一下MySQL的锁体系:
-
以锁粒度的维度划分
-
全局锁:锁定数据库中的所有表。加上全局锁之后,整个数据库只能允许读,不允许做任何写操作
-
表级锁:每次操作锁住整张表。主要分为三类
- 表锁(分为表共享读锁 read lock、表独占写锁 write lock)
- 元数据锁(meta data lock,MDL):基于表的元数据加锁,加锁后整张表不允许其他事务操作。这里的元数据可以简单理解为一张表的表结构
- 意向锁(分为意向共享锁、意向排他锁):这个是InnoDB中为了支持多粒度的锁,为了兼容行锁、表锁而设计的,使得表锁不用检查每行数据是否加锁,使用意向锁来减少表锁的检查
-
行级锁:每次操作锁住对应的行数据。主要分为三类
- 记录锁 / Record 锁:也就是行锁,一条记录和一行数据是同一个意思。防止其他事务对此行进行update和delete,在 RC、RR隔离级别下都支持
- 间隙锁 / Gap 锁:锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别下都支持
- 临键锁 / Next-Key 锁:间隙锁的升级版,同时具备记录锁+间隙锁的功能,在RR隔离级别下支持
-
-
以互斥性的角度划分
- 共享锁 / S锁:不同事务之间不会相互排斥、可以同时获取的锁
- 排他锁 / X锁:不同事务之间会相互排斥、同时只能允许一个事务获取的锁
- 共享排他锁 / SX锁:MySQL5.7版本中新引入的锁,主要是解决SMO带来的问题
-
以操作类型的维度划分
- 读锁:查询数据时使用的锁
- 写锁:执行插入、删除、修改、DDL语句时使用的锁
-
以加锁方式的维度划分
- 显示锁:编写SQL语句时,手动指定加锁的粒度
- 隐式锁:执行SQL语句时,根据隔离级别自动为SQL操作加锁
-
以思想的维度划分
- 乐观锁:每次执行前认为自己会成功,因此先尝试执行,失败时再获取锁
- 悲观锁:每次执行前都认为自己无法成功,因此会先获取锁,然后再执行
放眼望下来,是不是看着还蛮多的,但总归说来说去其实就共享锁、排他锁两种,只是加的方式不同、加的地方不同,因此就演化出了这么多锁的称呼。