MySQL进阶-锁

MySQL进阶-锁

Jason Lv3

1. 锁的分类

全局锁

锁住整个库 用来数据备份

表锁

MySQL 里面表级别的锁有两种:一种是表锁,一种是元数据锁(meta data lock,MDL)。

  1. 表锁

    举个例子, 如果在某个线程 A 中执行 lock tables t1 read, t2 write; 这个语句,则其他线程写 t1、读写 t2 的语句都会被阻塞。同时,线程 A 在执行 unlock tables 之前,也只能执行读 t1、读写 t2 的操作。连写 t1 都不允许,自然也不能访问其他表。

  2. 元数据锁 表结构

    读锁之间不互斥,因此你可以有多个线程同时对一张表增删改查。
    读写锁之间、写锁之间是互斥的,用来保证变更表结构操作的安全性。因此,如果有两个线程要同时给一个表加字段,其中一个要等另一个执行完才能开始执行。

  3. 意向锁

    意向锁存在的意义在于,使得行锁和表锁能够共存。
    由数据库引擎控制,当需要对数据行加s锁x锁时执行操作时,INNODB首先会获取所在表的意向锁,若能获得,则可以加s锁x锁。
    避免了逐行检查是否加了行级锁

    1
    2
    3
    4
    --设置共享锁:
    select * from user where id = 1 LOCK IN SHARE MODE;
    --设置排他锁:
    select * from user where id = 1 FOR UPDATE;

行锁

行级锁是 MySQL 中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能大大减少
INnoDB的行锁是针对索引加的,若不通过索引检索数据,则会对所有记录加锁

  • 间隙锁

    间隙锁,锁定一个范围,但不包含本身,如(1,5
    间隙锁是存储引擎为了防止幻读,而引入的锁机制。间隙锁之间是互不冲突的,因此可以有多个

  • 临键锁 (next-key lock)

    临键锁,锁定一个范围,并且包含本身

2.mysql锁在4种事务隔离级别里的应用

事务的四种隔离级别有:

  • 读未提交(Read Uncommitted)

此时select语句不加任何锁。此时并发最高,但会产生脏读。

  • 读提交(Read Committed, RC)

普通select语句是快照读

update语句、delete语句、显示加锁的select语句(select … in share mode 或者 select … for update) 等,除了在外键约束检查和重复键检查时会封锁区间,其他情况都只使用记录锁;

  • 可重复读(Repeated Read, RR)

普通select语句也是快照读

update语句、delete语句、显示加锁的select语句(select … in share mode 或者 select … for update)则要分情况:

1. 在唯一索引上使用唯一的查询条件,则使用记录锁。如: select * from user where id = 1;其中id建立了唯一索引
2. 在唯一索引上使用 范围查询条件,则使用间隙锁与临键锁。如: select * from user where id >20;
  • 串行化(Serializable)

此时所有select语句都会被隐式加锁:select … in share mode.

3.快照读、当前读

要理解前面四种隔离级别的加锁方式,对于MVCC、快照读、当前读 都是必须要理解的。

MVCC并发控制中,读操作可以分成两类:快照读 (snapshot read)与当前读 (current read)。

快照读,读取的是记录的可见版本 (有可能是历史版本),不用加锁。

当前读,读取的是记录的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。

在 RC 级别下,对于快照数据,一致性非锁定读总是读取被锁定行的最新一份快照数据。
在 RR 级别下,对于快照数据,一致性非锁定读总是读取本事务开始时的行数据版本。

什么是多版本并发控制(MVCC:multi-version concurrency control )

MVCC定义:多版并发控制系统。可认为是行级锁的一个变种,它能够避免更多情况下的加锁操作。
作用:避免一些加锁操作,提升并发性能。
实现:通过在每行记录的后面保存行的创建时间和过期时间或删除时间(它们是隐藏的),这两个时间实际都是系统的版本号。每开始一个新的事务,版本号都会自动增加。
具体原理
1.select:innoBD查询时会检查以下两个条件:一个是数据行的版本号早于当前事务的版本号;另一个是行的删除版本号,要么没有,要么大于当前事务的版本号。
2.insert/delete:innoDB将当前的系统版本号作为新插入(删除)的数据行的版本号。
3.update:先新插入一行数据,并将当前系统版本号作为行的版本号,同时将当前系统版本号作为原来行的删除版本号。更新主键时,聚集索引和普通索引都会产生两个版本;而更新非主键时,只要普通索引会产生两个版本。

  • Title: MySQL进阶-锁
  • Author: Jason
  • Created at : 2023-09-08 16:09:12
  • Updated at : 2023-09-10 16:00:53
  • Link: https://xxxijason1201.github.io/2023/09/08/MySQL/锁/
  • License: This work is licensed under CC BY-NC-SA 4.0.
 Comments