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

MySQL锁有多少内容?然后跟腾讯大哥技术面试,还是小看锁啊!

时间:2023-03-21 20:11:12 科技观察

对着酒唱,人生何其多!日日夜夜,唯有自己逃脱。一边苦苦寻找工作,一边不知道今天的时光是我心头的痛。这太难了,我不配找到工作。自面试以来,他所谓的等待已经过去了一段时间,只可惜,京东上的小金库要低价了。每每想起,我的心就揪紧。正在为难之际,手机突然来了一条短信,预约后续采访。我立即提着包踢门出去了。逃跑。这时,采访门外首先映入眼帘的是一个白色球状的东西,似圆非圆。好奇的瓜掉在地上。上半场还有湿身部分,格外抢眼。这里发生了什么事?这时,一名中年男子出现了。他穿着纯白的T恤,灰色的宽松休闲裤,腰围至少有三十多岁。添加一双夏季必备的皮凉鞋。只见他正低头看着手中的一张A4纸。通过一头黑色短发。他的脸上全是脂肪。另外,大肚腩快撑破T恤了。我看到后吓得忍不住咽了咽口水,生怕自己说错话。这就像一个肉饺子。不在职场努力八九年,也不会有现在的局面。什么是锁定面试官::你是来面试的吗?查查辉:不不不,我是来复试的。面试官::上次看别人的评论,MySQL优化还是广。那你说说对锁的理解?渣渣辉:呵呵,没事!锁是计算机在调度多进程和线程执行时强制限制资源访问的一种同步机制。用于保证并发访问时数据的一致性和有效性。锁用于在执行多线程时强制限制资源访问。同步机制,用于并发控制,保证互斥需求。通用锁是建议锁。每个线程在访问相应的资源之前都需要获取锁信息,然后根据这些信息决定自己是否可以访问。如果访问了相应的信息,锁的状态就会变成locked,所以此时其他线程是不会访问该资源的。当资源结束时,锁的状态将被恢复以允许其他线程访问。有些系统有强制锁(mandatorylock),如果一个未授权的线程想要访问被锁定的数据,在访问过程中就会产生异常。---《维基百科》锁的种类及应用原理面试官:一般的数据库都有哪些锁?一般如何使用?这一刻,我用傻眼的眼神看着面试官,真的很尴尬+害怕。数据库不就是共享锁和互斥锁吗?看来我太年轻了。这里一定有坑。殊不知,这一刻,我已经将你牢牢抓在心里,再也不会放手。查查辉:根据不同的划分方式,数据库锁的定义有很多种。业务访问有两种:独占锁在访问共享资源前先加锁,访问完成后解锁。成功获取锁后,任何其他线程请求获取锁都会被阻塞,直到当前线程自己释放锁。线程3状态:就绪、阻塞、执行。比如解锁时,不止一个线程被阻塞(资源已经释放),那么所有试图获取锁的线程都被CPU认为就绪。如果第一个处于就绪状态的线程执行了Lock操作,那么其他线程会再次进入就绪状态。这样,只有一个线程可以访问互斥体保护的资源。因此,MySQLSQL语句添加互斥量后,只有接收到请求并获得锁的线程才能访问和修改数据。因为互斥量是为了线程访问控制而不是请求本身。共享锁是可以共享的锁定资源,但仅限于读取请求。它的写请求只能独占到获取锁的请求。即加了共享锁的数据只能被当前线程修改,其他线程只能读取数据而不能修改。查查辉:SQL请求可以分为读锁和写锁。但本质还是处理共享锁和排它锁。面试官:那如何不对SQL请求加锁访问呢?为什么说它们属于共享锁和排它锁呢?它们之间有什么联系?查查辉:除了锁读之外,还有一种情况是解锁读。这种方式称为快照读,读请求锁称为共享读。之所以给请求加共享锁和独占锁是因为读请求本身是幂等的,无论读多少次数据都不会改变,所以给读请求加锁应该是共享锁。不然怎么保证它的特性呢?写请求本身需要修改数据,所以需要排他锁来保证数据修改的一致性。查查辉:按照锁的粒度,有表锁和行锁。表锁:是MySQL中最基本的加锁策略,也是开销最小的策略。较少的并发处理。表锁由MySQL服务或存储引擎管理。在大多数情况下,它由服务层管理,具体取决于SQL操作。例如:服务器对ALTERTABLE等语句使用表锁而忽略存储引擎锁。锁定机制:它会锁定整个表。用户在对表进行写操作(插入、删除、更新等)之前,需要获得一个写锁,这会阻塞其他用户对该表的所有读写操作。只有在没有写锁的情况下,其他用户才能获得读锁。行锁:锁定当前访问行的数据,并发处理能力强。但是锁成本最高。这取决于行数据量。由innoDB存储引擎支持。页级锁:页级锁是MySQL中锁定粒度介于行级锁和表级锁之间的一种锁。表级锁速度快,但冲突多,行级锁冲突少,但速度慢。因此,采用折衷的页级锁,一次锁定一组相邻的记录。页级锁由BDB存储引擎管理。面试官:为什么表锁比行锁更便宜?毕竟表锁锁的是整张表:表锁锁表是对的,但是并没有锁住表中所有的数据行,相当于阻塞了表的入口,所以只需要判断每个请求是否能获得表的锁,如果不能,则不加锁。行锁针对表中的每一行数据。当数据量很大时,锁定的内容会比较多,所以开销很大。但由于其粒度小,锁定行不会影响其他行。所以并发度很高。而如果表锁卡在一个表项上,那么整体的请求处理肯定会下降。面试官:我记得行锁有几种不同的实现方式,你知道吗?你真周到,为我想了这么多,老板们都这么周到吗?如果我说我不知道??,你是不是随时都准备好再穿小鞋了?忍住心里想啃人的冲动ps:看图说明你有故事扎扎辉:虽然innodb支持行锁,但是锁实现的算法和SQL的查询形式有关:RecordLock(记录锁):记录上的单行A锁。也就是我们每天想到的行锁。由`where=`形式触发GapLock(间隙锁):间隙锁,锁定一个范围,但不包括记录本身(它锁定一个范围内的多行,包括根本不存在的数据)。GAP锁的目的是为了防止事务插入引起的幻读。仅当隔离级别为RR或更高时,此锁才会存在。间隙锁的目的是防止其他事务在间隙中添加新数据。SQL中使用where>、>=等范围条件来触发,但会根据锁定的范围是否包含表中的真实记录而变化。如果有真实记录,就会演变成临时锁。反之,则为间隙所致。Next-KeyLock:是recordlock和gaplock的组合,锁定一个范围,同时锁定记录本身。对于行查询,采用这种方式,主要目的是解决幻读问题。下一键锁是InnoDB的默认设置。就是左开右闭的规则IS锁:IntentionSharedLock,IntentionSharedLock。当一个事务要对一条记录加S(读)锁时,需要先在表级加一个IS锁。IX锁:IntentionExclusiveLock,意向排他锁。当一个事务要给记录加X(写)锁时,需要先在表级加IX锁。采访者:那这个东西是怎么实现的呢?t(idPK,nameKEY,sex,flag);表中有4条记录:1、zhazhahui、m、A3、nezha、m、A5、lisi、m、A9、wangwu、f、Brecordlockselect*fromtwhereid=1forupdate;锁定记录间隙锁id=1select*fromtwhereid>3andid<9;lockingthevaluesintherange(3,5),(5,9)因为当前访问的是3到9条范围记录,需要锁定表中已有??的数据,解决幻读和不可重复读,临时建锁select*fromtwhereid>=9;会锁住[9,+∞)。查询会先选择9号记录,所以锁定范围从9开始到正无穷数据。面试官:意向排他锁和共享锁呢?它是关于什么的?查辉:意向排他锁和意向共享锁是针对当前SQL请求访问数据行,会提前申请访问。如果最后的行锁未命中,就会退化为这种表锁。面试官:那么这个意向排他锁有什么好处呢?查查辉:可以提前做出预判。在每次尝试获取行锁之前,它都会检查是否有表锁。如果存在,则不会继续申请行锁,从而减少锁的开销。因此整个表退化为表锁。采访者:那你可以亲手给我看每个场景。..(瞳孔放大2倍)我看清楚了吗?是故意针对你的吗?这个是来做什么的?欺负?没看到面试官突然抬起腿,有节奏地抖着腿,看着我。乍一看,抖音太多了,没办法压人。打碎了牙齿,咽了自己一口。你得仔细看看,最好不要眨眼睛。查辉:因为锁是为了解决事务并发的问题,所以记录锁就不演示了,只会在间隙和临时锁中徘徊。创建语句:CREATETABLE`t1`(`id`int(10)NOTNULLAUTO_INCREMENT,`name`varchar(64)COLLATEutf8mb4_general_ciDEFAULTNULL,`age`tinyint(3)unsignedDEFAULTNULL,PRIMARYKEY(`id`))ENGINE=InnoDBDEFAULTCHARSET=utf8mb4_generf表数据:Gaplock:关闭MySQL默认的事务自动提交机制。关闭前:关闭后:加锁:如果数据>8直接插入,会被阻塞,会加锁。为了解决插入新数据导致的幻读。【啊!我不知道幻读。下篇文章给大家安排】面试官:你的条件不是>=8吗?8呢?被吃辣?查查辉:别着急,还没完呢。为什么不指定8?因为condition>=8会从gaplock升级为临时锁,因为你的condition包含了8的真实数据。所以会加锁。如下:因此,最终的行锁会和SQL语句的条件触发有关。一旦范围查询包含了数据库中的真实数据,它就会升级为临时锁。别问我为什么?看前面的定义和面试官的独白:这家伙好像有点货,不错。这时,面试官露出了笑容。殊不知,他的心中正在酝酿着一个新的想法。只等我进骨灰盒。采访者:那什么情况下下行锁定不生效呢?什么被锁定?这一刻,我惊呆了,这到底是怎么回事。不要这样玩。妈的,net的触发机制不好:InnoDB的行锁是根据索引触发的。如果没有相关索引,行锁就会退化为表锁(即锁住整个表中的行)。锁锁定索引,即索引树中数据库字段的值。id是主键索引字段。锁定年龄字段。age字段没有索引,退化为表锁。直接查询将失败。如果有索引,使用索引字段查询可用数据,其他字段查询会失败。因为获取不到行锁,所以只能等待。索引被锁定,所以使用其他索引值的其他查询可以得到查询数据。索引字段被锁定。索引的当前字段被锁定,可以使用其他索引字段进行查询。没有索引字段也不错。面试官:你前面说的锁可以解决事务并发,但是MVCC也是用来解决并发的,那为什么要用锁呢?说说咋咋会:MVCC可以解决脏读、不可重复读、幻读等读一致性问题,但实际上它只是解决了普通select语句的数据读取问题。使用MVCC的事务执行的读取操作称为快照读取,在READCOMMITTED和REPEATABLEREAD隔离级别下,所有普通的SELECT语句都被视为快照读取。除了快照读取,还有一种锁定读取,即在读取时锁定记录。在加锁读的情况下,脏读、不可重复读、幻读等问题仍然需要解决。例如:如果1479的数据。如果条件是where>4,那么如果不锁定到(4,7](7,9],(9,+∞),势必造成早幻读和不可重复读ps:不可重复读?脏读是怎么产生的?死锁面试官:那你怎么说数据库死锁?同一个资源和请求互相加锁占用资源,导致恶性循环,当事务以不同的顺序尝试加锁资源时,可能会出现死锁,多个事务同时加锁同一个资源也可能产生死锁。一般通过死锁检测和死锁锁超时机制就是用来解决这个问题的。死锁检查:和InnoDB存储引擎一样,可以检测到死锁的循环依赖,并立即返回错误。否则,死锁会导致查询非常慢。设置它由参数开启erinnodb_deadlock_detect,启用它。超时机制:当查询时间达到锁等待超时设置时,放弃锁请求。InnoDB目前通过回滚持有最少行级排他锁的事务来处理死锁(这是一种相对简单的死锁回滚算法)。超时时间可以通过配置参数innodb_lock_wait_timeout来设置。如果部分用户使用大事务,将锁超时时间设置为大于事务执行时间。但是在这种情况下,死锁超时检查的发现时间是不可接受的。面试官:那你说说InnoDB和MyisAM是怎么发现死锁的?查查辉:innodb数据库会记录事务单元锁维护的锁和正在等待的锁。Innodb提供了wait-forgraph算法,用于主动检测死锁。每当锁请求不能立即满足而进入等待时,就会触发等待图算法。当数据库检测到两个事务在不同方向(顺序生成)锁定同一个资源时,认为发生死锁并触发等待图算法。例如:事务1锁A,事务2锁B,事务1锁B(等待),事务2锁A,发生死锁。那么死锁的解决方法就是终止事务的一侧执行。这种效率一般是最高的,也是主流数据库采用的方式。Innodb目前处理死锁的方法是回滚持有最少行级独占锁的事务。这是一种比较简单的死锁回滚方法。发生死锁后,只有其中一个事务的部分或完全回滚才能打破死锁。对于事务系统来说,这是无法避免的,所以应用程序在设计时必须考虑如何处理死锁。大多数情况下,只需要重新执行因死锁而回滚的事务即可。MyisAMMyisAM本身只支持表级锁,所以加锁后获取一次。因此,资源上不会有多个事务在处理之前需要彼此释放锁。所以不会出现死锁面试官:你是怎么理解wait-forgraph算法的?渣渣辉:如下图,四辆车僵持着,互相等待着对方的资源,形成了一个循环!每辆车都可以看作一个节点。当节点1需要等待节点2的资源时,生成一条指向节点2的有向边,最终形成有向图。我们只需要检测这个有向图中是否存在环路,环路就是死锁!这就是等待图算法。Innodb把每一个事务看作一个节点,资源就是每一个事务占用的锁。当事务1需要等待事务2的锁时,从1到2产生一条有向边,最终形成有向图。面试官:既然死锁无法避免,那么如何减少呢?查查辉:对申请进行调整/修改。在某些情况下,可以通过将大事务拆分成多个小事务,从而更快地释放锁,从而大大降低死锁的发生频率。在其他情况下,发生死锁是因为两个事务以不同的顺序对一个或多个表的同一数据集进行操作。需要改为按照相同的顺序读写这些数据集,也就是串行化访问这些数据集。这样,在并发事务期间,死锁就变成了锁等待。修改表的schema,例如:去掉外键约束来分隔两张表,或者添加索引来减少扫描和锁定行。如果出现间隙锁,可以将session或者transaction的事务隔离级别改为RC(readcommitted)级别来避免,这样可以避免很多间隙锁导致的死锁,但是这时候需要设置binlog_format为row或混合格式。给表添加一个合理的索引。如果不使用索引,表的每一行都会被锁定(相当于表锁),死锁的概率会大大增加。为了避免在单个InnoDB表上执行多个并发写操作时出现死锁,可以在事务开始时通过对每个预期要修改的元组(行)使用SELECT...FORUPDATE语句来获取必要的锁,即使这些行的更改语句随后执行。通过SELECT...LOCKINSHAREMODE获得该行的读锁后,如果当前事务需要更新记录,极有可能造成死锁。因解锁阅读而修改的时候,只见面试官坐在对面,抚着光秃秃的下巴,故作若有所思,好像在看什么。难道我通过了?这一刻,我的心就像一只小鹿在乱撞,大声喊着要来两次。这真的不容易。就在这时,他起身站了起来,白色的T恤包裹着他的大肚皮,像波浪一样上下翻滚。看来酒桌上的肉也没有少。只见少年不坏。这对我有把握吗?这并不容易。今天不开几个LOL,恨意难解。采访者:其实这个数据库的内容还是很多的。回去准备下一次面试。...什么鬼,下次?难怪这次我做不到,我还没考够,下巴没毛了,你这么用力的抚摸干什么,都在发呆。此时,我心如江海,龙过江。白鹤有展翅的冲动向他袭来,可我小小的身躯做不到。查查辉:嗯,下一次是多久?我用那双水汪汪的可怜巴巴的小眼睛看着他说道。但他很客气地笑了笑,对面试官说:来吧,小伙子,别担心,我看好你,来吧,我给你加碎花生油。采访的时候,我还觉得我的脸出油不多,全被你挤出来了。只能忍住内心的冲动。嘿咋咋会:好,那我走了。这一刻,我灰溜溜的背影露了出来,就像鲁迅先生笔下的孔乙己