기술 공유 | InnoDB Handlerread_* 변수 해석

저자: 고붕
글 말미에는 그가 저술한'마이SQL 주종원리 깊이 이해32강'이 있고, 마이SQL 주종, GTID 관련 기술 지식을 깊이 있게 이해한다.
원본 버전:percona 5.7.14
본문은 학습 기록이므로 오류가 있을 수 있으니 양해해 주십시오.
이 문서는 PC에서 보는 것이 더욱 효과적일 것을 권장합니다.
1,Handlerread_* 값의 본질
내부 표현은 다음과 같습니다.
{"Handler_read_first",       (char*) offsetof(STATUS_VAR, ha_read_first_count),     SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},
{"Handler_read_key",         (char*) offsetof(STATUS_VAR, ha_read_key_count),       SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},
{"Handler_read_last",        (char*) offsetof(STATUS_VAR, ha_read_last_count),      SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},
{"Handler_read_next",        (char*) offsetof(STATUS_VAR, ha_read_next_count),      SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},
{"Handler_read_prev",        (char*) offsetof(STATUS_VAR, ha_read_prev_count),      SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},
{"Handler_read_rnd",         (char*) offsetof(STATUS_VAR, ha_read_rnd_count),       SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},
{"Handler_read_rnd_next",    (char*) offsetof(STATUS_VAR, ha_read_rnd_next_count),  SHOW_LONGLONG_STATUS,    SHOW_SCOPE_ALL},

