写点什么

🐬【MySQL 技术导航】带你认识一下数据库的锁

发布于: 2021 年 06 月 03 日
🐬【MySQL技术导航】带你认识一下数据库的锁

前提介绍

在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的满足。

本文内容

  • 本文主要介绍:行级锁、表级锁、页级锁的相关概念以及原理介绍

  • 本文主要介绍:共享锁、排它锁的相关概念以及原理介绍

  • 本文主要介绍:意向锁共享锁、意向排它锁的相关概念以及作用介绍

行级锁和表级锁及页级锁

在 MySQL 数据库体系中,可以按照锁的粒度把数据库锁分为行级锁(Innodb 引擎)、表级锁(MyISam 引擎)和页级锁(BDB 引擎 )

行级锁

  • 行级锁是 MySQL 中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。

  • 行级锁能大大减少数据库操作的冲突

  • 其加锁粒度最小,但加锁的开销也最大


行级锁分为共享锁和排他锁。具体针对于这两种锁会在后续介绍


  • 特点

  • 开销大,加锁慢

  • 会出现死锁

  • 锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

表级锁

表级锁是 MySQL 中锁定粒度最大的一种锁,表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分 MySQL 引擎支持


  • 最常使用的 MYISAM 与 INNODB 都支持表级锁定。


表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)


  • 特点

  • 开销小,加锁快

  • 不会出现死锁

  • 锁定粒度大,发出锁冲突的概率最高,并发度最低

页级锁

页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。


表级锁速度快,但冲突多,行级冲突少,但速度慢。


所以取了折衷的页级,一次锁定相邻的一组记录。BDB 支持页级锁


  • 特点

  • 开销和加锁时间界于表锁和行锁之间;

  • 会出现死锁;

  • 锁定粒度界于表锁和行锁之间,并发度一般



MySQL 常用存储引擎的锁机制

  • MyISAM 和 MEMORY 采用表级锁(table-level locking)

  • BDB 采用页面锁(page-level locking)或表级锁,默认为页面锁

  • InnoDB 支持行级锁(row-level locking)和表级锁,默认为行级锁

Innodb 中的行锁与表锁

  • Innodb 引擎中既支持行锁也支持表锁,那么什么时候会锁住整张表,什么时候或只锁住一行呢?

  • InnoDB 行锁是通过给索引上的索引项加锁来实现的,这一点 MySQL 与 Oracle 不同,后者是通过在数据块中对相应数据行加锁来实现的。

  • InnoDB 这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁!


实际应用中,要特别注意 InnoDB 行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能。


  • 在不通过索引条件查询的时候,InnoDB 确实使用的是表锁,而不是行锁。

  • 由于 MySQL 的行锁是针对索引加的锁,不是针对记录加的锁,所以虽然是访问不同行的记录,但是如果是使用相同的索引键,是会出现锁冲突的。

  • 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,另外,不论是使用主键索引、唯一索引或普通索引,InnoDB 都会使用行锁来对数据加锁


即便在条件中使用了索引字段,但是否使用索引来检索数据是由 MySQL 通过判断不同执行计划的代价来决定的,如果 MySQL 认为全表扫效率更高,比如对一些很小的表,它就不会使用索引,这种情况下 InnoDB 将使用表锁,而不是行锁


因此,在分析锁冲突时, 别忘了检查 SQL 的执行计划,以确认是否真正使用了索引。

行级锁与死锁

MyISAM 中是不会产生死锁的,因为 MyISAM 总是一次性获得所需的全部锁,要么全部满足,要么全部等待


  • 在 InnoDB 中,锁是逐步获得的,就造成了死锁的可能。行级锁并不是直接锁记录,而是锁索引

  • 索引分为主键索引和非主键索引两种:

  • 如果一条语句操作了主键索引,MySQL 就会锁定这条主键索引

  • 如果一条语句操作了非主键索引,MySQL 会先锁定该非主键索引,再锁定相关的主键索引


