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

Redundant clustered lookups in covering scans when holding a table X-lock

    XMLWordPrintable

Details

    • Related to performance
    • Q4/2026 Server Maintenance

    Description

      Consider the following

      CREATE TABLE t1 (id INT PRIMARY KEY, sec INT, pad CHAR(100), KEY i1(sec))
      ENGINE=InnoDB;
      INSERT INTO t1 SELECT seq, seq, seq FROM seq_1_to_10000;
      SELECT sec,id FROM t1 FOR UPDATE;
      

      The SELECT statement is a covering scan on the secondary index i1. After MDEV-24813 it acquires a table X-lock. However, instead of doing only a scan on i1, it lock and reads the clustered records:

      /* in row_search_mvcc: */
      	if (index != clust_index && prebuilt->need_to_access_clustered) {
      		if (row_search_with_covering_prefix(prebuilt, rec, offsets)) {
      			goto use_covering_index;
      		}
      requires_clust_rec:
       
      /* in row_search_with_covering_prefix: */
      	/* In ha_innobase::build_template() we choose to access the
      	whole row when using exclusive row locks or In case of fts
      	query, we need to read from clustered index */
      	if (prebuilt->select_lock_type == LOCK_X || prebuilt->in_fts_query
      	    || !index->is_btree()) {
      		return false;
      	}
      

      This is not the case for S-lock, which contributes to the discrepancy in performance gains between FOR UPDATE and LOCK IN SHARE MODE for full index scans, see https://jira.mariadb.org/browse/TODO-6178?focusedCommentId=336460&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-336460.

      With innodb_table_lock_on_full_scan on, we save on the clustered locks, but FOR UPDATE still suffers from extra clustered reads.

      This can be observed in the following testcase:

      --source include/have_innodb.inc
      --source include/have_sequence.inc
      SET GLOBAL innodb_monitor_enable='lock_rec_lock_created';
      CREATE TABLE t1 (id INT PRIMARY KEY, sec INT, pad CHAR(100), KEY i1(sec))
      ENGINE=InnoDB;
      INSERT INTO t1 SELECT seq, seq, seq FROM seq_1_to_10000;
      let $bp= SELECT VARIABLE_VALUE FROM information_schema.GLOBAL_STATUS
               WHERE VARIABLE_NAME='INNODB_BUFFER_POOL_READ_REQUESTS';
      let $lk= SELECT COUNT FROM information_schema.INNODB_METRICS
               WHERE NAME='lock_rec_lock_created';
       
      set innodb_table_lock_on_full_scan=0;
       
      let $r1= `$bp`;
      let $l1= `$lk`;
      --disable_result_log
      SELECT sec,id FROM t1 FOR UPDATE;
      --enable_result_log
      let $r2= `$bp`;
      let $l2= `$lk`;
      --disable_result_log
      SELECT sec,id FROM t1 LOCK IN SHARE MODE;
      --enable_result_log
      let $r3= `$bp`;
      let $l3= `$lk`;
       
      set innodb_table_lock_on_full_scan=1;
       
      --disable_result_log
      SELECT sec,id FROM t1 FOR UPDATE;
      --enable_result_log
      let $r4= `$bp`;
      let $l4= `$lk`;
      --disable_result_log
      SELECT sec,id FROM t1 LOCK IN SHARE MODE;
      --enable_result_log
      let $r5= `$bp`;
      let $l5= `$lk`;
       
      --echo # read requests
      eval SELECT ($r2 - $r1) as x_off, ($r3 - $r2) as s_off,  ($r4 - $r3) as x_on, ($r5 - $r4) as s_on;
       
      --echo # locks
      eval SELECT ($l2 - $l1) as x_off, ($l3 - $l2) as s_off,  ($l4 - $l3) as x_on, ($l5 - $l4) as s_on;
       
      DROP TABLE t1;
      SET GLOBAL innodb_monitor_enable=default;
      

      it prints:

      # read requests
      #  [... 1 lines elided]
      x_off	s_off	x_on	s_on
      20011	11	20011	11
      # locks
      #  [... 1 lines elided]
      x_off	s_off	x_on	s_on
      96	10	0	0
      

      Attachments

        Issue Links

          Activity

            People

              ycp Yuchen Pei
              ycp Yuchen Pei
              Yuchen Pei Yuchen Pei
              Marko Mäkelä Marko Mäkelä
              Votes:
              0 Vote for this issue
              Watchers:
              4 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Time Tracking

                  Estimated:
                  Original Estimate - 1d 4h 38m Original Estimate - 1d 4h 38m
                  1d 4h 38m
                  Remaining:
                  Remaining Estimate - 0d
                  0d
                  Logged:
                  Time Spent - 2d 25m
                  2d 25m

                  Git Integration

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