mysql锁机制 | 青训营笔记

76 阅读5分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 8 天

高并发数据安全性:

高并发下线程安全问题,利用锁来解决这个问题。

锁的由来:

客户端向mysql发送的一条sql可以理解为一个但是事务,事务基于数据库连接,需要一个工作线程来维护,多个事务就是并发事务,需要处理脏读,不可重复读,幻读等问题,锁机制主要用来处理并发线程的数据安全性问题,事务线程在操作同一表过程中的才会出现数据并发问题。

锁机制的分类:

锁由存储引擎复杂是新,锁体系:

以锁粒度进行划分:

  • 表锁:
    • 全局锁:上锁全数据库,只允许读
    • 元数据锁/MDL锁:基于表的元数据加锁,上锁后整张表不允许其他事务操作
    • 意向锁:
    • 自增锁:
  • 页面锁:
  • 行锁:
    • 记录锁/Record锁:也就是行锁
    • 间隙锁/Gap锁:InnoDB中解决幻读的一种锁机制
    • 临建锁/Next-Key锁:间隙锁升级,同时具备记录锁和间隙锁的功能。

以互斥性的维度划分:

  • 共享锁/S锁:不同事务之间不会互斥,可以同时获取锁。
  • 排他锁/X锁:不同事务之间互相排斥,同时只能允许一个事物获取锁。

以操作类型的维度划分:

  • 读锁:查询数据使用的锁
  • 写锁:执行插入,删除,修改,DDL语句使用的锁

以加锁方式的维度划分:

  • 显示锁:编写SQL,手动指定加锁的粒度
  • 隐式锁:执行SQL,根据隔离界别自动为SQL加锁。

以思想维度进行划分:

  • 乐观锁:每次执行前认为自己成功,先尝试执行,失败后再获取锁。
  • 悲观锁:每次执行都认为自己会失败,先获取再执行。

共享锁和排他锁

共享锁/S锁:读操作之间共享锁,写操作会出现排斥行为。

排他锁/X锁:也叫做独占锁,也就是数据只能由一个线程进行操作,对于事务并行操作进行会进行排斥。

锁的释放:

锁的释放时隐式的。释放的时机时不同的。

表锁:

一张表只能够有一个同一类型的表锁,不同存储引擎上表锁方式不同。

select*from zz_users forupdate;

InnoDB支持多种锁机制,基于存储引擎的,基于聚簇索引实现的,执行SQL命中数据就是行锁,没有命中数据则加表锁。

MyISAM不支持聚簇索引,所以无法通过这种机制进行上锁,

LOCK TABLES table_name READ;

LOCK TABLES table_name WRITE;

MyISAM中,获取读锁和写锁Read,Write,对应具有共享和排他特性,这就是共享锁和排他锁翻译为读锁和写锁的由来。

  • MyISAM中获取了锁,还需要自己手动释放锁,否则死锁,

元数据锁:

MDL锁主要作用是维护表元数据的数据一致性,在表上有活动事务(显式或隐式)的时候,不可以对元数据进行写入操作。修改表结构的时候使用,这时候不许进行其他操作,所以是要给表锁。

意向锁:

一个事务操作一条数据,另一个事务给表上写锁,难道需要遍历表看是否含有写锁吗,错误这时候只需要有一个意向锁,表示表中有数据被上锁了就可以,这就是意向锁,更加像一个概念。

自增锁:

专门为了自增ID的并发性能生成的,自增锁主要负责维护并发事务下自增列的顺序,防止并发插入ID重复,使用一种轻量级锁Mutex-Lock来防止自增值重复分配。

全局锁:

特殊的表锁,对整个数据库进行上锁,只需读,不许写。

行锁:

很多数据操作只会影响到行,锁的范围不用到表,于是就使用行锁:

记录锁:

实际上就是行锁,锁定一条数据,InnoDB行锁基于索引实现。

-- 获取行级别的 共享锁
select * from `zz_users` where user_id = 1 lock in share mode;
-- 获取行级别的 排他锁
select * from `zz_users` where user_id = 1 for update;

间隙锁:

用于解决幻读问题,例如主键4-9中间没有数据,间隙锁就是锁定中间区域,上锁满足左右开区间的规则,其他事务插入间隙锁的区间会阻塞。

临键锁:

临键锁是间隙锁的Plus版本,或者可以说成是一种由记录锁+间隙锁组成的锁。

  • 记录锁:包含真实数据
  • 间隙锁:包含空白数据

结合后就是,左闭右开的区间。

插入意向锁:

插入意向锁,听起来似乎跟前面的表级别意向锁有些类似,但实际上插入意向锁是一种间隙锁,这种锁是一种隐式锁,也就是咱们无法手动的获取这种锁。再间隙锁和临键锁的时候插入进入阻塞,等待过程中插入一个插入意向锁,表示有数据进行等待,再同样位置插入数据就会异常。不会排斥其他事务,也是一种共享锁

页面锁,乐观锁,悲观锁

页面锁:

这是Berkeley DB的一种锁,很少用。

以也为单位,锁住一页。

乐观锁:

乐观锁:每次执行都认为只会有自身一条线程操作,因此无需拿锁直接执行。一般都是基于CAS思想实现的,而在MySQL中则可以通过version版本号+CAS的形式实现乐观锁,也就是在表中多设计一个version字段。

悲观锁:

每次执行时都会加锁再执行