MySQL锁(lock和latch、锁的粒度、InnoDB锁的类型、一致性非锁定读、一致性锁定读)

什么是锁

锁是数据库系统区别于文件系统的一个关键特性。锁机制用于管理对共享资源的并发访问,提供数据的完整性和一致性。

虽然现在数据库系统做的越来越类似,但是有多少种数据库,就可能有多少种锁的实现方式。

对于MyISAM引擎,其锁是表锁设计,并发情况下读没有问题,但是并发插入时的性能相对较差。

InnoDB存储引擎锁的实现和Oracle数据库非常类似,提供一致性的非锁定读、行级锁支持。行级锁没有相关的额外开销,并可以同时得到并发性和一致性。

lock和latch

Latch一般称为闩(shuan)锁(轻量级的锁),因为其要求锁定的时间必须非常短。若持续的时间长,则应用的性能会非常差,在InnoDB引擎中,Latch又可以分为mutex(互斥量)和rwlock(读写锁)。其目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。

Lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。并且一般lock的对象仅在事务commit或rollback后进行释放(不同事务隔离级别释放的时间可能不同)。

本文主要关注的是lock。

锁的粒度

Lock锁根据粒度主要分为表锁、页锁和行锁。不同的存储引擎拥有的锁粒度都不同。

下表为MySQL中不同引擎支持的锁的粒度

表锁页锁行锁
InnoDB支持不支持支持
MyISAM支持不支持不支持
BDB支持支持不支持
表锁

表级别的锁定是MySQL各存储引擎中最大颗粒度的锁定机制。该锁定机制最大的特点是实现逻辑非常简单,带来的系统负面影响最小。所以获取锁和释放锁的速度很快。由于表级锁一次会将整个表锁定,所以可以很好的避免困扰我们的死锁问题。

当然,锁定颗粒度大所带来最大的负面影响就是出现锁定资源争用的概率也会最高,致使并大度大打折扣。
使用表级锁定的主要是MyISAM,MEMORY,CSV等一些非事务性存储引擎。

页锁

页级锁定是MySQL中比较独特的一种锁定级别,在其他数据库管理软件中也并不是太常见。页级锁定的特点是锁定颗粒度介于行级锁定与表级锁之间,所以获取锁定所需要的资源开销,以及所能提供的并发处理能力也同样是介于上面二者之间。另外,页级锁定和行级锁定一样,会发生死锁。
在数据库实现资源锁定的过程中,随着锁定资源颗粒度的减小,锁定相同数据量的数据所需要消耗的内存数量是越来越多的,实现算法也会越来越复杂。不过,随着锁定资源 颗粒度的减小,应用程序的访问请求遇到锁等待的可能性也会随之降低,系统整体并发度也随之提升。
使用页级锁定的主要是BerkeleyDB存储引擎。

行锁

行级锁定最大的特点就是锁定对象的粒度很小,也是目前各大数据库管理软件所实现的锁定颗粒度最小的。由于锁定颗粒度很小,所以发生锁定资源争用的概率也最小,能够给予应用程序尽可能大的并发处理能力而提高一些需要高并发应用系统的整体性能。
虽然能够在并发处理能力上面有较大的优势,但是行级锁定也因此带来了不少弊端。由于锁定资源的颗粒度很小,所以每次获取锁和释放锁需要做的事情也更多,带来的消耗自然也就更大了。此外,行级锁定也最容易发生死锁。
使用行级锁定的主要是InnoDB存储引擎。

InnoDB锁的类型

InnoDB存储引擎实现了如下两种标准的行级锁:

  • 共享锁(S Lock),允许事务读一行数据
  • 排他锁(X Lock),允许事务删除或更新一条数据

如果一个事务T1已经获得了行r的共享锁,那么另外的事务T2可以立即获得行r 的共享锁,因为读取并没有改变行r的数据,这种情况称为锁兼容。若有其他的事务T3想获得行r的排他锁,则必须等待事务T1、T2释放行r上的共享锁——这种情况称为锁不兼容。

排他锁和共享锁的兼容性如下:

XS
X不兼容不兼容
S不兼容兼容

从表中可以看出X锁与任何锁都不兼容,S锁仅和S锁兼容,需要注意,S和X锁都是行级锁,兼容是对同一记录(行)锁的兼容性情况

