十四. MySQL 锁相关
目录
- 一. MySQL 锁基础
- Mysql 锁分类
- 二. InnoDB 下的锁
- 增删改查操作时底层的加锁处理
- 表级锁
- 1. 意向锁
- 2. AUTO-INC锁
- id不连续对主从同步的影响
- 3. 其它表锁
- 行锁分析
- 1. 记录锁 Record Locks
- 2. 间隙锁 Gap Locks
- 3. 临键锁 Next-Key Locks
- 4. 插入意向锁
- 5. 隐式锁
- 6. 加锁算法
- InnoDB 行锁变降级为表锁
- 锁冲突
- 如何分析行锁定
- 三. MyISAM与表锁
- 1. 读锁
- 2. 写锁
- 3. 如何分析表锁定
- 4. 总结
- 四. 死锁排查
- 推荐优化
一. MySQL 锁基础
- MySQL 使用InnoDB存储引擎时,默认使用可重复读隔离级别,基于可重复读隔离级别使用MVCC解决了脏读,不可重复读,一定程度上解决了幻读问题, 那么真正是如何解决幻读的,或者说怎么保证数据安全的,引出了锁
- 或者说在快照读时基于MVCC可以读取undo版本日志中的历史版本,但是一些操作需要我们读取最新的版本,所以提出了锁
- 在不加锁执行读取操作时使用MVCC又称为快照度,一致性读
- 加锁读取时称为LBCC锁定读或当前读,LBCC(Lock-Based Concurreny Control)基于锁的并发控制
- MySQL不同存储引擎使用的锁不同
- MyISAM时采用的表锁,表锁下又细分为读锁,写锁
- InnoDB时根据是否命中索引添加的是行锁间隙锁,如果没命中索引则使用表锁
Mysql 锁分类
- 在操作角度分为: 读锁,写锁
读锁: 又叫共享锁,针对同一份数据,多个读操作可以同时进行互不影响
写锁: 又叫排它锁,在当前操作没完成之前,会阻断其它的读锁和写锁
- 在锁粒度角度分为:表锁,行锁,页锁(简单理解即可)
表锁(偏读锁,是不是可以理解为使用表锁是通常使用的是表锁中的读锁): 偏向MyISAM存储引擎,优点是:开销小, 加锁快, 无死锁, 锁粒度大,发生锁冲突竞争的概率高,并发度低(整个表都锁的原因)
行锁(偏写锁): 偏向InnoDB存储引擎,开销大,加锁慢,会出现死锁现象,锁粒度小,发生死锁概率低,并发度搞
- 在锁模式上又分为:
- 共享锁 Shared Locks
- 排它锁 Exclusive Locks
- 意向锁 Intention Locks: 意向锁又分为,意向共享锁Intention Shared Locks,意向排它锁 Intention Exclusive Locks
- 在锁算法上又分为
- 记录锁 Record Locks
- 间隙锁 Gap Locks
- 临键锁 Next Key Locks
二. InnoDB 下的锁
- 首先在使用InnoDB存储引擎时,如果对锁进行细分,内部包含
- 共享锁又叫乐观锁
- 互斥锁又叫排它锁
- 意向锁
- 记录锁
- 间隙锁
- 记录锁+间隙锁的临键锁Next-key Locks
- 插入意向锁
- 自增锁
- 空间索引预测锁
增删改查操作时底层的加锁处理
- InnoDB对于普通select查询不加锁,基于MVCC机制解决脏读,不可重复读,一定程度上解决幻读问题
- 手动针对select语句加锁示例:
共享锁(S):SELECT * FROM table_name WHERE … LOCK IN SHARE MODE
排他锁(X) :SELECT * FROM table_name WHERE … FOR UPDATE
- 对于UPDATE,DELETE和INSERT操作时,不考虑索引是否生效的情况下,可以概括的说为会给涉及到的数据添加X排它锁,X排它锁,因为索引失效时会锁表,并且直接执行insert操作,或者底层执行insert操作时特殊情况下会添加隐式锁,下面有分析
- 在delete操作时会添加X排它锁
- insert操作时一般是不加锁的,特殊情况下为了保证事物的隔离性会添加隐式锁(下面会讲解)
- update操作时要考虑更新前后数据的大小与是否更新主键等情况
- 如果更新前后数据大小相同,在mysql存储时可以理解为原地更新,此时添加的是X排它锁
- 如果更新后的数据大于更新前的,mysql底层会先删除就记录(delete_flag标记为删除状态),然后插入一条新记录,删除阶段添加的是X排它锁,插入新记录时跟insert操作相同使用隐式锁
- 更新主键时可以理解为组合了delete操作与insert操作,delete操作添加X排它锁,insert操作添加隐式锁
表级锁
1. 意向锁
- 首先意向锁IntentionLock,又分为意向共享锁IS, 与意向排它锁IX 是表锁
- 我理解的意向锁,在上层使用角度看并不是真实加锁,而是在表级加了一个锁标识,是为了提高加锁效率提出的意向锁
- 在没有意向锁时,假设事务A想获取某个表的表锁的时,就需要对该表下的每一行记录进行遍历,查看是否有其它事务进行了锁的获取,如果存在排它锁,则需要等待这些锁释放后才能获取,遍历成本较大
- 提出意向锁后,mysql底层在加锁时,如果加排它锁,会在表级别上添加一个意向排它锁,如果加共享锁首先会在表级别上添加意向共享锁,当下一个事物需要获取锁时,通过添加的意向锁标识就可以判断能不能获取到锁,不需要遍历判断
- 并且在添加意向锁时,当发现获取的资源已经被锁,需要判断当前加的是共享锁还是排它锁,被锁定的使用的是共享锁还是排它锁,如果两个都是共享锁,可以在表上面再次添加一个意向共享锁,意向共享锁可以同时并存多个,意向排他锁同时只能有一个存在
- InnoDB四种锁定模式下:共享锁S, 排他锁X, 意向共享锁IS, 意向排他锁IX之间的冲突关系
- 注意意向锁不能手动添加是InnoDB自动加的
- 怎么解释意向锁的表锁行锁共存: 根据2,3,意向锁相当于一个锁标识,结合锁冲突,去解释(有些博客中说意向锁就是为了解决表锁行锁共存所产生的)
2. AUTO-INC锁
- 给某个列加入自增长属性,例如自增主键,在并发操作时这个自增是怎么保证线程安全的,就是通过这个AUTO-INC锁加锁保证的,这个AUTO-INC也算是一个表级锁
- 在老版本时以自增主键为例,在插入数据时会在表级别上添加AUTO-INC锁,获取自增主键数据插入成功后再释放锁,可以认为锁定的范围是整个插入流程,进而保证自增主键的安全
- 在后续版本中考虑AUTO-INC在加锁与释放锁时的性能问题,进行了优化提出了轻量级锁,通过innodb_autoinc_lock_mode来设置,提供了3个模式(默认是consecutive 1)
- traditional: 设置为 0时,在该模式下,insert语句在开始时都会获得一个表锁autoinc_lock.该锁会一直持有到insert语句执行结束才会被释放,主从复制时,基于这种模式从库主库生成的主键id相同且有序
- consecutive: 设置为1时, 该模式下,对于简单的插入语句如果能够获取到插入数据条数,insert语句在开始时会获得一个表锁autoinc_lock,批量获取自增值,然后释放锁,不会等到数据插入完毕再释放,而对于复杂插入例如无法得到插入数据的条数,仍会加锁到数据插入完毕再释放,这种模式下获取的自增值是有序的,主从复制表现跟traditional下一样
- interleaved: 设置为2时, 该模式下,会评估的去批量获取自增值,对于能够判断出插入数据量的简单insert语句能保证ID是连续的,但是复杂的insert的ID则可能有断续出现,主从同步时,从库基于该模式可能造成id与主库不同切断续
id不连续对主从同步的影响
- mysql主从同步架构中,有两种同步模式:
- statement level基于SQL语句的复制
- row level基于一条记录的复制
- statement level与 row level 优缺点: statement模式下通过执行sql语句进行赋值,可以减少Binlog日志量,节约了IO成本,而row模式下是复制数据变更的内容,基于行的复制可能会产生大量的日志内容,IO成本比较高
- id不连续造成的问题: 如果我们主从同步中使用statement 基于sql语句进行复制,并且插入数据时的自增id是强制批量获取,当插入数据量不能预知,底层通过msyql自己预判生成批量获取id,是不可控的,就可能造成主库执行sql获取到id为1,3,5,当sql复制给从库,从库执行预判拿到了2,4,6的id,最终造成id不一致
3. 其它表锁
- 手动添加表锁
#读锁(共享锁S)
Lock table t Read;
#写锁(排它锁X)
Lock table t Write;
- 元数据锁: 在执行DDL语句对表结构,索引等等信息进行修改时,底层会添加一个元数据锁(Metadata Locks 简称MDL )是Server级别的锁,这也是表锁,用来保证读写的正确性
行锁分析
- 行锁在锁模式上分为共享锁与排它锁两种
- 共享锁又称读锁,当一个事务对某几行上读锁时,允许其他事务对这几行进行读操作,但不允许其进行写操作,也不允许其他事务给这几行上排它锁,但允许上读锁。
- 排它锁又称写锁,当一个事务对某几个上写锁时,不允许其他事务写,但允许读,更不允许其他事务给这几行上任何锁,包括写锁
- 在锁粒度上又分为: 记录锁、间隙锁、临键锁、插入意向锁
- 行锁的优缺点
- 优点: 锁定粒度小,发送锁冲突概率低,可以实现的并发度高
- 缺点: 锁的开销比较大,加锁会比较慢,容易出现死锁情况
- 使用InnoDB存储引擎创建 test_innoDB表,该表中有a,b两个字段,并分别对这两个字段添加索引,插入数据
- 学习测试中,关闭数据库自动提交
set autocommit=0;
- 行锁必须有索引才能实现,否则会自动锁全表,会自动降级为表锁,两个事务不能锁同一个索引
#事务A先执行:
select math from zje where math>60 for update;#事务B再执行:
select math from zje where math<60 for update;
#这样的话,事务B是会阻塞的。如果事务B把 math索引换成其他索引就不会阻塞,
#但注意,换成其他索引锁住的行不能和math索引锁住的行有重复。
1. 记录锁 Record Locks
- 记录锁是行锁,仅锁定一行,官方称为LOCK_REC_NOT_GAP, 记录锁内部也分为共享记录锁S, 排它记录锁X
- 记录锁锁定的是主键索引,如果没有主键会用唯一索引锁定,如果也没有会使用rowid
2. 间隙锁 Gap Locks
- MySQL在可重复读的隔离级别下是可以解决幻读问题的,解决方案有两种,可以使用MVCC,也可以采用加锁方案解决,用来解决幻读的锁就是间隙锁,因为在执行查询时可能出现幻读的位置上还没有数据,只能针对一定的范围添加间隙锁
- 间隙锁的产生条件
- 必须在RR可重复读隔离级别下
- 检索条件必须有索引(主键索引,或一个唯一索引除外),因为条件更新时若没有索引,行锁会升级为表锁,锁定整张表,包括不存在的所有记录,此时其他事务不能修改删除添加等操作)
- 间隙锁锁定的范围: 会根据检索条件向左寻找最靠近检索条件的记录值A,作为左区间,向右寻找最靠近检索条件的记录值B作为右区间,即锁定的间隙为
- 如下图以执行SQL :“update table set table.id=100 where number=5”为例解释,假设number上有索引, 此时间隙锁的范围向左4,向右11, 如果number=11,此时间隙锁的锁定范围向左5,向右无穷大,假设number=13,表中没有13这条数据,但是也存在间隙锁,向左11,向右无穷大,在锁定范围内的再去操作会阻塞等待
- 通过范围更新再次举例间隙锁 假设当前表中存在id为1,4,5,6记录,假设当前执行update,where的条件是id大于1并且id小于6的记录,这个时候,虽然中间没有id为2,3的记录,但是也会给上间隙锁,如果此时第二个请求过来查询id为2,或3的记录,就会阻塞等待(实际这块应该说是临建锁)
- 间隙锁的危害: 当锁定一个范围时,即使范围中间的某些键值不存在也会被加锁,这样就造成无法插入锁定键值范围内的任何数据,造成阻塞
- 问题: 执行更新操作,where条件中命中普通索引,在更新成功前有间隙锁吗,有,于命中普通索引,数据是根据普通索引进行排序加锁,如果在更新成功前执行插入操作,插入数据中对应普通索引字段值刚好是间隙锁的范围,则会阻塞等待
3. 临键锁 Next-Key Locks
- 临键锁是记录锁与间隙锁的组合体,实现了锁住指定记录 ,并且阻止其它事务在该记录前边的间隙插入新记录
- 临键锁针对 REPEATABLE READ 隔离级别下
- 以执行范围update更新操作为例,在执行更新时,InnoDB首先会给符合条件的已有数据的索引项添加记录锁,同时会锁定一个范围就叫做间隙,会针对这个间隙加锁,假设当前表中存在id为1,4,5,6记录,假设当前执行update,where的条件是id大于1并且id小于6的记录,这个时候,虽然中间没有id为2,3的记录,但是也会给上间隙锁,如果此时第二个请求过来查询id为2,或3的记录,就会阻塞等待
4. 插入意向锁
- 这里可以问为什么rr级别下next key锁能避免幻锁的原因
- 在执行插入操作时,会先检查当前插入的记录对应到落库的索引上是否存在锁对象(判断是否有间隙锁)如果锁住了,如果有,当前插入操作进行等待,并生成一个"插入意向锁"结构,插入意向锁也就是配合间隙锁或者临键锁一起防止幻读
- 我是这样理解插入意向锁的,一个锁结构,锁标识,表示一种意向,可以同时存在多个插入意向锁,多个插入操作打到同一个间隙锁上进行等待时都会生成插入意向锁,多个插入意向锁按顺序等待执行
5. 隐式锁
- 此处也可以问:insert操作会加锁吗?, 默认情况下是不加锁的,也可以说成会加一个隐式锁
- 在执行插入时,默认会给添加一个隐式锁,此时时无锁状态,当其它事物执行通过trx_id判断当前记录的事物还在活跃中,并且要获取该记录的X排它锁或者S共享锁时,这个隐式锁会升级为显示的X排它锁,只有当可能会产生冲突的时候才会加锁,减少锁的数量,提高系统的性能
- 隐式锁应该与插入意向锁一块来说:
- 隐式锁: INSERT 语句在执行插入之前,如果判断被插入数据落库的索引上有间隙锁,会先在这个间隙上插入意向锁,并且如果是唯一索引会进行重复 Key 判断,如果唯一索引值重复,会加排它锁与共享锁,(因为重复唯一索引后续可能会回滚删除,防止发生死锁)然后等待,获取到锁后进行插入
- 数据插入时会插入隐式锁, 在该插入事物未提交完毕时,其它事物要针对这条记录申请加锁时,这个隐式锁会升级为显示的X排它锁,有点像synchronized偏向锁到轻量级锁的过程
6. 加锁算法
- MySQL是针对索引进行加锁的,遵守以下加锁算法
- 主键索引: 如果我们加锁的行上存在主键索引, 会在这个主键索引上加 Record Lock 记录锁
- 辅助索引: 如果我们加锁的行上存在辅助索引, 会在这行的辅助索引上添加 Next-Key Lock,并在这行之后的辅助索引上添加一个 Gap Lock, 辅助索引上的 Next-Key Lock 和 Gap Lock 都是针对可重复读隔离模式存在的,这两种锁都是为了防止幻读现象的发生。
- 唯一的辅助索引: 也就是辅助索引是唯一索引时,MySQL 会将 Next-Key Lock 降级为 Record Lock,只会锁定当前记录。
- 如果唯一索引由多个列组成的,而我们只锁定其中一个列的话,那么此时并不会进行锁降级,还会添加 Next-KeyLock 和 GapLock。
- Insert 语句: 在 InnoDB 存储引擎中,对于 Insert 的操作,其会检查插入记录的下一条记录是否被锁定,若已经被锁定,则不允许查询。
- 未添加主键问题: 如果 InnoDB 表在创建的时候没有设置任何主键索引,那么 Record Lock 会锁住隐式的主键。
- 在加锁时,一条记录一个锁吗,不是, 有锁共享的条件,同一个事物,同一个数据页,同一个锁类型共享一把锁,否则会生成新锁
InnoDB 行锁变降级为表锁
- InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据才使用行级锁,否则会降级为表锁,所以在执行增删改操作时要注意是否能命中索引,有没有索引失效的问题
- 当表有多个索引的时候,不同的事务可以使用不同的索引锁定不同的行,不论是使用主键索引,唯一索引还是普通索引,InnoDB都会通过行锁来对数据加锁
- 进而引出一个问题: 由于MySQL的行锁不是针对记录加的锁,而是针对索引加的锁,所以即使是访问不同行的记录,如果使用了相同的索引键,也会出现锁冲突问题
- 还有一个问题索引是否生效是由MySQL通过执行优化器来觉得的,如果MySQL认为全表扫描效率更高,比如对一些很小的表,它就不会使用索引,这种情况下InnoDB将使用表锁,而不是行锁。因此在分析锁冲突时,别忘了检查SQL的执行计划,以确认是否真正使用了索引
锁冲突
- 什么是锁冲突: 假如对某一行数据加锁未释放,此时第二个请求需要操作这个被锁定的数据,第二个请求会阻塞等待前面的提交释放锁后才可以执行,这个阻塞等待,我们称为锁冲突
- 考虑锁冲突要了解InnoDB锁实现原理,是通过索引实现的,假如当前执行sql没有索引,则行锁变为表锁,表锁下如果第一个请求未释放,第二个请求无法更新该表数据会一直阻塞等待
- 如果一条执行更新的sql用到了索引,会根据该索引值锁定,如果此时第二个操作数据请求过来,有两种情况如果刚好用到被锁定的数据,会阻塞等待,如果用不到会正常执行
- 如果一条执行更新的sql用到了索引,并且是范围查询,如果此时第二个操作数据请求过来刚好命中这个范围内的数据(假设实际数据是1,4,5,范围是大于1小于等于5,就是第二个请求操作的是2,3也会阻塞,因为有间隙锁)
如何分析行锁定
- 执行"show status like ‘innodb_row-lock%’ " 查看是否有行锁,重点关注(Innodb_row_lock_time_avg等待的平均时长,Innodb_row_lock_waits等待的总次数,Innodb_row_lock_time等待总时长,如果等等次数很高,并且等待时长也不小的时候,就需要使用show proFile进行分析优化)
Innodb_row_lock_current_waits: 当前正在等待锁定的数量
Innodb_row_lock_time: 从系统启动到现在锁定总时间长度
Innodb_row_lock_time_avg: 每次等待所花费平均时间
Innodb_row_lock_time_max: 从系统启动到现在等待最长一次所花费时间
Innodb_row_lock_waits: 系统启动后到现在总共等待次数
- 还有默认情况下,看不到事物对哪些数据加了锁,需要通过"innodb_status_output_locks" 设置为"ON"(很少使用)
- 然后执行"show engine innodb status\G " 查看状态时,就可以获取到加锁的详细信息(可以获取到锁的哪个数据页, 锁的哪个索引列,锁类型等等信息)(很少使用)
三. MyISAM与表锁
- 使用MyISAM 存储引擎创建mylock表并添加数据
- 使用InnoDB存储引擎,创建book表(此处不加示例了)
- 加锁命令,read表示读锁, write表示写锁
#对一张表加锁
LOCK TABLE 表名 read/write;#一次对多张表加锁
LOCK TABLE 表名 read/write, 表2名 read/write;
4. 查看库中表是否被加锁命令
show open tables;
5. 释放锁命令
#执行完该命令后原则上当前库中的所有锁都会被释放
unlock tables;
1. 读锁
- 解释: 启动两个session会话对使用MyISAM存储引擎的mylock表添加读锁,查看当前session下,锁释放前能否再次读,能否修改数据,能否读取其它表数据,查看另外一个session会话能否读当前被加锁的数据,能否读取其它表数据,能发修改当前被加锁的数据,
- 当前加锁session会话执行示例:加锁后,发现,可以再次读取当前表数据,但是不能读取其它表数据,并且不能对当前加锁表进行增删改操作
- 其它session会话可以读取加读锁表的数据,可以读取其它不加锁的表数据,但是如果其它session会话对加锁表进行增删改操作会阻塞等待
2. 写锁
- 解释: 启动两个session会话对使用MyISAM存储引擎的mylock表添加写锁,
- 加写锁后,当前session会话下,可以再次读取当前表数据,可以更新当前表数据,但是无法更新,读取其它表数据
- 加锁后其它session会话,可以读取其操作其它表数据,但是读取与更新被加锁的表数据,会一直阻塞
3. 如何分析表锁定
- 执行" show status like ‘table%’ " 命令,通过table_locaks_waited 和table_locks_immediate状态变量来分析系统上的表锁定
Table_locks_immediate: 产生表级锁定的次数,表示可以立即获取锁的查询此处,每理解获取锁值加1
Table_locks_waited: 出现表级锁定,争用而发生的等待次数(不能立即获取锁的次数,每等待一次锁值加1),次值越高说明存在较严重的表级锁竞争情况
4. 总结
- MyISAM在执行查询语句(SELECT)前,会自动给所涉及表添加读锁,在执行增删改操作前会自动给涉及表添加写锁
- MySql表级锁有两种模式: 表共享读锁, 表独占写锁
- 根据上面的示例总结出
- MyISAM 执行查询操作时,会自动添加读锁,不会阻塞其它进程对当前表的读操作,会阻塞其它进程对当前表的更新操作,并且当前加锁进程无法再次读取当前表与其它表操作,也无法对当前表与其它表进行更新操作
- MySAM 执行增删改操作时会自动对该表添加写锁,写锁下,会阻塞其它进程对当前表的任何操作,但是当前进程是可以再次读取当前表的数据(无法读取其它表数据)
- 简单来说: 读写会阻塞写不会阻塞读,写锁时读与写都会阻塞
- MyISAM的读写锁调度是"写优先",不适合做以写为主的表的引擎,因为写锁后,其它线程不可以对该表进行任何操作,大量的更新操作可能会造成查询请求无法获取到锁,一直阻塞等待
四. 死锁排查
- 产生死锁的四个必要条件:(跟jdk中的死锁产生原因一样)
- 互斥: 首先时互斥锁
- 请求和保持: A先获取到锁一致持有,并请求获取B的锁
- 不剥夺: 进程已经持有的资源在未使用完毕时不会强制释放
- 环路等待: 线程1持有a锁等待获取线程2持有的b锁, 线程2持有b锁等待获取线程1持有a锁,形成环
- InnoDB发现死锁之后,会计算出两个发生死锁的事务各自操作的数据量(增删改)来判定两个事务的大小,数据较小的事务回滚,较大的事务成功完成.也就是说改变的记录条数越多在死锁中就越不会被回滚掉
- 但是需要注意,当产生死锁的场景中涉及到不止InnoDB存储引擎,InnoDB是没办法检测到该死锁的,这时候就只能通过锁定超时限制参数InnoDB_lock_wait_timeout来解决。这个参数并不是只用来解决死锁问题,在并发访问比较高的情况下,如果大量事务因无法立即获得所需的锁而挂起,会占用大量计算机资源,造成严重性能问题,甚至拖跨数据库。我们通过设置合适的锁等待超时阈值,可以避免这种情况发生。
- 通常来说,死锁都是应用设计的问题,通过调整业务流程、数据库对象设计、事务大小,以及访问数据库的SQL语句,绝大部分死锁都可以避免。下面就通过实例来介绍几种避免死锁的常用方法:
- 执行"SHOW ENGINE INNODB STATUS;"来查看死锁日志,显示
------------------------
LATEST DETECTED DEADLOCK
------------------------
170219 13:31:31
##1.TRANSACTION: 表示当前执行中的事物
*** (1) TRANSACTION:
TRANSACTION 2A8BD, ACTIVE 11 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 376, 1 row lock(s)
##2.关注点: root updating后是实际执行的sql
MySQL thread id 448218, OS thread handle 0x2abe5fb5d700, query id 18923238 renjun.fangcloud.net 121.41.41.92 root updating
delete from test where a = 2##3.WAITING FOR THIS LOCK TO BE GRANTED: 等待这个锁被授予(也就是当前执行的sql正在等待的锁)
##index `a` of table `oauthdemo`.`test` trx id 2A8BD lock_mode X waiting Record loc : 表示在等待oauthdemo库下的test表中的索引列为a,的x锁
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 923 n bits 80 index `a` of table `oauthdemo`.`test` trx id 2A8BD lock_mode X waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 320: len 4; hex 00000002; asc ;;1: len 4; hex 00000002; asc ;;##4.表示当前第二个执行中的事物
*** (2) TRANSACTION:
TRANSACTION 2A8BC, ACTIVE 18 sec inserting
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1248, 3 row lock(s), undo log entries 2
MySQL thread id 448217, OS thread handle 0x2abe5fd65700, query id 18923239 renjun.fangcloud.net 121.41.41.92 root update
##5.当前实际执行的sql
insert into test (id,a) values (10,2)##6. HOLDS THE LOCK(S):记录了事物2中持有的锁(index `a` of table `oauthdemo`.`test` 表示oauthdemo库中的tst表的a索引的锁) Record lock 记录锁
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 0 page no 923 n bits 80 index `a` of table `oauthdemo`.`test` trx id 2A8BC lock_mode X locks rec but not gap
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 320: len 4; hex 00000002; asc ;;1: len 4; hex 00000002; asc ;;##7.(2) WAITING FOR THIS LOCK TO BE GRANTED: 事物2中在申请的锁(index `a` of table `oauthdemo`.`test` trx id 2A8BC lock mode S waiting Record lock 表示事物2持有a列的锁,正在申请s锁"lock mode S waiting")
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 0 page no 923 n bits 80 index `a` of table `oauthdemo`.`test` trx id 2A8BC lock mode S waiting
Record lock, heap no 3 PHYSICAL RECORD: n_fields 2; compact format; info bits 320: len 4; hex 00000002; asc ;;1: len 4; hex 00000002; asc ;;*** WE ROLL BACK TRANSACTION (1)
- 死锁日志解释: 在日志中,每个动作都会进行分块记录,每个块中包含"TRANSACTION", “WAITING FOR THIS LOCK TO BE GRANTED”, " HOLDS THE LOCK(S)"大概这三个部分,通过前缀的序号来区分,序号相同的代表是同一个完整操作记录
#1. " *** (1) TRANSACTION: ": 可以看为第一次事物执行,里面包含了当前事物执行的sql语句,操作的库表信息
#2. " *** (1) WAITING FOR THIS LOCK TO BE GRANTED: ": 等待这个锁被授予(当前事物正在等待的锁),记录了等待的哪个库表中的锁,锁类型,
#3. " *** (2) HOLDS THE LOCK(S): ": 当前事物持有的锁,锁在哪个库表,锁的类型等前面的"(2)"对应第二个事物操作
- 根据上方的死锁日志分析为什么出现死锁:
- 通过 “(1) WAITING FOR THIS LOCK TO BE GRANTED” 分析到事物1中正在等待oauthdemo库下的test表中的索引列为a,的x锁
- 通过 “(2) HOLDS THE LOCK(S):” 分析到事物2正在持有oauthdemo库下的test表中的索引列为a锁,也就是事物1正在等待的锁
- 通过 “(2) WAITING FOR THIS LOCK TO BE GRANTED” 分析到事务2正在申请S锁,也就是共享锁,
- 重点: 为什么事物2要申请s锁,而不是x锁, s锁该锁是insert into test (id,a) values (10,2)语句申请的。insert语句在普通情况下是会申请排他锁,也就是X锁,但是这里出现了S锁。这是因为a字段是一个唯一索引,所以insert语句会在插入前进行一次duplicate key的检查,为了使这次检查成功,需要申请S锁防止其他事务对a字段进行修改。
- 重点: 为什么出现死锁,是因为申请s锁失败,为什么失败: 对同一个字段的锁的申请是需要排队的。S锁前面还有一个未申请成功的X锁,所以S锁必须等待
- 所以事物1在等待a的x锁,事物2持有a的x锁,在等待S锁,事物2的s锁获取前需要先拿到一个x锁,但由于x锁需要排队,形成了循环等待,死锁出现了
推荐优化
- 在检索查询过滤数据时尽可能使用索引完成,避免无索引行锁降级为表锁
- 合理设计索引,InnoDB通过索引加锁,尽量缩小锁的范围
- 尽可能少的过滤条件,避免间隙锁
- 尽量控制事物大小,减少锁定的资源量,时间长度
- 保证业务功能不受影响的前提下尽可能的降低事物隔离级别
相关文章:

