MySQL中的锁

316 阅读5分钟

概述

为了支持对共享资源进行并发访问,保证提供数据的完整性和一致性

保证了事务的隔离性,通过表锁,行锁等方式可以实现事务的各个隔离等级。

分类

MySQL中锁大致可以按照数据库的层级分为DB级别锁、表级别锁以及行级别锁,而不同的数据库引擎支持的锁类型也不同

全局锁

在DB级别对整个数据库实例加锁,加锁之后:

  • 数据库处于只读状态
  • 阻塞对数据的增删改以及DDL

加锁方式:lock Flush tables with read lock 释放锁:unlock tables(发生异常时会自动释放)

全局锁主要用于做全库的逻辑备份,和设置数据库只读(set global readonly=true)相比,全局锁在发生异常时会自动释放

MyISAM、InnoDB都支持全局锁,但InnoDB一般不使用 基于InnoDB对事物的支持以及MVCC多版本并发的实现,InnoDB可以选择mysqldump工具加 –single-transaction参数,在不阻塞写操作的同时做全库的逻辑备份

表级别锁

表级别对操作的整张表加锁,锁定颗粒度大,资源消耗少,不会出现死锁,但并发度低,表级锁有两种模式:

  • 表共享锁:对同一表的操作不阻塞读,阻塞写
  • 表独占锁:对同一表的操作读写阻塞

在数据库中,读锁和写锁是互斥的,读写操作是串行

  • 如果某个进程想要获取读锁,同时另外一个进程想要获取写锁。在mysql里边,写锁是优先于读锁的
  • 写锁和读锁优先级的问题是可以通过参数调节的:max_write_lock_countlow-priority-updates

MyISAM引擎默认支持表级别锁

  • 表级别的锁有两种:表锁
  • 元数据锁(MDL)

表锁

  • 显示加锁方式:lock tables {tb_name} read/write
  • 释放锁:unlock table {tb_name} (连接中断也会自动释放)

MyISAM引擎下隐式加锁:

  • 执行SELECT查询自动加共享锁(读锁)
  • 执行INSERT、UPDATA、DELETE操作自动加独占锁(写锁)

MyISAM读写锁优先级: 默认情况下写锁比读锁具有更高的优先级,即使读请求先到等待队列,写锁也会插入到读锁之前,优先执行写操作,但MyISAM也支持依据生产环境通过修改参数的设置改变读写的优先级

元数据锁(MDL)

隐式锁,主要针对对表结构改变的操作(DDL),没有显示加锁方式,访问表时自动加锁:

  • 执行DML(SELECT, INSERT…) 操作加共享锁(读锁)
  • 执行DDL(ALTER, DROP…) 操作加独占锁(写锁)

当向表里增加一个字段隐式加MDL写锁,那么线上所有对这个表的增删改查(DML)操作都会阻塞

MySQL在5.6之后引入online DDL,也就是进行DDL操作时MDL写锁会降级成读锁,线上DML操作不会被阻塞,DDL操作完成之后升级回MDL写锁然后释放

查看表级锁争用情况:SHOW STATUS LIKE ‘table%’

总之表级锁因为锁的粒度大,若一个事物执行时间过长,很可能会导致后面对这个表的请求全部阻塞

行级别锁

InnoDB支持行级别锁,锁粒度小并发度高,但是加锁开销大也很可能会出现死锁,锁模式:

  • 共享锁(读锁) S:对同一行的操作读不阻塞,阻塞写
  • 排它锁(写锁) X:对同一行的操作读写都会阻塞
  • 意向共享锁 IS:一个事物想要加S锁时必须先获得该表的IS
  • 意向排它锁 IX:一个事物想要加X锁时必须先获得该表的IX

为什么需要意向锁: 意向锁是表级别的锁,作为一个标识量,用来标识该表上有数据被锁住或即将被锁,对于表级别的请求(LOCK TABLE…),就可以直接判断是否有锁冲突,不需要逐行检查锁的状态

行级锁算法

记录锁

只对索引上锁,锁定某一行

间隙锁

锁定某一范围内的闭区间

Next-Key Locks

结合记录锁和间隙锁,实现innodb默认加锁单位(左开右闭区间)。

死锁

InnoDB中的锁

全局锁

加锁方式:lock Flush tables with read lock
释放锁:unlock tables(发生异常时会自动释放)

表锁

显示加锁方式:lock tables {tb_name} read/write
释放锁:unlock table {tb_name} (连接中断也会自动释放)

行锁

InnoDB的默认隔离级别RR(可重复读),在RR下读数据有两种方式:

  • 快照读:在MVCC下,事物开启执行第一个SELECT语句后会获取一个数据快照,直到事物结束读取到的数据都是一致的,普通的 select… 查询都是快照读

  • 当前读:读取的数据的最新版本,并且在读的时候不允许其它事物修改当前记录

  • select… lock in share mode(读锁)

  • select… for update(写锁) 加锁方式:

  • 普通 select… 查询 (不加锁)

  • 普通 insert、update、delete… (隐式加写锁)

  • select…lock in share mode (加读锁)

  • select…for update (加写锁)

行级锁默认加 next-key lock,查询过程中访问到的索引项都会加锁,而根据不同的索引也有不同的加锁规则:

  • 唯一索引等值查询:当索引项存在时,next-key lock 退化为 record lock;当索引项不存在时,默认 next-key lock,访问到不满足条件的第一个值后next-key lock退化成gap lock

  • 唯一索引范围查询:默认 next-key lock,(特殊’<=’ 范围查询直到访问不满足条件的第一个值为止)

  • 非唯一索引等值查询:默认next-key lock ,索引项存在/不存在都是访问到不满足条件的第一个值后next-key lock退化成gap lock

  • 非唯一索引范围查询:默认 next-key lock,向右访问到不满足条件的第一个值为止

锁的数据结构

image.png

资料来源

(32条消息) MySQL锁机制,行锁竟然加在索引上!!_guozizi1718的博客-CSDN博客

InnoDB技术内幕