通过修改源码,让Innodb打印更多的加锁信息

版权声明:本文为博主原创文章,未经博主允许不得转载。 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 锁。

需要强调的时,这个日志信息,只会打印显示加锁的日志信息,对于隐式锁,另开一篇介绍。

猜你喜欢

转载自blog.csdn.net/sun_ashe/article/details/82589567