当前位置: 首页 > 后端技术 > Java

一日看懂MySQL锁面试八字随笔

时间:2023-04-01 13:59:40 Java

本文已收录到Github仓库,里面包括计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招代理招聘等核心知识点分享,欢迎star~Github地址:https://github.com/Tyson0314/Java-learning为什么需要加锁?可能有多个事务同时操作同一行数据。如果不对并发操作进行控制和锁定,可能会写入错误的数据,或者读取错误的数据,破坏数据的一致性。因此,需要考虑锁。表级锁和行级锁有什么区别?MyISAM只支持表级锁,一把锁锁住整张表,在并发写的情况下效率很低。InnoDB不仅支持表级锁,还支持行级锁。默认是行级锁。行级锁的粒度更小,只能锁定相关记录(锁定一行或多行记录),因此对于并发写操作,InnoDB具有更高的性能。表级锁和行级锁的比较:表级锁:MySQL中锁定粒度最大的一种锁。是针对非索引字段的锁,锁住当前正在操作的整个表。实现简单,消耗资源少,加锁速度快,不会出现死锁。它的锁定粒度最大,触发锁冲突的概率最高,并发度最低。MyISAM和InnoDB引擎都支持表级锁。行级锁:MySQL中锁定粒度最小的锁。是索引字段的锁,只锁当前操作的记录。行级锁可以大大减少数据库操作中的冲突。它的加锁粒度最小,并发度高,但是加锁的开销也最大,加锁慢,可能会出现死锁。共享锁和排它锁有什么区别?无论是表级锁还是行级锁,都有共享锁(ShareLock,S锁)和排他锁(ExclusiveLock,X锁)两种:共享锁(S锁):也称为读锁,事务在读取记录时获取共享锁,允许多个事务同时获取(锁兼容)。排他锁(X锁):又称写锁/排他锁,一个事务在修改一条记录时获取一个排他锁,不允许多个事务同时获取。如果一条记录已经被独占锁定,其他事务就不能向该事务添加任何类型的锁(锁不兼容)。独占锁不兼容任何锁,共享锁只兼容共享锁。SlockXlockSlocknoconflictconflictXlockconflict由于MVCC的存在,InnoDB不会对一般的SELECT语句加任何锁。但是,您可以通过以下语句显式添加共享锁或排他锁。#共享锁SELECT...LOCKINSHAREMODE;#独占锁SELECT...FORUPDATE;意向锁的作用是什么?如果需要使用表锁,如何判断表中的记录没有行锁呢?逐行遍历肯定不行,性能太差。我们需要使用一种叫做意向锁的东西来快速判断一个表是否可以使用表锁。意向锁是表级锁,有两种:意向共享锁(IntentionSharedLocks,IS锁):事务有意向表中的某些表加共享锁(S锁)。在加共享锁之前,表必须获得IS锁。意向排他锁(IntentionExclusiveLock,IX锁):事务有意对表中的某些记录加排他锁(X锁)。在加排他锁之前,必须先获得表的IX锁。意向锁由数据引擎自行维护,用户无法手动操作意向锁。在给数据行加共享/排他锁之前,InooDB会先获取该数据行所在数据表对应的意向锁。意向锁是相互兼容的。IS锁IX锁IS锁兼容IX锁兼容意向锁与共享锁和排他锁互斥(这里指的是表级共享锁和排他锁,意向锁不会和行级共享锁结合和互斥锁互斥)。IS锁、IX锁、S锁、兼容互斥、X锁、互斥互斥。InnoDB有哪些类型的行锁?按锁粒度分类,有行级锁、表级锁和页级锁。行级锁是MySQL中最细粒度的锁。表示只锁定当前正在操作的行。行级锁可以大大减少数据库操作中的冲突。加锁粒度最小,但加锁开销也最大。行级锁主要有三种:RecordLock,记录锁,即只锁定一条记录;GapLock,间隙锁,锁定一个范围,但不包括记录本身;Next-KeyLock:RecordLock+GapLock组合,锁定范围,并锁定记录本身。表级锁是mysql中锁定粒度最大的一种锁,意思是锁定当前正在操作的整个表。实现简单,占用资源少,大多数mysql引擎都支持。最常用的MyISAM和InnoDB都支持表级锁定。页级锁是MySQL中的一种锁,锁定粒度介于行级锁和表级锁之间。表级锁速度快,但冲突多,行级锁冲突少,但速度慢。因此,采用折衷的页级锁,一次锁定一组相邻的记录。按锁级别分类,有共享锁、排他锁、意向锁。共享锁,也称为读锁,是由读操作创建的锁。其他用户可以并发读取数据,但在所有共享锁被释放之前,任何事务都不能修改数据(获得数据的排他锁)。独占锁也称为写锁、独占锁。如果事务T给数据A加了排他锁,其他事务就不能给A加任何类型的锁。被授予排它锁的事务既可以读取也可以修改数据。意向锁是表级锁,主要设计用于揭示将为事务中的下一行请求的锁类型。InnoDB中的两种表锁:Intentionalsharedlock(IS):表示事务要给数据行加共享锁,也就是说在给a加共享锁之前必须先获取表的IS锁数据行;Intentionalexclusivelock(IX):和上面类似,表示事务要给数据行加排他锁,表示事务要先获得表的IX锁,再给一个数据加排他锁排。意向锁由InnoDB自动添加,无需用户干预。对于INSERT、UPDATE、DELETE,InnoDB会自动对涉及到的数据加排他锁;对于一般的SELECT语句,InnoDB不会加任何锁,事务可以通过下面的语句显式加共享锁或排它锁。共享锁:SELECT...LOCKINSHAREMODE;独占锁:SELECT...FORUPDATE;什么是死锁?如何防止死锁?什么是死锁?死锁是指两个或多个事务在同一个资源上相互占用,并请求锁定对方的资源,从而形成恶性循环的现象。如何防止死锁?尽量约定固定的顺序访问表,因为交叉访问更容易造成事务等待循环。尽量避免大额交易,建议拆分成多个小额交易。因为大事务占用的锁资源越多,越容易出现死锁。降低数据库隔离级别,比如RR到RC,因为RR隔离级别有GAP锁,死锁的概率会高很多。死锁和索引密不可分。适当优化索引将减少死锁的可能性。如果业务处理不好,怎么用分布式事务锁或者乐观锁来处理死锁呢?通过innodblockwait_timeout设置超时时间,等待超时。发起死锁检测,发现死锁后,主动回滚死锁中的事务,不需要其他事务继续执行。什么是全局锁?它的应用场景有哪些?全局锁就是锁住整个数据库实例。它的典型使用场景是对整个数据库进行逻辑备份。该命令可以在只读状态下使用整个数据库。使用该命令后,数据更新语句、数据定义语句、更新事务提交语句等操作都会被阻塞。使用全局锁带来的问题?如果在主库备份,备份期间无法更新,业务停止,更新业务会处于等待状态。如果备份了从库,备份过程中无法执行主库同步的binlog,导致主从延迟。什么是乐观锁和悲观锁?数据库中的并发控制是为了保证当多个事务同时访问数据库中的同一数据时,不破坏事务的隔离性和统一性以及数据库的统一性。乐观锁和悲观锁是并发控制中使用的主要技术手段。悲观锁:假设会发生并发冲突,将操作的数据加锁,直到事务提交后才释放锁,其他事务可以修改。实现方法:使用数据库中的锁机制。乐观锁:假设不会发生并发冲突,只在操作提交时检查数据是否被修改。在表中增加一个version字段,提交修改前检查version是否等于原来的version值。如果相等,说明数据没有被修改,可以更新。否则数据是脏的,无法更新。实现方法:乐观锁一般使用版本号机制或者CAS算法来实现。selectforupdate加表锁还是加行锁需要讨论:RR和RC隔离级别,以及查询条件(唯一索引、主键、通用索引、无索引)。如果查询条件是RC隔离级别下的唯一索引,会加IX意向排他锁(表级锁,不影响插入),两个X排他锁(行锁,对应唯一索引和主键索引)分别)。如果查询条件是主键,会加IX意向排他锁(表级锁,不影响插入),主键对应的一个X排他锁(行锁,会锁住主键的行关键索引)。如果查询条件是普通索引,如果查询命中一条记录,一个IX意向排他锁(表锁)和两个X排它锁(行锁,分别对应普通索引的X锁和primary的X锁键分别)将被添加;如果没有命中数据库表中的记录,则只加一个IX意向排他锁(表锁,不影响插入)。如果查询条件是无索引,会加两把锁,一把IX意向排他锁(表锁),一把X排他锁(行锁,主键对应的X锁)。查询条件是没有索引,为什么不锁表呢?MySQL会使用聚簇(主键)索引进行全表扫描过滤。每条记录都会加X锁。不过为了效率,MySQL在这方面做了改进。扫描过程中,不满足过滤条件的记录将被解锁。同时优化违反了2PL原则。在RR隔离级别,如果查询条件是唯一索引,命中数据库表记录时,一共加三把锁:一把IX意向排他锁(表锁,不影响插入),一把X主键对应排他锁(行锁),唯一索引对应一个X排他锁(行锁)。如果查询条件是主键,一个IX意向排他锁(表级锁,不影响插入),一个X主键对应的排他锁(行锁,会锁住主键的行索引)将被添加。如果查询条件是普通索引,如果命中查询记录,除了X锁(行锁),IX锁(表锁,不影响插入),Gap锁(间隙锁,会影响插入)添加。如果查询条件是无索引,会加一个IX锁(表锁,不影响插入),每行实际记录行加一个X锁,supremum伪记录对应的虚拟全表行锁添加。这种场景,通俗地说,其实就是表锁。参考链接:https://juejin.cn/post/7199666255884009532大家对锁的优化有什么建议吗?使用尽可能低的隔离级别。精心设计索引,尽可能使用索引访问数据,使加锁更加精确,从而减少锁冲突的机会。选择合理的事务大小,小事务发生锁冲突的概率也更小。显式锁定记录集时,最好立即请求足够级别的锁定。比如要修改数据,最好直接申请排他锁,而不是先申请共享锁,修改的时候再申请排他锁,容易死锁。当不同的程序访问一组表时,它们应该尽量同意以相同的顺序访问这些表。对于一个表,尽量以固定的顺序访问表中的行。这大大减少了死锁的机会。不要申请比实际需要更多的锁级别。除非必要,否则在查询时不要显式锁定。MySQL的MVCC可以实现无锁事务中的查询,优化事务性能;最后给大家分享一个Github仓库,里面有大斌编译的300多本经典计算机书籍PDF,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法,机器学习,编程生活等等,可以star一下,下次找书直接上搜,仓库持续更新~Github地址:https://github.com/Tyson0314/java-书籍