Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-9839

update one row but lock more than one rows

    XMLWordPrintable

Details

    Description

      tx_isolation is READ-COMMITTED
      table:

      CREATE TABLE `b` (
        `a` int(11) NOT NULL,
        `b` int(11) NOT NULL,
        `c` varchar(20) COLLATE utf8_bin DEFAULT NULL,
        `d` varchar(10) COLLATE utf8_bin DEFAULT NULL,
        PRIMARY KEY (`a`,`b`)
      

      data:

      MariaDB [test]> select * from b;
      +---+----+-------+------+
      | a | b  | c     | d    |
      +---+----+-------+------+
      | 1 |  1 | 222   | 1    |
      | 1 |  2 | 333   | 2    |
      | 1 |  3 | 10406 | 3    |
      | 1 |  4 | 60736 | 4    |
      | 1 |  5 | 50170 | 5    |
      | 1 |  6 | 55447 | 6    |
      | 1 |  7 | 29705 | 7    |
      | 1 |  8 | 13657 | 8    |
      | 1 |  9 | 63638 | 9    |
      | 1 | 10 | 36187 | 10   |
      +---+----+-------+------+
      10 rows in set (0.01 sec)
      

      excute plain is:

      MariaDB [test]> explain update test.b set c ='111'  where b= 1;
      +------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
      | id   | select_type | table | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
      +------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
      |    1 | SIMPLE      | b     | index | NULL          | PRIMARY | 8       | NULL |   10 | Using where |
      +------+-------------+-------+-------+---------------+---------+---------+------+------+-------------+
      1 row in set (0.00 sec)
      

      When i opened 5 sessions to execute

      update test.b set c ='111' where b=1,2,3,4,5

      some of the sessions may lock more than one rows,like this:

      ---TRANSACTION 122145, ACTIVE 761 sec
      3 lock struct(s), heap size 1248, 2 row lock(s), undo log entries 1
      MySQL thread id 31, OS thread handle 0x7f644b3ec700, query id 420 localhost root cleaning up
      TABLE LOCK table `test`.`b` trx id 122145 lock mode IX
      RECORD LOCKS space id 5 page no 3 n bits 192 index `PRIMARY` of table `test`.`b` trx id 122145 lock_mode X locks rec but not gap
      Record lock, heap no 118 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
       0: len 4; hex 80000001; asc     ;;
       1: len 4; hex 80000003; asc     ;;
       2: len 6; hex 00000001dd21; asc      !;;
       3: len 7; hex 170000019518c3; asc        ;;
       4: len 3; hex 313131; asc 111;;
       5: len 1; hex 33; asc 3;;
       
      RECORD LOCKS space id 5 page no 3 n bits 192 index `PRIMARY` of table `test`.`b` trx id 122145 lock_mode X locks rec but not gap
      Record lock, heap no 99 PHYSICAL RECORD: n_fields 6; compact format; info bits 0
       0: len 4; hex 80000001; asc     ;;
       1: len 4; hex 80000005; asc     ;;
       2: len 6; hex 00000000383a; asc     8:;;
       3: len 7; hex 420000019a0281; asc B      ;;
       4: len 5; hex 3530313730; asc 50170;;
       5: len 1; hex 35; asc 5;;
       

      I traced the source code, and guess this may be a bug: row_search_for_mysql() calls sel_set_rec_lock() try to lock one row

      //if had to wait :
      case DB_LOCK_WAIT:
      			/* Never unlock rows that were part of a conflict. */
      			prebuilt->new_rec_locks = 0;   //set new_rec_locks=0
      //try semi consistent read :
      row_sel_build_committed_vers_for_mysql()
       
      /*The lock was granted while we were
      				searching for the last committed version.
      				Do a normal locking read. */
      				
      //then:				
      case DB_SUCCESS:
      			if (clust_rec == NULL) {
      				/* The record did not exist in the read view */
      				ut_ad(prebuilt->select_lock_type == LOCK_NONE);
       
      				goto next_rec;	
      				
      //only really did a semi consistent read, would set did_semi_consistent_read. but in my case,skipped this 
      did_semi_consistent_read = TRUE;
       
      //so the value of did_semi_consistent_read is FLASE,and set prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT
       
      if (UNIV_UNLIKELY(did_semi_consistent_read)) {
      	prebuilt->row_read_type = ROW_READ_DID_SEMI_CONSISTENT; 
      } else {
      	prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
      }
       
       
      ha_innobase::unlock_row()
      	switch (prebuilt->row_read_type)
      	
      	case ROW_READ_TRY_SEMI_CONSISTENT:
      		row_unlock_for_mysql(prebuilt, FALSE);
      		break;
      	case ROW_READ_DID_SEMI_CONSISTENT:
      		prebuilt->row_read_type = ROW_READ_TRY_SEMI_CONSISTENT;
      		break;
       
      row_unlock_for_mysql()
      //because prebuilt->new_rec_locks was set to 0,so the locked row can't be unlock!!
      if (prebuilt->new_rec_locks >= 1) {
      lock_rec_unlock()
      }
      

      Attachments

        Activity

          People

            jplindst Jan Lindström (Inactive)
            linzhoukai Zhoukai Lin
            Votes:
            1 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Git Integration

                Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.