在 UPDATE、DELETE 操作时,MySQL 不仅锁定 WHERE 条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的 next-key locking


  • 当两个事务同时执行:

  • 一个锁住了主键索引,在等待其他相关索引

  • 另一个锁定了非主键索引,在等待主键索引。这样就会发生死锁


发生死锁后,InnoDB 一般都可以检测到,并使一个事务释放锁回退,另一个获取锁完成事务。


  • 有多种方法可以避免死锁,这里只介绍常见的三种

  • 如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。

  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;

  • 对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;

共享锁与排他锁

上面介绍过,行级锁是 MySQL 中锁定粒度最细的一种锁,行级锁能大大减少数据库操作的冲突。


行级锁分为共享锁和排他锁两种,本文将详细介绍共享锁及排他锁的概念、使用方式及注意事项等。

共享锁(Share Lock)

共享锁又称读锁,是读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到已释放所有共享锁


如果事务 T 对数据 A 加上共享锁后,则其他事务只能对 A 再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据

用法

SELECT ... LOCK IN SHARE MODE;
复制代码


  • 在查询语句后面增加 LOCK IN SHARE MODEMySQL 会对查询结果中的每行都加共享锁

  • 当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请共享锁,否则会被阻塞其他线程也可以读取使用了共享锁的表,而且这些线程读取的是同一个版本的数据

排他锁(exclusive Lock)

  • 排他锁又称写锁,如果事务 T 对数据 A 加上排他锁后,则其他事务不能再对 A 加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据


对于 insert、update、delete,InnoDB 会自动给涉及的数据加排他锁(X)

用法

SELECT ... FOR UPDATE;
复制代码


在查询语句后面增加 FOR UPDATE,MySQL 会对查询结果中的每行都加排他锁,当没有其他线程对查询结果集中的任何一行使用排他锁时,可以成功申请排他锁,否则会被阻塞。

意向锁

意向锁是一种不与行级锁冲突表级锁,这一点非常重要。意向锁分为两种: 1. 意向共享锁(intention shared lock, IS):事务有意向对表中的某些行加共享锁(S 锁) 2. 意向排他锁(intention exclusive lock, IX):事务有意向对表中的某些行加排他锁(X 锁) 即:意向锁是有数据引擎自己维护的,用户无法手动操作意向锁,在为数据行加共享 / 排他锁之前,InnoDB 会先获取该数据行所在在数据表的对应意向锁


InnoDB 支持多粒度锁,特定场景下,行级锁可以与表级锁共存。


  • 意向锁之间互不排斥,但除了 IS 与 S 兼容外,意向锁会与共享锁 / 排他锁 互斥。

  • IX,IS 是表级锁,不会和行级的 X,S 锁发生冲突。只会和表级的 X,S 发生冲突。

  • 意向锁在保证并发性的前提下,实现了行锁和表锁共存且满足事务隔离性的要求。




  • 意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先取得该表的 IS 锁

  • 意向排他锁(IX):类似上面,表示事务准备给数据行加入排他锁,说明事务在一个数据行加排他锁前必须先取得该表的 IX 锁


对于一般的 Select 语句,InnoDB 不会加任何锁(快照读)。

用户头像

我们始于迷惘,终于更高水平的迷惘。 2020.03.25 加入

🏆 【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 🤝未来我们希望可以共同进步🤝

评论 (2 条评论)

发布
用户头像
这种情况不会发生,参考https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html。建议作者在mysql上跑一下再做论断。

只有通过索引条件检索数据,InnoDB 才使用行级锁,否则,InnoDB 将使用表锁!

2021 年 06 月 06 日 14:17
回复
谢谢,版本参考不同,本身在索引层面来讲 已经升级了很多

2021 年 06 月 06 日 20:02
回复
没有更多了
🐬【MySQL技术导航】带你认识一下数据库的锁