十四. MySQL 锁相关
目录一. MySQL 锁基础Mysql 锁分类二. InnoDB 下的锁增删改查操作时底层的加锁处理表级锁1. 意向锁2. AUTO-INC锁id不连续对主从同步的影响3. 其它表锁行锁分析1. 记录锁 Record Locks2. 间隙锁 Gap Locks3. 临键锁 Next-Key Locks4. 插入意向锁5. 隐式锁6. 加锁算法InnoDB 行锁…...

ModStartBlog v7.0.0 网站简单统计,支持博客分享
ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。 系统完全开源,基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场,后台一键快速安装 …...

【C语言蓝桥杯每日一题】—— 递增序列
【C语言蓝桥杯每日一题】—— 递增序列😎前言🙌递增序列🙌总结撒花💞😎博客昵称:博客小梦 😊最喜欢的座右铭:全神贯注的上吧!!! 😊作者…...

node_express框架01
01_express 基本结构 注意点:app.get 指定了 get 方法,如果是 app.all 就是指定了所有的请求方法(例如:post delete 都是包含的),而 app.get(/) 里面访问的是根路径,如果访问别的路径ÿ…...

想转行做程序员,该怎么选择开发语言?哪个岗位工资最高?
本文主要针对零基础想了解或者转行从事开发岗的同学。 我们收集了往届毕业同学和一些正在咨询的同学,发现大家在学习初期,对转行互联网做开发,最多的疑问或者顾虑大体分为几类: 现在哪门语言比较火? 学什么语言好找到工…...