此外,InnoDB存储引擎支持多粒度锁定,这种锁定允许事务在行级上的锁和表级上的锁同时存在,为了支持在不同粒度上进行加锁操作,InnoDB存储引擎支持一种额外的加锁方式,称之为意向锁。意向锁是将锁定的对象分为多个层次,意向锁意味着事务希望在更细粒度上进行加锁。

如果上锁的对象看成一棵树,那么对最下层的对象上锁,也就是对最细粒度的对象进行上锁,那么首先要对粗粒度的对象上锁。如下图,如果要对页上的记录r进行上X锁,那么分别要对数据库A、表、页上意向锁IX,最后对记录r上X锁。如果其中任何一个部分导致等待,那么该操作需要等待粗粒度锁的完成。比如在对记录r上X锁之前,已经有事务对表1进行了S锁,那么表1上已存在S锁,之后事务需要对记录r在表1上加IX,由于不兼容,所以该事务需要等待表锁的操作的完成。
在这里插入图片描述
InnoDB支持两种意向锁:

  • 意向共享锁(IS Lock),事务想要获得一张表中某几行的共享锁
  • 意向排他锁(IX Lock),事务想要获得一张表中某几行的排他锁

兼容性如下:

ISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

一致性非锁定读

一致性非锁定读是指InnoDB存储引擎通过行多版本控制的方式来读取当前执行时间数据库中的数据。如果读取的行正在执行DELETE或UPDATE操作,这是读取操作不会因此去等待行上的锁释放。相反,InnoDB存储引擎会去读取行的一个快照数据。

之所以称为非锁定读,因为不需要等待访问的行上的锁的释放。快照数据是指该行的之前版本的数据,该实现是通过undo日志完成的。undo日志本身是用于事务中回滚数据的,因此快照数据是不需要额外开销的。此外,读取快照数据是不需要上锁的,因为没有事务需要对历史的数据进行修改。每行数据可能有多个版本,即可能存在多个快照数据,一般称之为行多版本技术。由此带来的并发控制,称之为多版本并发控制。

非锁定读极大地提高了数据库的并发性。在InnoDB存储引擎的默认设置下,这是默认的读取方式,即读取不会占用和等待表上的锁。

在事务隔离级别为READ COMMITTED和REPEATABLE READ,InnoDB存储引擎使用非锁定的一致性读。然而对于快照数据的定义却不同,在READ COMMITTED级别下,对于快照数据,非一致性读总是读取被锁定行的最新一份快照数据。而在REPEATABLE READ级别下,对于快照数据,非一致性读总是读取事务开始时的行数据版本。

下面来看一个例子,假定此时表table_test中存在一行数据且id=1

时间会话A会话B
1开始
2SELECT * FROM table_test WHERE id = 1
3开始
4UPDATE table_test SET id = 3 WHERE id = 1
5SELECT * FROM table_test WHERE id = 1
6COMMIT
7SELECT * FROM table_test WHERE id = 1
8COMMIT

针对上面案例中第5步的查询,不管是READ COMMITTED还是REPEATABLE READ事务隔离级别,都能读取到id=1的那行数据,因为此时虽然会话B更新了id为3但还未提交事务。

在会话B提交事务后,这时执行第7步的查询语句,两种事务隔离级别就会得到不同的结果。

对于READ COMMITTED事务隔离级别,他总是读取行的最新版本,即读取行的最新的快照。在上述案例中,第6步已经提交了事务,所以第7步的查询id=1的数据为空。

而对于REPEATABLE READ事务隔离级别,总是读取事物开始时的行数据,因此仍能读取到那行id=1的数据。

一致性锁定读

上面已经介绍了事务的隔离级别为READ COMMITTED和REPEATABLE READ下InnoDB存储引擎的SELECT操作使用一致性非锁定读,但是在某些情况下,用户需要显式的对数据进行加锁以保证数据逻辑的一致性。而这要求数据库支持加锁语句。InnoDB存储引擎对于SELECT语句支持两种一致性读:

  • SELECT…FOR UPDATE,对读取事务加X锁
  • SELECT…LOCK IN SHARE MODE,对读取事务加S锁

的一致性。而这要求数据库支持加锁语句。InnoDB存储引擎对于SELECT语句支持两种一致性读:

  • SELECT…FOR UPDATE,对读取事务加X锁
  • SELECT…LOCK IN SHARE MODE,对读取事务加S锁

上述两种加锁必须在一个事务中,当事务提交了,锁也就释放了。因此在使用上述两种锁定语句时,务必加上BEGIN,START TRANSACTION 或者 SET AUTOCOMMIT=0。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值