版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sun_ashe/article/details/82589567
在做Innodb加锁机制的分析时,需要查看各种信息,以确认当前语句的加锁情况,通常来讲,是通过show engine innodb status
来进行查看,但是很多情况下,show engine innodb status
显示的信息不够清晰,举个例子
//表结构
mysql> show create table ashe\G
*************************** 1. row ***************************
Table: ashe
Create Table: CREATE TABLE `ashe` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8mb4
1 row in set (0.00 sec)
//表中数据
mysql> select * from ashe;
+----+------+
| id | name |
+----+------+
| 1 | aaa |
| 5 | aaa |
| 8 | aa |
| 10 | aa |
| 12 | aaa |
| 13 | aaa |
+----+------+
6 rows in set (0.00 sec)
//开启事务
mysql> begin
-> ;
Query OK, 0 rows affected (0.01 sec)
//查询
mysql> select * from ashe where id=4 for update;
Empty set (0.00 sec)
如上所示的例子中,这条sql如何加锁呢?对Innodb加锁机制有点了解的同学,可能知道,是id=5这条索引记录的gap锁。但是,通过show engine innodb status
并不能清晰的发现,到底这条语句的加锁情况是怎样的,如下:
------------
TRANSACTIONS
------------
Trx id counter 327542
Purge done for trx's n:o < 327535 undo n:o < 0 state: running but idle
History list length 76
Total number of lock structs in row lock hash table 1
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 281479625653472, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 327541, ACTIVE 10 sec
2 lock struct(s), heap size 1160, 1 row lock(s)
MySQL thread id 3, OS thread handle 123145334444032, query id 102 localhost root
所以,这里介绍一种新的查看Innodb加锁信息的方法,就是修改代码,打印加锁日志。
关于下载源码,以及通过源码安装mysqld的方法请看https://blog.csdn.net/sun_ashe/article/details/81433759这里
下载代码之后,找到函数RecLock::lock_add(lock_t* lock, bool add_to_hash)
将其修改为如下:
RecLock::lock_add(lock_t* lock, bool add_to_hash)
{
ut_ad(lock_mutex_own());
ut_ad(trx_mutex_own(lock->trx));
if (add_to_hash) {
ulint key = m_rec_id.fold();
++lock->index->table->n_rec_locks;
HASH_INSERT(lock_t, hash, lock_hash_get(m_mode), key, lock);
ib::info()
<< "trx_id: "
<< lock->trx->id
<< " create a record lock and add it to lock hash table,"
<<"\nspace_id: "
<< m_rec_id.m_space_id
<<" \npage_no: "
<< m_rec_id.m_page_no
<< " \nheap_no: "
<< m_rec_id.m_heap_no
<<" \nn_bits: "
<<lock->un_member.rec_lock.n_bits
<< " \nprimary key: "
<< dict_index_is_clust(lock->index)
<< " \nis record lock: "
<< lock->is_record_lock()
<<" \nis waiting: "
<< lock->is_waiting()
<<"\nis gap: "
<<lock->is_gap()
<<"\nis record not gap: "
<<lock->is_record_not_gap()
<<"\nis insert intention: "
<<lock->is_insert_intention()
<<" \nlock_mode: "
<< static_cast<int>(LOCK_MODE_MASK&(lock->type_mode))
<<" (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE)"
;
}
else
{
ib::info() << "create a record lock, "
<<" space_id: "
<< lock->un_member.rec_lock.space
<<" page_no: "
<<lock->un_member.rec_lock.page_no
<<" n_bits: "
<<lock->un_member.rec_lock.n_bits
<< " primary key: "
<< dict_index_is_clust(lock->index)
<< " is record lock: "
<< lock->is_record_lock()
<<" is waiting: "
<< lock->is_waiting()
<<" is gap: "
<<lock->is_gap()
<<" is record not gap: "
<<lock->is_record_not_gap()
<<" is insert intention: "
<<lock->is_insert_intention();
}
if (m_mode & LOCK_WAIT) {
lock_set_lock_and_trx_wait(lock, lock->trx);
}
UT_LIST_ADD_LAST(lock->trx->lock.trx_locks, lock);
}
然后进行编译安装即可。
通过修改后的代码,可以在mysql的错误日志中,拿到如下类似的加锁信息,比如上文中举的例子
,可以看到如下:
2018-09-10T14:36:52.107552+08:00 3 [Note] InnoDB: trx_id: 327541 create a record lock and add it to lock hash table,
space_id: 183 //表空间id
page_no: 3 //page 号
heap_no: 3 //数据行的heap_no
n_bits: 80 //行锁bit map位置
primary key: 1 //是否为主键
is record lock: 1 //是否为行锁
is waiting: 0 //是否为等待状态
is gap: 1 //是否为gap锁,只锁gap,不锁记录
is record not gap: 0 //是否是 是锁记录,不锁gap
is insert intention: 0 //是不是带有insert intention属性
lock_mode: 3 (0:LOCK_IS, 1:LOCK_IX, 2:LOCK_S, 3:LOCK_X, 4:LOCK_AUTO_INC, 5:LOCK_NONE) //加锁类型
所以,我们可以通过如上得出,
对于select * from ashe where id=4 for update
这条sql的加锁方式为,对heap_no为3的这条索引记录加X gap 锁。
需要强调的时,这个日志信息,只会打印显示加锁的日志信息,对于隐式锁,另开一篇介绍。