JavaWeb——【笔记】3.2JavaWeb_Web核心_Request(请求)+Response(响应)
Request(请求)Response(响应)两个对象 request、response是service()方法中的两个参数。作用分别是获取请求数据进行逻辑处理;对数据解析设置响应数据 一、简介 示例: 二、Request(请求) 1、Request继承体系 能更清楚其是由谁创建及查阅什么文档 2、Request获…...

HTML 标签和属性
一些标签 单双标签 双标签。双标签指标签是成对出现的,也就是有一个开始标签和一个结束标签,开始标签用 <标签名> 表示,结束标签用 </标签名> 表示,只有一对标签一起使用才能表示一个具体的含义。例如 <html>&…...

MySQL 连接的使用
MySQL 连接的使用 在前几章节中,我们已经学会了如何在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据。 本章节我们将向大家介绍如何使用 MySQL 的 JOIN 在两个或多个表中查询数据。 你可以在 SEL…...

配置案例丨EtherCAT转Profinet网关连接凯福科技总线步进驱动器
西门子S7-1200/1500系列的PLC,采用PROFINET实时以太网通讯协议,需要连接带EtherCAT的通讯功能的伺服驱动器等设备,就必须进行通讯协议转换。小疆GW-PN-ECATM系列的网关提供了,快速可行的解决方案。GW-PN-ECATM支持两种实时以太网通…...