실제로 이 변수들은 모두 MySQL 층에서 정의한 것이다. 왜냐하면 MySQL은 여러 개의 저장 엔진을 포함할 수 있기 때문이다.따라서 이러한 값을 어떻게 증가하려면 엔진 층의 인터페이스에서 자체적으로 실현해야 한다. 즉, 각 엔진은 모두 자신의 실현을 가지고 MySQL층에서 총결산을 해야 하기 때문에 이런 값은 특정한 엔진 특유의 것이 아니다. 예를 들어 Innodb와 MyISAM 엔진이 있다면 이 값은 두 엔진의 총체이다.이 문서는 Innodb를 주요 학습 대상으로 삼아 해석할 것이다.
2. 각 값의 해석
1. Handler_read_key
  • 내부 표시:haread_key_count
  • Innodb 인터페이스 변경:hainnobase::index_read
  • 설명서: The number of requests to read a row based on a key.If this value is high, it is a good indication that your tables are properly indexed for your queries.
  • 소스 함수 설명: Positions an index cursor to the index specified in the handle.Fetches the row if any.
  • 저자 설명: 이 함수는 색인에 접근할 때 값이 있는 위치를 지정하는 데 사용되는 함수입니다. 색인을 읽는 시작 위치를 알아야 아래로 접근할 수 있기 때문입니다.

  • 2. Handler_read_next
  • 내부 표시:haread_next_count
  • Innodb 인터페이스 변경:hainnobase::index_next_same ha_innobase::index_next
  • 설명서: The number of requests to read the next row in key order.This value is incremented if you are

  • querying an index column with a range constraint or if you are doing an index scan.
  • 원본 함수 해석:
  • index_next - Reads the next row from a cursor, which must have previously been positioned using index_read.index_next_same - Reads the next row matching to the key value given as the parameter.
  • 저자 설명: 색인에 접근한 다음 데이터가 봉인된hainnobase::general_fetch 함수, indexnext_same 및 indexnext는 접근 방식이 다르다는 데 다르다. 예를 들어 범위range 검색은 색인과 전체 검색에 사용되고 indexnext,ref 접근 방식은 indexnext_same

  • 3. Handler_read_first
  • 내부 표시:haread_first_count
  • Innodb 인터페이스 변경:hainnobase::index_first
  • 설명서: The number of times the first entry in an index was read.If this value is high, it suggests that the

  • server is doing a lot of full index scans; for example, SELECT col1 FROM foo, assuming that col1is indexed
  • 소스 함수 설명: Positions a cursor on the first record in an index and reads the corresponding row to buf.
  • 저자 설명: 포지셔닝 인덱스의 첫 번째 데이터는 실제로도 봉인된hainnobase::index_read 함수 (예: 전체 테이블 스캔/전체 인덱스 스캔 호출)
  • 4. Handler_read_rnd_next
  • 내부 표시:haread_rnd_next_count
  • Innodb 인터페이스 변경:hainnobase::rnd_next
  • 설명서: The number of requests to read the next row in the data file.This value is high if you are doing a lot of table scans. Generally this suggests that your tables are not properly indexed or that your queries

  • are not written to take advantage of the indexes you have.
  • 소스 함수 설명: Reads the next row in a table scan(also used to read the FIRST row in a table scan).
  • 저자 설명: 전체 테이블 스캐닝이 다음 데이터에 접근하면 실제로도 봉인된hainnobase::general_fetch, 방문하기 전에hainnobase::index_first

  • 5. Handler_read_rnd
  • 내부 표시:haread_rnd_count
  • Innodb 인터페이스 변경:hainnobase::rnd_pos
  • Memory 인터페이스 변경:haheap::rnd_pos
  • 설명서: The number of requests to read a row based on a fixed position.This value is high if you are doing a lot of queries that require sorting of the result. You probably have a lot of queries that require MySQL to scan entire tables or you have joins that do not use keys properly.
  • 저자는 이 상태값은 내가 테스트하는 동안 임시표에 대한 정렬만 발견할 때 사용되고 메모리 엔진이기 때문에 구체적으로 문서에 따라 이해할 수 밖에 없다고 설명했다.

  • 6.기타
    마지막 두 개. 짧게 말해주세요.
  • Handler_read_prev

  • Innodb 인터페이스는 ha 입니다.innobase::index_prev 색인에 접근한 이전 데이터, 실제로도 봉인된hainnobase::general_fetch 함수, ORDER By DESC 인덱스 검색을 위한 정렬 방지, 내부 상태 값haread_prev_count 증가.
  • Handler_read_last

  • Innodb 인터페이스는 ha 입니다.innobase::index_last 접근 인덱스의 마지막 데이터를 포지셔닝으로 하고 실제로는 봉인된hainnobase::index_ORDER BY DESC 색인 검색을 위한 정렬 방지, 내부 상태 값 haread_last_count 증가.
    3. 상용 조회 테스트
    1. 테스트 용례
    mysql> show create table z1;
    +-------+-------------------------------------------------------------------------------------------------------------------------------------------+
    | Table | Create Table                                                                                                                              |
    +-------+-------------------------------------------------------------------------------------------------------------------------------------------+
    | z1    | CREATE TABLE `z1` (
      `a` int(11) DEFAULT NULL,
      `name` varchar(20) DEFAULT NULL,
      KEY `a` (`a`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
    +-------+-------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.00 sec)
    
    mysql> show create table z10;
    +-------+------------------------------------------------------------------------------------------------------------------------------------------------+
    | Table | Create Table                                                                                                                                   |
    +-------+------------------------------------------------------------------------------------------------------------------------------------------------+
    | z10   | CREATE TABLE `z10` (
      `a` int(11) DEFAULT NULL,
      `name` varchar(20) DEFAULT NULL,
      KEY `a_idx` (`a`)
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
    +-------+------------------------------------------------------------------------------------------------------------------------------------------------+
    1 row in set (0.00 sec)
    mysql> select count(*) from z1;
    +----------+
    | count(*) |
    +----------+
    |    56415 |
    +----------+
    1 row in set (5.27 sec)
    
    mysql> select count(*) from z10;
    +----------+
    | count(*) |
    +----------+
    |       10 |
    +----------+
    1 row in set (0.00 sec)

    2. 전체 테이블 스캔
    mysql> desc select * from z1;
    +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows  | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
    |  1 | SIMPLE      | z1    | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 56650 |   100.00 | NULL  |
    +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> pager cat >>/dev/null
    PAGER set to 'cat >>/dev/null'
    mysql> flush status;
    Query OK, 0 rows affected (0.10 sec)
    mysql> select * from z1;
    56415 rows in set (4.05 sec)
    mysql> pager;
    Default pager wasn't set, using stdout.
    mysql> show status like 'Handler_read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 1     |
    | Handler_read_key      | 1     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 0     |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 56416 |
    +-----------------------+-------+
    7 rows in set (0.01 sec)

    Handler_read_first 첫 번째 포지셔닝에 1회 추가,Handlerread_키 1회 증가,Handlerread_rnd_next에서 스캔 줄 수를 증가합니다.저희가 앞에서 말했잖아요. 하 때문에.innobase::index_first도 봉인된hainnobase::index_read는 따라서 +1이 필요합니다.
    3. 전체 인덱스 검색
    mysql> desc select a from z1;
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-------------+
    | id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows  | filtered | Extra       |
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-------------+
    |  1 | SIMPLE      | z1    | NULL       | index | NULL          | a    | 5       | NULL | 56650 |   100.00 | Using index |
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> flush status;
    Query OK, 0 rows affected (0.12 sec)
    
    mysql> pager cat >>/dev/null
    PAGER set to 'cat >>/dev/null'
    mysql> select a from z1;
    56415 rows in set (4.57 sec)
    
    mysql> pager
    Default pager wasn't set, using stdout.
    mysql> show status like 'Handler_read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 1     |
    | Handler_read_key      | 1     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 56415 |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+
    7 rows in set (0.01 sec)

    Handler_read_first 첫 번째 포지셔닝에 1회 추가,Handlerread_키 1회 증가,Handlerread_다음 줄을 연속으로 접근하는 데 사용할 스캔 줄 수를 추가합니다.저희가 앞에서 말했잖아요. 하 때문에.innobase::index_first도 봉인된hainnobase::index_read는 따라서 +1이 필요합니다.
    4. 색인 ref 액세스
    저는 여기 테스트 인덱스라서 다 10이고 force index가 붙었어요.
    mysql>  desc select  * from z1 force index(a) where a=10;
    +----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key  | key_len | ref   | rows  | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------+
    |  1 | SIMPLE      | z1    | NULL       | ref  | a             | a    | 5       | const | 28325 |   100.00 | NULL  |
    +----+-------------+-------+------------+------+---------------+------+---------+-------+-------+----------+-------+
    1 row in set, 1 warning (0.01 sec)
    
    mysql> flush status;
    Query OK, 0 rows affected (0.13 sec)
    
    mysql> pager cat >>/dev/null
    PAGER set to 'cat >>/dev/null'
    mysql> select  * from z1 force index(a) where a=10;
    56414 rows in set (32.39 sec)
    mysql> pager
    Default pager wasn't set, using stdout.
    mysql> show status like 'Handler_read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 0     |
    | Handler_read_key      | 1     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 56414 |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+
    7 rows in set (0.06 sec)

    Handler_read_키 1회 추가 첫 번째 포지셔닝에 사용됨,Handlerread_다음 데이터 접근에 사용할 스캔 줄 수를 늘립니다.
    5. 색인range 액세스
    mysql> desc select  * from z1 force index(a) where a>9 and a<12;
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-----------------------+
    | id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows  | filtered | Extra                 |
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-----------------------+
    |  1 | SIMPLE      | z1    | NULL       | range | a             | a    | 5       | NULL | 28325 |   100.00 | Using index condition |
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-----------------------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql>  pager cat >>/dev/null
    PAGER set to 'cat >>/dev/null'
    mysql> select  * from z1 force index(a) where a>9 and a<12;
    56414 rows in set (47.54 sec)
    mysql> show status like 'Handler_read%';
    7 rows in set (0.03 sec)
    mysql>  pager
    Default pager wasn't set, using stdout.
    mysql> show status like 'Handler_read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 0     |
    | Handler_read_key      | 1     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 56414 |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+
    7 rows in set (0.02 sec)

    Handler_read_키 1회 추가 첫 번째 포지셔닝에 사용됨,Handlerread_다음 데이터 접근에 사용할 스캔 줄 수를 늘립니다.
    6. 드라이버에 의한 색인 액세스
    mysql> desc select * from z1 STRAIGHT_JOIN z10 force index(a_idx) on z1.a=z10.a;
    +----+-------------+-------+------------+------+---------------+-------+---------+-----------+-------+----------+-------------+
    | id | select_type | table | partitions | type | possible_keys | key   | key_len | ref       | rows  | filtered | Extra       |
    +----+-------------+-------+------------+------+---------------+-------+---------+-----------+-------+----------+-------------+
    |  1 | SIMPLE      | z1    | NULL       | ALL  | a             | NULL  | NULL    | NULL      | 56650 |   100.00 | Using where |
    |  1 | SIMPLE      | z10   | NULL       | ref  | a_idx         | a_idx | 5       | test.z1.a |    10 |   100.00 | NULL        |
    +----+-------------+-------+------------+------+---------------+-------+---------+-----------+-------+----------+-------------+
    2 rows in set, 1 warning (0.01 sec)
    
    mysql> flush status;
    Query OK, 0 rows affected (0.47 sec)
    
    mysql> pager cat >> /dev/null
    PAGER set to 'cat >> /dev/null'
    mysql>  select * from z1 STRAIGHT_JOIN z10 force index(a_idx) on z1.a=z10.a;
    112828 rows in set (1 min 21.21 sec)
    
    mysql> pager
    Default pager wasn't set, using stdout.
    mysql>  show status like 'Handler_read%';
    +-----------------------+--------+
    | Variable_name         | Value  |
    +-----------------------+--------+
    | Handler_read_first    | 1      |
    | Handler_read_key      | 56416  |
    | Handler_read_last     | 0      |
    | Handler_read_next     | 112828 |
    | Handler_read_prev     | 0      |
    | Handler_read_rnd      | 0      |
    | Handler_read_rnd_next | 56416  |
    +-----------------------+--------+
    7 rows in set (0.00 sec)

    Handler_read_first는 드라이버 z1 전체 테이블 스캔 포지셔닝의 시작으로 한 번 추가합니다. 다음handlerread_rnd_next 모든 기록을 스캔합니다. 매번 스캔할 때마다 z10표에서 색인 aidx 포지셔닝 Handlerread_키 1회 추가, 다음 색인 a 진행idx 데이터 검색Handlerread_next가 스캔하는 줄 수로 증가했습니다.
    7. 인덱스는 정렬의 정방향과 반전을 피한다
    mysql>  flush status;
    Query OK, 0 rows affected (0.05 sec)
    
    mysql> pager cat >> /dev/null
    PAGER set to 'cat >> /dev/null'
    mysql> select * from z1 force index(a) order by a;
    56415 rows in set (27.39 sec)
    
    mysql> pager
    Default pager wasn't set, using stdout.
    mysql> show status like 'Handler_read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 1     |
    | Handler_read_key      | 1     |
    | Handler_read_last     | 0     |
    | Handler_read_next     | 56415 |
    | Handler_read_prev     | 0     |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+
    7 rows in set (0.01 sec)
    
    mysql> flush status;
    Query OK, 0 rows affected (0.10 sec)
    
    mysql> desc  select * from z1 force index(a) order by a desc;
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-------+
    | id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows  | filtered | Extra |
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-------+
    |  1 | SIMPLE      | z1    | NULL       | index | NULL          | a    | 5       | NULL | 56650 |   100.00 | NULL  |
    +----+-------------+-------+------------+-------+---------------+------+---------+------+-------+----------+-------+
    1 row in set, 1 warning (0.00 sec)
    
    mysql> pager cat >> /dev/null
    PAGER set to 'cat >> /dev/null'
    mysql>  select * from z1 force index(a) order by a desc;
    56415 rows in set (24.94 sec)
    
    mysql> pager
    Default pager wasn't set, using stdout.
    mysql>  show status like 'Handler_read%';
    +-----------------------+-------+
    | Variable_name         | Value |
    +-----------------------+-------+
    | Handler_read_first    | 0     |
    | Handler_read_key      | 1     |
    | Handler_read_last     | 1     |
    | Handler_read_next     | 0     |
    | Handler_read_prev     | 56415 |
    | Handler_read_rnd      | 0     |
    | Handler_read_rnd_next | 0     |
    +-----------------------+-------+
    7 rows in set (0.01 sec)

    너무 설명하지 않아도 Handlerread_last 및 Handlerread_prev의 용도.
    4. 총결산
  • Handler_read_rnd_next는 보통 전체 테이블 스캔을 대표합니다.
  • Handler_read_first는 보통 전체 테이블이나 전체 색인 스캔을 대표합니다.
  • Handler_read_next는 일반적으로 인덱스나 전체 인덱스 스캔을 합리적으로 사용한 것을 의미합니다.
  • Handler_read_키는 전체 테이블의 전체 인덱스든 정확하게 사용하는 인덱스든 사실상 증가합니다. 단지 한 번의 인덱스 포지셔닝일 뿐입니다.
  • Innodb에서 전체 테이블 스캔도 메인 키의 전체 인덱스 스캔입니다.

  • 순서대로 방문한 기록은 실제로ha 를 호출합니다innobase::general_fetch 함수, 다른 기능 innodbthread_concurrency 매개 변수의 기능은 그 안에서 실현됩니다. 다음에 다시 이야기합시다.
    5. 참고 창고 프레임
        
    
    mysql> desc select * from z1 ;
    +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
    | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
    +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
    | 1 | SIMPLE | z1 | NULL | ALL | NULL | NULL | NULL | NULL | 56650 | 100.00 | NULL |
    +----+-------------+-------+------------+------+---------------+------+---------+------+-------+----------+-------+
    1 row in set, 1 warning (0.00 sec)
    
       :
    #0 row_search_mvcc (buf=0x7fff2ccc9380 "\377", mode=PAGE_CUR_G, prebuilt=0x7fff2cd4bb40, match_mode=0, direction=0)
    at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/row/row0sel.cc:4479
    #1 0x00000000019b3051 in ha_innobase::index_read (this=0x7fff2cd32480, buf=0x7fff2ccc9380 "\377", key_ptr=0x0, key_len=0, find_flag=HA_READ_AFTER_KEY)
    at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9104
    #2 0x00000000019b4374 in ha_innobase::index_first (this=0x7fff2cd32480, buf=0x7fff2ccc9380 "\377")
    at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9551
    #3 0x00000000019b462c in ha_innobase::rnd_next (this=0x7fff2cd32480, buf=0x7fff2ccc9380 "\377")
    at /root/mysql5.7.14/percona-server-5.7.14-7/storage/innobase/handler/ha_innodb.cc:9656
    #4 0x0000000000f66fa2 in handler::ha_rnd_next (this=0x7fff2cd32480, buf=0x7fff2ccc9380 "\377") at /root/mysql5.7.14/percona-server-5.7.14-7/sql/handler.cc:3099
    #5 0x00000000014c61b6 in rr_sequential (info=0x7fff2c0026a0) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/records.cc:520
    #6 0x000000000155f2a4 in join_init_read_record (tab=0x7fff2c002650) at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:2481
    #7 0x000000000155c381 in sub_select (join=0x7fff2c001f70, qep_tab=0x7fff2c002650, end_of_records=false)
    at /root/mysql5.7.14/percona-server-5.7.14-7/sql/sql_executor.cc:1271
    #8 0x000000000155bd

    마지막으로 고붕의 칼럼인'마이SQL 주종원리를 깊이 이해하다 32강'을 추천하며, 마이SQL 주종원리를 철저히 이해하고자 하는 친구들을 놓치지 말아야 한다.

    좋은 웹페이지 즐겨찾기