mysql死锁排查及解决,mysql查看死锁
关系型数据库发生死锁时,通过显示引擎引擎状态;命令并不能看到事务中引起死锁的所有结构化查询语言语句。
死锁排查起来就比较麻烦,需要查询事件_语句_%表,来获取SQL,同时需要对业务也比较熟悉,这样能分析出造成死锁的语句。
本着探究的目的,来看下关系型数据库死锁检测实现及为何无法打印出触发死锁的所有结构化查询语言语句。
锁定位图截取显示引擎引擎状态;命令查询锁信息时一段内容:
*** (1)等待授予此锁:
记录锁空间id 124第四页n位80索引idx_id表dhy t Trx id 45909锁模式X等待
记录锁,堆号2物理记录:n _ fields 2;紧凑格式;信息位32
0:镜头4;十六进制80000002;asc
1:镜头6;十六进制00000000601;asc空间身份证,页码、n位(锁定_记录_t结构体)通过这三个可以定位到某一条记录,这里对应的就是heap_no=2这条n _位。是一个位图,用来记录行记录上是否持有锁定。
lock_t结构用来描述锁信息的,通过n _位可以将锁定(lock_t类型的变量,下文中都将这样描述)与行记录的对应起来。锁定记录结构如下:
结构锁_记录_t {
ib_uint32_t空间;/*!空间id */
ib _ uint32 _ t page _ no/*!页码*/
ib _ uint32 _ t n _ bits/*!锁中的位数
位图;注意:锁位图是
紧接在
锁定结构*/
/**将记录锁打印到给定的输出流中
@param[in,out] out输出流
@返回给定的输出流。*/
STD:ostream print(STD:ostream out)const;
};n _位的大小分配为: (1 (记录锁64)/8) * 8
大小大小:
静态大小_t锁大小(常量页面_t*页面){
ulint n _ RECs=page _ dir _ get _ n _ heap(page);
/*将锁定位图放大一个安全余量*/
return(1((n _ RECs LOCK _ PAGE _ BITMAP _ MARGIN)/8));
}这么大的内存空间分配在锁内存之后的:
ulint n _ bytes=size sizeof(* lock);
mem _ heap _ t * heap=Trx-lock。lock _ heap
lock=reinterpret _ cast lock _ t *(mem _ heap _ alloc(heap,n _ bytes));在锁定记录设置第n位中,设置位图位图信息:
锁定_记录_设置_第n位(
/*=================*/
lock_t* lock,/*!输入:记录锁定*/
ulint i) /*!在:位的索引*/
{
ulint字节索引
ulint位索引
字节_索引=I/8;//标识第几个字节
bit _ index=I % 8;//标识字节中的第几位
((byte *)lock[1])[byte _ index]=1 bit _ index;//将对应字节上相应的少量位设置为一
锁-Trx-锁。n _ rec _ locks
}创建好的锁都会被添加到混杂表中,space_no与第_页相同的锁会被分配到同一个混杂桶中
ulint key=m _ rec _ id。fold();
锁-索引-表-不知道记录_锁;
HASH_INSERT(lock_t,HASH,lock_hash_get(m_mode),key,lock);
/**
@ return { space,page_no} */的折叠值
乌伦特褶皱()常量
{
return(m _ fold);
}判断锁上位图对应的少量位是否存在锁:
UNIV _内联
ibool
lock _ rec _ get _ n _ bit(/*=================*/
const lock_t* lock,/*!输入:记录锁定*/
ulint i) /*!在:位的索引*/{
常量字节* b;
if(I=lock-un _ member。记录_锁定。n _ bits){
返回(假);
}
b=((const byte *)lock[1])(I/8);//根据我(也就是heap_no)计算出是锁之后的第几个字节
return(1 * b(I % 8));//判断对应的少量位上是否为一
}死锁检测先介绍几个重要点:
变量:
const lock _ t * m _ wait _ lock//想持有的锁
const trx_t* m_start //直译过来是:正在以不兼容模式请求锁定的联接事务,举例说明下比较好理解:会话一
会议2
开始;
开始;
锁定:a
锁定:b
锁定:b
锁定:a
m _开始就是对应会议2这个事务
ulint heap_no //记录对应的物理位置号
函数get_first_lock //获取等待锁对应记录上的第一个锁,例如上面例子中,就是获取会话一中对a这条记录持有的锁信息。精简后流程如下:死锁检查器:get _ first _ lock(ulint * heap _ no)const
{
const lock _ t * lock=m _ wait _ lock
if(LOCK _ get _ type _ low(LOCK)==LOCK _ REC){
哈希_表_t*锁_哈希;
lock_hash=锁类型模式锁谓词
?lock _ sys-prdt _散列
:lock _ sys-rec _ hash;
/*我们只对匹配heap_no. */的记录感兴趣
* heap _ no=lock _ rec _ find _ set _ bit(lock);//找到锁对应的heap_no
/*找到页面上的锁。*/
lock=lock _ rec _ get _ first _ on _ page _ addr(
lock_hash,
lock- un_member.rec_lock.space,
lock-un _ member . rec _ lock . page _ no);//找到页面上的第一个锁
/*物理记录上第一个锁的位置。*/
如果(!Lock _ rec _ get _ nth _ bit (lock,* heap _ no)){//如果锁的位图对应的位上有锁,则返回锁,否则寻找下一个锁,直到对应的位上有锁。
lock=lock _ rec _ get _ next _ const(* heap _ no,lock);
}
}否则{
/*表锁不关心堆号*/
* heap _ no=ULINT _ UNDEFINED
dict _ table _ t * table=lock-un _ member . tab _ lock . table;
lock=UT_LIST_GET_FIRST(表锁);
}
返回(锁定);
} deadlock checker:search()中的死锁检测核心函数将执行以下操作:
在m_wait_lock对应的记录上获取第一个锁const lock _ t * lock=get _ first _ lock(heap _ no);进入一个循环,这里代码比较多,直接粘贴代码不直观。放个流程图。
过程漫长,比如看上图:
会话1
会议2
开始;
开始;
锁定:a
锁定:b
锁:b//阻塞
锁定:a
进入死锁检测时:
M _ start: session2事务信息锁:m _ wait _ lock对应于记录上的第一个锁,对应于session1持有的记录A上的锁M _ wait _ lock:session 2想要持有的记录A上的锁。
进入循环后,只满足条件lock-Trx-lock . que _ state==Trx _ que _ lock _ wait(这也意味着m_wait_lock和lock在等待一个锁)。第7步,m_wait_lock赋值后,是B在session2中想要持有的锁,锁变成session2到B持有的锁,再次进入循环。
此时lock- trx==m_start(都是session2),即检测到死锁。如下图所示:
日志死锁死锁日志只能看到事务中的最后一条SQL语句,因为每执行一条语句后,m_query_string变量都会被reset_query()。要实现这一点,需要在SQL语句和锁之间建立一个对应关系,以保持SQL每次都被执行。
这一块还涉及到死锁日志的一个参数:
Innodb_print_all_deadlocks:死锁信息将被打印到errorlock中。最好设置此参数以保持死锁日志便于查看,因为显示引擎innodb状态;只会保留上一次死锁日志的信息,因为mysql会在tmp目录下创建一个以ib开头的临时文件,每次重启后都会重新构建。这里的结论梳理了死锁检测的过程。由于水平有限,文中可能存在一些不准确之处。请指正。
版权归作者所有:原创作品来自博主小二上九8,转载请联系作者取得转载授权,否则将追究法律责任。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。