VSCODE连接ssh服务器时提示could not establish connection to解决方法
VSCODE连接ssh服务器时提示could not establish connection to解决方法 1.点击扩展设置 在Remote.ssh:config file中输入config路径 重新连接即可,如果是之前连接过ubuntu现在无法连接则需要打开刚刚的地址文件中删掉known_hostsj即可 虚拟机中ubuntu安…...

网络安全之防火墙 双机热备实验
目录 网络安全之防火墙 双机热备实验 实验图 基本配置 PC1 SW2 PC2 编辑 SW3配置 登陆防火墙图形界面 编辑 FW1的配置 FW2的配置 新建trust to untrust 区域的安全策略 配置心跳线 在FW1与FW2之间拉一条心跳线 编辑 配置FW1 g 1/0/2 口 ip 编辑 配置FW2 g…...

Java高频面试题(2023最新整理)
Java的特点 Java是一门面向对象的编程语言。面向对象和面向过程的区别参考下一个问题。 Java具有平台独立性和移植性。 Java有一句口号:Write once, run anywhere,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编…...

mongoDB学习笔记
1.大数据定义: 数据量级大 byte kb MB GB TB PB ... 数据种类多 数据维度 例如:人物画像 数据处理速度快 数据有价值 问题:①.存储 ? ②.数据分析? ③.高并发? 大数据应用领域: 电商(推…...

快速融人,融资的共享模式,实体,线上皆可参考
有一种模式现在非常流行,它既能帮助商家快速收钱,又能帮助商家快速裂变更多客户,这个神奇的模式就是共享股东模式,现在很多老板都在用这个模式。 梦龙商业案例分析,带你了解商业背后的秘密 这个模式也适用于很多个行…...

纯干货版阿里巴巴国际站入门攻略
阿里巴巴国际站作为目前全球排名名列前茅的B2B电商平台,很多跨境电商卖家都很想入局。但是目前很多公司的国际站都没有专职运营的人员,只是靠外贸业务员操作,所以涉猎的都是比较浅的东西。今天龙哥就来讲讲如果想要深研这个平台的话ÿ…...

jQuery四、其他方法
零、文章目录 文章地址 个人博客-CSDN地址:https://blog.csdn.net/liyou123456789个人博客-GiteePages:https://bluecusliyou.gitee.io/techlearn 代码仓库地址 Gitee:https://gitee.com/bluecusliyou/TechLearnGithub:https:…...

2023年先进无人飞行系统国际会议(ICAUAS 2023) | IOP JPCS独立出版
会议简介 Brief Introduction 2023年先进无人飞行系统国际会议(ICAUAS 2023) 会议时间:2023年7月13日-16日 召开地点:中国哈尔滨&加拿大多伦多双会场 大会官网: ICAUAS 2023-2023 International Conference on Advanced Unmanned Aerial …...

2022蓝桥杯省赛——修剪灌木
问题描述 爱丽丝要完成一项修剪灌木的工作。 有 N 棵灌木整齐的从左到右排成一排。爱丽丝在每天傍晩会修剪一棵灌木, 让灌木的高度变为 0 厘米。爱丽丝修剪灌木的顺序是从最左侧的灌木开始, 每天向右修剪一棵灌木。当修剪了最右侧的灌木后, 她会调转方向, 下一天开始向左修剪…...

Spring Boot Aop初接触
AOP(面向切面编程),或多或少都听过一点。名字比较怪,切面,不容易理解,但其中真正含义,无非就是旁路控制,非侵入式编码之类。比如我想加个操作日志功能,利用AOP࿰…...

【创作赢红包】LeetCode:232. 用栈实现队列
🍎道阻且长,行则将至。🍓 🌻算法,不如说它是一种思考方式🍀算法专栏: 👉🏻123 一、🌱232. 用栈实现队列 题目描述:请你仅使用两个栈实现先入先出队…...

Mybatis+Mysql 实现向下递归查询
介绍 说到递归查询,大家可以想到的技术实现方式主要如下几种: 1、各种主流应用开发语言本身通过算法实现 2、各种数据库引擎自身提供的算法实现 本文提到主要是针对第二种和第一种的结合 主要技术栈 1、ORM:Mybatis 2、DB:MyS…...

python@调用系统命令行@os.system@subprocess@标准输入输出@sys.stdin@sys.stdout@input@print
文章目录python调用系统命令行os.system标准输入输出sys.stdinsys.stdoutinputprint概要os.systemdemoswindows命令解释器ComSpecsubprocessrecommended🎈基本用法demos标准输入输出sys.stdininput()sys.stdin.inputinput()交互模式小结sys.stdoutsys.stdout.wirte(…...

手握数据智能密钥,诸葛智能打开数字化经营“三重门”
科技云报道原创。 如果说上世纪传统麦迪逊大街上的“广告狂人”吸金立足之本,还主要是基于“Big Idea”的话,那么在当下,数据正在成为企业营销和运营的金矿。 这是一个“人与机器共同进化”的时代,技术作为延伸人类感觉的媒介之…...

C语言可以实现各种滤波算法
C语言可以实现各种滤波算法,以下是一些常见的滤波算法: 均值滤波(Mean Filter):将图像中每一个像素周围一定区域内的灰度值取平均值作为该像素的新灰度值,用于去除高斯噪声等随机噪声。 下面是一个简单的 C…...

使用Netty,当然也要了解它的连接闲置处理
连接闲置网络连接的闲置指的是当前网络连接处于空闲状态,即没有正在进行的数据传输或通信活动。当我们的某个连接不再发送请求或者接收响应的时候,这个连接就开始处于闲置状态。网络连接的闲置时间越长,说明该连接越不活跃。此时,…...

2、K-Planes
运行配置 主页:https://sarafridov.github.io/K-Planes/ 代码:https://github.com/sarafridov/K-Planes 预训练权重:https://drive.google.com/drive/folders/1zs_folzaCdv88y065wc6365uSRfsqITH Neural_3D_Video_Dataset:htt…...

张文海教授课题组在国际高水平期刊《Cerebral Cortex》发表研究成果
调节悲伤情绪对于维持伴侣间的浪漫关系至关重要。人际情绪调节策略包括情感参与(AE)和认知参与(CE),这两种策略在浪漫关系中效用如何?它们是如何通过情感纽带调节伴侣情绪的?其背后的脑际神经互…...

ffmpeg4.1 源码学习之-转封装
前言 ffmpeg 的源码量非常的多,而且非常繁杂,非常多的函数,如果一个函数一个函数看的话要花费比较多的时间。所以本文通过跟踪ffmpeg转封装的过程来学习ffmpeg的源码具体转封装的命令:ffmpeg -i 1_cut.flv -c copy -f mp4 1.mp4在…...

ChatGPT写作文章-快速使用ChatGPT不用注册方式
如何更好地使用ChatGPT批量生成文章:详细教程 作为一款强大的文本生成器,ChatGPT可以帮助您快速、高效地批量生成文章。但如果您还不知道如何更好地使用ChatGPT,那么这篇详细的列表教程将会指导您如何使用它来生成高质量的文章,提…...

Nginx配置ip白名单(服务权限控制)
Nginx服务器权限控制:Nginx 是一款高性能的 HTTP 和反向代理服务器。它可以通过配置文件实现权限控制,从而限制或允许特定的 IP 地址、网络或用户访问指定的资源。这里是一些基本的 Nginx 权限控制方法: 1. 基于 IP 地址的访问控制 在 Ngin…...