Innodb:為什麼lock in share mode在show engine看不到行鎖資訊

gaopengtttt發表於2019-03-20

水平有限 有誤請指出
版本:Percona MySQL 5.7.22
對於鎖的學習我做了一些輸出詳細參考如下:

其中有readme


一、問題提出

不知道有沒有朋友和我一樣用lock in share mode做加鎖實驗,但是卻在show engine innodb status中看不到加鎖資訊,今天剛好有朋友在問@在樹枝上吹風,今天就做了一下簡單的debug,因為我也挺納悶的。(我喜歡多問一個為什麼也挺累的)
問題如下:
首先我開啟了我的列印行鎖引數,讓加鎖輸出到日誌中

mysql> show variables like '%gaopeng%';
+--------------------------------+-------+| Variable_name                  | Value |
+--------------------------------+-------+
| gaopeng_mdl_detail             | OFF   || innodb_gaopeng_row_lock_detail | ON    |
+--------------------------------+-------+

然後跑如下語句

mysql> show create table t \G
*************************** 1. row ***************************       Table: t
Create Table: CREATE TABLE `t` (  `id` int(11) NOT NULL,  `c` int(11) DEFAULT NULL,  `d` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `c` (`c`)
) ENGINE=InnoDB DEFAULT CHARSET=utf81 row in set (0.00 sec)
mysql> select * from t;
+----+------+------+| id | c    | d    |+----+------+------+|  0 |    0 |    0 ||  5 |    5 |    5 || 10 |   10 |   10 || 15 |   15 |   15 || 20 |   20 |   20 || 25 |   25 |   25 |+----+------+------+6 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t where id=0 lock in share mode;
+----+------+------+| id | c    | d    |+----+------+------+|  0 |    0 |    0 |+----+------+------+1 row in set (4.21 sec)

按理說這個時候應該在主鍵ID=0這一行上了LOCK_S,但是show engine innodb卻看不到加鎖資訊如下:

------------
TRANSACTIONS
------------
Trx id counter 241482
Purge done for trx's n:o < 241482 undo n:o < 0 state: running but idle
History list length 182
Total number of lock structs in row lock hash table 1
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 422211785606640, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 422211785605248, not started
0 lock struct(s), heap size 1160, 0 row lock(s)
--------
FILE I/O
--------

根本看不到加鎖資訊。但是我的日誌中卻有輸出如下:

2019-03-20T14:37:41.980845+08:00 10 [Note] InnoDB: TRX ID:(0) table:test/t index:PRIMARY space_id: 95 page_id:3 heap_no:2 row lock mode:LOCK_S|LOCK_NOT_GAP|
PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000000; asc     ;;
 1: len 6; hex 00000003676a; asc     gj;;
 2: len 7; hex d8000000330110; asc     3  ;;
 3: len 4; hex 80000000; asc     ;;
 4: len 4; hex 80000000; asc     ;;

因此我基本斷定加鎖肯定是做了的,但是為什麼沒有輸出呢?

二、分析

我開始懷疑是否是提前釋放了或者是列印的時候過濾掉了?後來發現都不是。看了到了一個TRX_ID為422211785605248,這是隻讀事物的TRX_ID的形式,會不是因為應列印的時候只會列印讀寫的事物的鎖結構資訊,因為Innodb中讀寫事物有一個獨立的連結串列,如果只列印這個連結串列上的資訊就會出現這個問題。接著我做了一個事物先做了一個delete操作然後做lock in share mode語句可以看到LOCK_S結構就可以看到了,如下:

mysql> begin;
Query OK, 0 rows affected (2.43 sec)
mysql> delete from t2 limit 1; ##這個語句我就顯示的開始了一個讀寫事物Query OK, 1 row affected (3.53 sec)
mysql> select * from t where id=0 lock in share mode; 
+----+------+------+| id | c    | d    |+----+------+------+|  0 |    0 |    0 |+----+------+------+1 row in set (2.98 sec)
mysql>

再來看看

---TRANSACTION 422211785606640, not started0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 422211785605248, not started0 lock struct(s), heap size 1160, 0 row lock(s)
---TRANSACTION 241482, ACTIVE 85 sec4 lock struct(s), heap size 1160, 2 row lock(s), undo log entries 1MySQL thread id 10, OS thread handle 140737153423104, query id 391 localhost root
TABLE LOCK table `test`.`t2` trx id 241482 lock mode IX
RECORD LOCKS space id 33 page no 19 n bits 624 index GEN_CLUST_INDEX of table `test`.`t2` trx id 241482 lock_mode X(LOCK_X) locks rec but not gap(LOCK_REC_NOT_GAP)
Record lock, heap no 447 PHYSICAL RECORD: n_fields 4; compact format; info bits 32
 0: len 6; hex 00000000451d; asc     E ;; 1: len 6; hex 00000003af4a; asc      J;; 2: len 7; hex 3c000000453040; asc <   E0@;; 3: len 4; hex 80000001; asc     ;;
TABLE LOCK table `test`.`t` trx id 241482 lock mode IS
RECORD LOCKS space id 95 page no 3 n bits 80 index PRIMARY of table `test`.`t` trx id 241482 lock mode S(LOCK_S) locks rec but not gap(LOCK_REC_NOT_GAP)
Record lock, heap no 2 PHYSICAL RECORD: n_fields 5; compact format; info bits 0
 0: len 4; hex 80000000; asc     ;; 1: len 6; hex 00000003676a; asc     gj;; 2: len 7; hex d8000000330110; asc     3  ;; 3: len 4; hex 80000000; asc     ;; 4: len 4; hex 80000000; asc     ;;

