当前位置: 首页 > 科技观察

MySQL的又一神器——锁,MySQL面试必备

时间:2023-03-12 00:27:09 科技观察

1什么是锁1.1锁的概述生活中锁的例子不胜枚举,从古老的简单门锁到密码锁,再到现在的指纹解锁和人脸认识锁都是锁的生动例子,所以我们对锁的理解应该是很简单的。再来说说MySQL中的锁,对于MySQL来说,锁是一个非常重要的特性。数据库的锁是为了支持对共享资源的并发访问,提供数据的完整性和一致性,从而保证在高并发下访问数据库时,数据不会出现问题。1.2锁的两个概念在数据库中,lock和latch都可以称为锁,只是含义不同。Latch一般被称为闩锁(轻量级锁),因为它需要非常短的锁定时间。如果持续很长时间,应用程序的性能会很差。在InnoDB引擎中,Latch可以分为mutex(互斥)和rwlock(读写锁)。其目的是保证并发线程对关键资源操作的正确性,通常没有死锁检测机制。Lock的对象是一个事务,用于锁定数据库中的对象,如表、页、行等。而一般锁对象只有在事务提交或回滚后才会释放(不同的事务隔离级别释放时间可能不同)。2InnoDB存储引擎中的锁2.1锁的粒度在数据库中,锁的粒度可以分为表锁、页锁和行锁。这些锁的粒度也会升级。锁升级的意思就是说当前的锁粒度变小了。数据库可以将一张表的1000行锁升级为页锁,也可以将页锁升级为表锁。下面分别介绍这三种锁的粒度(参考博客:https://blog.csdn.net/baoling...)。表锁表级锁是MySQL存储引擎中最细粒度的锁机制。这种锁机制最大的特点是实现逻辑非常简单,对系统的负面影响极小。所以获取和释放锁的速度都非常快。由于表级锁会一次性锁定整个表,所以可以很好的避免困扰我们的死锁问题。当然,大锁粒度最大的负面影响就是争用锁资源的概率会最高,造成很大的折扣。MyISAM、MEMORY、CSV等一些非事务性存储引擎主要使用表级锁。特点:开销小,锁紧速度快;没有死锁;锁粒度大,锁冲突概率最高,并发度最低。PagelockPagelevellocking是MySQL中特有的一种锁定级别,在其他数据库管理软件中并不常见。页级锁定的特点是锁定粒度介于行级锁定和表级锁定之间,因此获取锁定所需的资源开销及其所能提供的并发处理能力也介于上述两者之间。此外,页级锁定与行级锁定一样,也会导致死锁。在数据库实现资源锁定的过程中,随着锁定资源的粒度变小,锁定同样数量的数据所需要的内存量会消耗越来越多的内存,实现算法也会越来越复杂。但是随着锁定资源的粒度变小,应用的访问请求遇到锁等待的可能性也会变小,系统的整体并发量也会增加。使用页级锁定的主要引擎是BerkeleyDB存储引擎。特点:开销和加锁时间介于表锁和行锁之间;可能会出现死锁;锁粒度介于表锁和行锁之间,并发度一般。行级锁定最大的特点是锁定对象的粒度非常小,也是目前各大数据库管理软件实现的锁定粒度最小的。由于锁的粒度很小,因此发生锁资源争用的概率也最小,可以赋予应用尽可能多的并发处理能力,以提高一些并发要求高的应用系统的整体性能。虽然在并发处理能力上有很大的优势,但是行级锁也带来了很多缺点。由于加锁资源的粒度很小,所以每次获取和释放锁需要做的事情比较多,消耗自然就大了。另外,行级锁也是最容易出现死锁的。特点:开销大,加锁慢;可能会出现死锁;锁粒度最小,锁冲突概率最低,并发度最高。对比表锁,我们可以发现这两种锁的特点基本相反。从锁的角度来说,表级锁更适合于以查询为主,只根据索引条件更新数据的应用,比如Web行级锁更适合于对a的并发更新量较大的应用根据索引条件和并发查询的少量不同数据,例如一些联机事务处理(OLTP)系统。不同MySQL引擎支持的锁的粒度2.2锁的类型InnoDB存储引擎中有不同类型的锁,下面将一一介绍。S或X(共享锁、排它锁)数据操作实际上只有两种,即读和写,而数据库在实现锁的时候,也会对这两种操作使用不同的锁;InnoDB实现了标准的行级锁,即共享锁(SharedLock)和互斥锁(ExclusiveLock)。共享锁(读锁)(SLock),允许事务读取一行数据。排它锁(写锁)(XLock),允许事务删除或更新一行数据。IS或IX(shared,exclusive)意向锁为了让行锁和表锁共存,实现多粒度的锁机制,InnoDB存储引擎支持一种额外的锁方式,叫做意向锁,是表级锁在InnoDBLocks中,意向锁分为:意向共享锁:表示一个事务想要获取表中某些行的共享锁。意向排他锁:表示一个事务想要获取表中某些行的排他锁。另外,这些锁不一定共存,有些锁是不兼容的。所谓兼容性,就是事务A在某一行上获得了某个锁后,事务B也尝试在该行上获得某个锁。如果可以立即获得锁,则称为锁兼容,否则称为冲突。让我们来看看这两种锁的兼容性。S或X(共享锁、排他锁)的兼容性IS或IX(共享、排他)意向锁的兼容性3之前的总结这里用思维导图对之前的概念进行总结。4一致非锁读和一致锁读一致锁读(LockingReads)在一个事务中查询数据时,普通的SELECT语句不会对查询到的数据加锁,其他事务仍然可以对查询到的数据执行更新和删除操作。因此,InnoDB提供了两种类型的锁定读取以确保额外的安全性:SELECT...LOCKINSHAREMODESELECT...FORUPDATE...LOCKINSHAREMODE:读行加S锁,其他的东西可以加S锁这些行,如果他们加了X锁,他们就会被阻塞。SELECT...FORUPDATE:将对查询到的行和关联的索引记录加X锁,阻塞其他事务请求的S锁或X锁。当事务提交或回滚时,这两条语句加的锁就会被释放。注意:SELECTFORUPDATE只有在禁用自动提交时才能锁定行。如果启用了自动提交,匹配的行将不会被锁定。####一致非锁读一致非锁读(consistentnonlockingread)是指InnoDB存储引擎通过多版本控制(MVVC)读取当前数据库中行数据的方式。如果读取的行正在执行DELETE或UPDATE操作,则读取操作不会等待行锁的释放。相反,InnoDB读取该行的快照。因此,非锁读机制大大提高了数据库的并发性。Consistentnon-lockingread是InnoDB默认的读取方式,即读取不会占用和等待行上的锁。在事务隔离级别READCOMMITTED和REPEATABLEREAD下,InnoDB使用一致的非锁定读取。但是,快照数据的定义不同。在READCOMMITTED事务隔离级别下,一致的非锁读总是读取锁定行的最新快照数据。在REPEATABLEREAD事务隔离级别下,读取事务开始时的行数据版本。让我们通过一个简单的例子来说明这两种方法的区别。首先创建一个表;插入一条数据;insertintolock_testvalues(1);查看隔离级别;选择@@tx_isolation;下面分两种交易来操作。在REPEATABLEREAD事务隔离级别下;在REPEATABLEREAD事务隔离级别下,读取的是事务开始的行数据,所以当sessionB修改数据时,仍然可以通过之前的查询查询到数据。READCOMMITTED事务隔离级别下;在READCOMMITTED事务隔离级别下,读取行版本的最新快照数据,所以,由于B会话修改数据并提交事务,A无法读取数据。5行锁算法InnoDB存储引擎有3种行锁算法,它们是:记录锁:对单行记录的锁。GapLock:间隙锁,锁定一个范围,但不包含记录本身。Next-KeyLock:GapLock+RecordLock,锁定一个范围,锁定记录本身。记录锁定:索引记录始终被锁定。如果InnoDB存储引擎表在创建的时候没有设置任何索引,那么InnoDB存储引擎会使用隐式主键来加锁。Next-KeyLock:一种结合了GapLock和RecordLock的锁定算法。在Next-KeyLock算法下,InnoDB将这种锁定算法用于行查询。比如10、20、30,那么Next-KeyLocking可能锁定的索引范围是:除了Next-KeyLocking,还有Previous-KeyLocking技术,与Next正好相反-KeyLock,锁定范围为区间范围和上一个值。对于上面相同的值,使用Previous-KeyLocking技术,可锁定的区间为:不是所有的索引都会加Next-keyLock。有一种特殊情况,就是查询的列是唯一索引(包括主键索引)的情况下,Next-keyLock会降级为RecordLock。接下来,让我们用一个例子来解释它。CREATETABLEtest(xINT,yINT,PRIMARYKEY(x),//x为主键索引KEY(y)//y为普通索引);INSERTINTOtestselect3,2;INSERTITOtestselect5,3;INSERTINTOtestselect7,6;INSERTITOtestselect10,8;我们现在在会话A中执行下面的语句;SELECT*FROMtestWHEREy=3FORUPDATE下面分析一下此时的加锁情况。对于主键x和辅助索引y,用户可以通过以下两种方式显式关闭GapLock:设置事务的隔离级别为READCOMMITED。将参数innodb_locks_unsafe_for_binlog设置为1。GapLock的作用:防止多个事务向同一个范围内插入记录。它旨在解决PhontomProblem(幻读问题)。在MySQL默认的隔离级别(RepeatableRead)下,InnoDB用它来解决幻读问题。幻读:是指在同一个事务下,连续两次执行同??一条SQL语句可能会得到不同的结果,第二次SQL可能会返回之前不存在的行,即在第一次执行和第二次执行之间还有其他事务向其中插入新行。6锁带来的问题6.1脏读脏读:在不同的事务下,当前事务可以读取其他事务未提交的数据。另外需要注意的是,MySQL默认的隔离级别是REPEATABLEREAD,不会出现脏读。发生脏读的条件是需要事务的隔离级别为READUNCOMMITTED,所以如果发生脏读,可能就是这个隔离级别导致的。的。让我们看一个例子。从上面的例子可以看出,当我们事务的隔离级别为READUNCOMMITTED时,当sessionA还没有提交的时候,sessionB可以查询到sessionA还没有提交的数据。6.2Non-repeatablereadNon-repeatableread:指在一个事务内多次读取同一个集合的数据,但多次读取的数据不同,违反了数据库事务的一致性原则。但是,这与脏读不同。脏读数据不提交,但不可重复读数据是提交数据。我们通过下面的例子来看看这个问题的发生。从上面的例子可以看出,在A的一个session中,由于sessionB插入了数据,导致两次查询的结果不一致,所以出现了不可重复读的问题。需要注意的是,不可重复读读取的数据是提交数据,事务的隔离级别是READCOMMITTED。我们可以接受这样的问题。如果我们需要避免不可重复读的问题,那么我们可以使用Next-KeyLock算法(设置事务的隔离级别为READREPEATABLE)来避免。在MySQL中,不可重复读的问题是PhantomProblem,也就是幽灵问题。6.3更新丢失更新丢失:是指一个事务的更新操作会被另一个事务的更新操作覆盖,导致数据不一致。在当前数据库的任何隔离级别下,都不会导致更新丢失的问题。如果出现此问题,则可能发生在多用户计算机系统环境中。如何避免丢失更新的问题,我们只需要让事务的操作串行化,而不是并行化。我们一般使用SELECT...FORUPDATE语句来给操作加一个排他X锁。6.4小结这里做一个小结,主要是比较不同事务隔离级别下出现的问题,这样会比较清楚。