[테스트 환경]
OS : CentOS7
DB : MariaDB 10.1.12
멀티 레인지 리드란 ?
Multi Range Read(MRR)는 Random I/O를 Sequential I/O로 처리할 수 있도록 도와주는 기능으로, Non-Clustered Index를 통해 Range Scan을 하는 경우, 바로 데이터를 조회하지 않고 어느 정도 rowid(primary key) 값들을 Random 버퍼(mrr_buffer_size)에 채운 다음, 버퍼 내용을 정렬하여 최대한 rowid(primary key) 순서대로 데이터를 접근할 수 있도록 해주는 것이다.
1. rowid 기준 정렬 (Rowid-odered scan)
1-1 조회 되는 데이터의 양이 적다면 옵티마이저는 멀티 레인지 스캔을 하지 않는가?
/*파라미터 설정*/ --멀티 레인지 스캔 최적화를 하기 위해서 아래와 같은 파라미터를 활성화(on) 한다. SET optimizer_switch='mrr=on'; SET optimizer_switch='mrrsort_keys=on'; /*인덱스 생성*/ create index ix_firstname on employees(first_name); create index salary_idx01 on salaries(salary); /*first_name이 A로 시작하면서 연봉이 30000 ~ 100000인직원을 조회 하시오*/ -- 건수 : 201417 analyze format=json -- EXPLAIN SELECT A.emp_no, A.first_name, A.last_name, B.salary, A.gender, A.hire_date FROM employees A INNER JOIN salaries B ON(A.emp_no = B.emp_no) WHERE A.first_name LIKE 'A%' AND B.salary BETWEEN 30000 AND 100000; ; /*mrr 사용*/ { "query_block": { "select_id": 1, "r_loops": 1, "r_total_time_ms": 343.66, "table": { "table_name": "A", "access_type": "range", "possible_keys": ["PRIMARY", "ix_firstname"], "key": "ix_firstname", "key_length": "44", "used_key_parts": ["first_name"], "r_loops": 1, "rows": 43234, "r_rows": 22039, "r_total_time_ms": 45.616, "filtered": 100, "r_filtered": 100, "index_condition": "(A.first_name like 'A%')", "mrr_type": "Rowid-ordered scan" }, "table": { "table_name": "B", "access_type": "ref", "possible_keys": ["PRIMARY", "emp_no_salary_idx01", "salary_idx01"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["emp_no"], "ref": ["employees.A.emp_no"], "r_loops": 22039, "rows": 4, "r_rows": 9.4711, "r_total_time_ms": 178.23, "filtered": 100, "r_filtered": 96.495, "attached_condition": "(B.salary between 30000 and 100000)" } } } /*mrr 미사용*/ /*수행시간 1.6 ~ 1.8초*/ /*파라미터 설정*/ SET optimizer_switch='mrr=off'; SET optimizer_switch='mrr_sort_keys=off'; { "query_block": { "select_id": 1, "r_loops": 1, "r_total_time_ms": 333.9, "table": { "table_name": "A", "access_type": "range", "possible_keys": ["PRIMARY", "ix_firstname"], "key": "ix_firstname", "key_length": "44", "used_key_parts": ["first_name"], "r_loops": 1, "rows": 43234, "r_rows": 22039, "r_total_time_ms": 71.33, "filtered": 100, "r_filtered": 100, "index_condition": "(A.first_name like 'A%')" }, "table": { "table_name": "B", "access_type": "ref", "possible_keys": ["PRIMARY", "emp_no_salary_idx01", "salary_idx01"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["emp_no"], "ref": ["employees.A.emp_no"], "r_loops": 22039, "rows": 4, "r_rows": 9.4711, "r_total_time_ms": 191.01, "filtered": 100, "r_filtered": 96.495, "attached_condition": "(B.salary between 30000 and 100000)" } } }
/*데이터 건수가 적어도 멀티 레이니지 리드 최적화를 사용 한다.*/ -- 데이터 건수와 상관 없이 range 조건일 경우 mrr을 상요하고, 등치조건 일때는 사용하지 않는다. -- 데이터 건수 : 1979건 analyze format=json -- EXPLAIN SELECT A.emp_no, A.first_name, A.last_name, B.salary, A.gender, A.hire_date FROM employees A INNER JOIN salaries B ON(A.emp_no = B.emp_no) WHERE A.first_name LIKE 'AE%' AND B.salary BETWEEN 30000 AND 100000; ; { "query_block": { "select_id": 1, "r_loops": 1, "r_total_time_ms": 5.0293, "table": { "table_name": "A", "access_type": "range", "possible_keys": ["PRIMARY", "ix_firstname"], "key": "ix_firstname", "key_length": "44", "used_key_parts": ["first_name"], "r_loops": 1, "rows": 207, "r_rows": 207, "r_total_time_ms": 0.5652, "filtered": 100, "r_filtered": 100, "index_condition": "(A.first_name like 'AE%')", "mrr_type": "Rowid-ordered scan" }, "table": { "table_name": "B", "access_type": "ref", "possible_keys": ["PRIMARY", "emp_no_salary_idx01", "salary_idx01"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["emp_no"], "ref": ["employees.A.emp_no"], "r_loops": 207, "rows": 4, "r_rows": 10.01, "r_total_time_ms": 3.1933, "filtered": 100, "r_filtered": 95.512, "attached_condition": "(B.salary between 30000 and 100000)" } } }2.key 기준 정렬 (Key-odered scan)
/*파라미터 설정*/ -- key기준 정렬 최적화를 하기 위해서 아래와 같은 파라미터를 활성화(on) 한다 SET optimizer_switch='join_cache_hashed=on'; SET optimizer_switch='join_cache_bka=on'; /*레벨의 상세 설명은 리얼 마리아DB 309쪽 참조*/ SET join_cache_level =8; analyze format=json -- EXPLAIN SELECT A.emp_no, A.first_name, A.last_name, B.salary, A.gender, A.hire_date FROM employees A INNER JOIN salaries B ON(A.emp_no = B.emp_no) WHERE A.first_name LIKE 'A%' AND B.salary BETWEEN 30000 AND 100000; { "query_block": { "select_id": 1, "r_loops": 1, "r_total_time_ms": 349.44, "table": { "table_name": "A", "access_type": "range", "possible_keys": ["PRIMARY", "ix_firstname"], "key": "ix_firstname", "key_length": "44", "used_key_parts": ["first_name"], "r_loops": 1, "rows": 43234, "r_rows": 22039, "r_total_time_ms": 35.722, "filtered": 100, "r_filtered": 100, "index_condition": "(A.first_name like 'A%')", "mrr_type": "Rowid-ordered scan" }, "block-nl-join": { "table": { "table_name": "B", "access_type": "ref", "possible_keys": ["PRIMARY", "emp_no_salary_idx01", "salary_idx01"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["emp_no"], "ref": ["employees.A.emp_no"], "r_loops": 0, "rows": 4, "r_rows": null, "r_total_time_ms": 154.09, "filtered": 100, "r_filtered": null }, "buffer_type": "flat", "buffer_size": "2048Kb", "join_type": "BKAH", "mrr_type": "Key-ordered scan", "attached_condition": "(B.salary between 30000 and 100000)", "r_filtered": 96.495 } } } /*mrr 미사용*/ SET optimizer_switch='join_cache_hashed=off'; SET optimizer_switch='join_cache_bka=off'; /*레벨의 상세 설명은 리얼 마리아DB 309쪽 참조*/ SET join_cache_level =2; { "query_block": { "select_id": 1, "r_loops": 1, "r_total_time_ms": 334.7, "table": { "table_name": "A", "access_type": "range", "possible_keys": ["PRIMARY", "ix_firstname"], "key": "ix_firstname", "key_length": "44", "used_key_parts": ["first_name"], "r_loops": 1, "rows": 43234, "r_rows": 22039, "r_total_time_ms": 44.15, "filtered": 100, "r_filtered": 100, "index_condition": "(A.first_name like 'A%')", "mrr_type": "Rowid-ordered scan" }, "table": { "table_name": "B", "access_type": "ref", "possible_keys": ["PRIMARY", "emp_no_salary_idx01", "salary_idx01"], "key": "PRIMARY", "key_length": "4", "used_key_parts": ["emp_no"], "ref": ["employees.A.emp_no"], "r_loops": 22039, "rows": 4, "r_rows": 9.4711, "r_total_time_ms": 170.41, "filtered": 100, "r_filtered": 96.495, "attached_condition": "(B.salary between 30000 and 100000)" } } }3.멀티 레인지 상태 변수 확인 (Key-odered scan)
/*멀티 레인지 리드 최적화 사용 횟수*/ show global status like '%Handler_mrr_init%'; /*멀티 레인지 리드 최적화에서 key_ordered scan이 몇번이나 mrr 버퍼가 다시 채워 졌는지 확인*/ show global status like '%Handler_mrr_key_refills%'; /*멀티 레인지 리드 최적화에서 rowid_ordered scan이 몇번이나 mrr 버퍼가 다시 채워 졌는지 확인*/ show global status like '%Handler_mrr_rowid_refills%'; MariaDB [mysql]> show global status like '%Handler_mrr%' \G; *************************** 1. row *************************** Variable_name: Handler_mrr_init Value: 144 *************************** 2. row *************************** Variable_name: Handler_mrr_key_refills Value: 0 *************************** 3. row *************************** Variable_name: Handler_mrr_rowid_refills Value: 12 3 rows in set (0.00 sec)
'MariaDB > SQL Tuning' 카테고리의 다른 글
[MariaDB][성능] 서브쿼리 최적화 (0) | 2016.09.25 |
---|---|
[MariaDB][성능] LEFT OUTER JOIN과 스칼라 서브쿼리 실행계획 비교2 (0) | 2016.08.28 |
[MariaDB][성능] LEFT OUTER JOIN과 스칼라 서브쿼리 실행계획 비교1 (0) | 2016.08.24 |
[MariaDB][성능] 스칼라 서브쿼리 실행계획2 (0) | 2016.08.17 |
[MariaDB][성능]인라인뷰 처리 방식 분석 (0) | 2016.08.17 |