我們看到了 lock mode S(LOCK_S) locks rec but not gap(LOCK_REC_NOT_GAP)的資訊看來沒有問題,猜測是實驗是一樣的,但是還是要原始碼驗證一下。

三、原始碼驗證

  • 1、列印函式lock_print_info_all_transactions
/*********************************************************************//**
Prints info of locks for each transaction. This function assumes that the
caller holds the lock mutex and more importantly it will release the lock
mutex on behalf of the caller. (This should be fixed in the future). */voidlock_print_info_all_transactions(/*=============================*/
    FILE*       file)   /*!< in/out: file where to print */{
    ut_ad(lock_mutex_own());    fprintf(file, "LIST OF TRANSACTIONS FOR EACH SESSION:\n");
    mutex_enter(&trx_sys->mutex);    /* First print info on non-active transactions */
    /* NOTE: information of auto-commit non-locking read-only
    transactions will be omitted here. The information will be
    available from INFORMATION_SCHEMA.INNODB_TRX. */
    PrintNotStarted print_not_started(file);//建立一個結構體,目的是做not start 事物的列印
    ut_list_map(trx_sys->mysql_trx_list, print_not_started); //這個地方列印出那些事物狀態是no start的事物,但是這裡存在一個問題,等會看程式碼在看。mysql_trx_list是全事物。
    const trx_t*    trx;
    TrxListIterator trx_iter; //這個迭代器是trx_sys->rw_trx_list 這個連結串列的迭代器
    const trx_t*    prev_trx = 0;    /* Control whether a block should be fetched from the buffer pool. */
    bool        load_block = true;    bool        monitor = srv_print_innodb_lock_monitor && (srv_show_locks_held != 0);    while ((trx = trx_iter.current()) != 0) { //透過迭代器進行迭代 ,顯然這裡不會有隻讀事物的資訊。
        check_trx_state(trx);        if (trx != prev_trx) {
            lock_trx_print_wait_and_mvcc_state(file, trx);
            prev_trx = trx;            /* The transaction that read in the page is no
            longer the one that read the page in. We need to
            force a page read. */
            load_block = true;
        }        /* If we need to print the locked record contents then we
        need to fetch the containing block from the buffer pool. */
        if (monitor) {            /* Print the locks owned by the current transaction. */
            TrxLockIterator& lock_iter = trx_iter.lock_iter();            if (!lock_trx_print_locks(
                    file, trx, lock_iter, load_block)) {                /* Resync trx_iter, the trx_sys->mutex and
                the lock mutex were released. A page was
                successfully read in.  We need to print its
                contents on the next call to
                lock_trx_print_locks(). On the next call to
                lock_trx_print_locks() we should simply print
                the contents of the page just read in.*/
                load_block = false;                continue;
            }
        }
        load_block = true;        /* All record lock details were printed without fetching
        a page from disk, or we didn't need to print the detail. */
        trx_iter.next();
    }
    lock_mutex_exit();
    mutex_exit(&trx_sys->mutex);
    ut_ad(lock_validate());
}

這個函式是呼叫的邏輯。

  • 結構體PrintNotStarted括號過載
void    operator()(const trx_t* trx)
    {
        ut_ad(trx->in_mysql_trx_list);
        ut_ad(mutex_own(&trx_sys->mutex));        /* See state transitions and locking rules in trx0trx.h */
        if (trx_state_eq(trx, TRX_STATE_NOT_STARTED)) {//這裡我們發現只有狀態為TRX_STATE_NOT_STARTED才會進行輸出
            fputs("---", m_file);
            trx_print_latched(m_file, trx, 600);
        }
    }

我們這裡可以看到只有狀態為TRX_STATE_NOT_STARTED才會輸出為not start狀態。

TrxListIterator迭代器初始化程式碼
    TrxListIterator() : m_index()
    {        /* We iterate over the RW trx list first. */
        m_trx_list = &trx_sys->rw_trx_list;
    }

我們這裡可以看到只有讀寫事物才會進行鎖結構的輸出。

四、問題總結

我發現既然如此,我們show engine innodb status的事物部分的輸出中我們發現會列印出如下資訊:

  • 事物狀態是not start的事物資訊
  • 正在執行的讀寫事物的資訊包括鎖資訊

但是如果只讀事物事物狀態為running狀態的呢?這裡就不列印了,但是這其實是一個比較關鍵的資訊,這個時候只有在innodb_trx 檢視中去看了如下:

mysql> select * from INNODB_TRX \G;
*************************** 1. row ***************************                    trx_id: 422211785602464                 trx_state: RUNNING
               trx_started: 2019-03-20 15:21:54...

真是因為如此lock in share mode 事物在show engine innodb 是看不到的,因為壓根不會列印,除非先顯示開啟一個讀寫事物。而對於 for update 這種上LOCK_X鎖的事物會歸入讀寫事物因此可以列印出來。
最後吐槽一下show engine innodb 事物列印部分應該可以改進啊。

作者微信:gp_22389860


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2638875/,如需轉載,請註明出處,否則將追究法律責任。

相關文章