SQL 쿼리 최적화 원리 분석 (900W + 데이터,이 300ms로 17S에서)

금융 플로우 테이블 쿼리 소요가없는 서브 라이브러리 서브 테이블 데이터의 현재 양 9,555,695이었다 매김 질의 이전 최적화 제한하는 사용 16 938 MS S  (실행 107 MS : (16)이 인출 831 MS를들)에 따라, 소비하기 방식의 SQL를 조정 한 후 (347 개)를 MS  (: MS (163)를 인출 할 수있다 : 실행 MS (184));

작업 :  하위 쿼리, 하위 쿼리에만 기본 검색 키 ID, 그리고 다른 속성 필드를 결정하기 위해 쿼리의 관련 하위 쿼리의 기본 키를 사용하여에 질의;

원리 :  테이블 작업에 다시 감소;

-- 优化前SQL
SELECT  各种字段
FROM`table_name`
WHERE 各种条件
LIMIT 0,10;
-- 优化后SQL
SELECT  各种字段
FROM `table_name` main_tale
RIGHT JOIN
(
SELECT  子查询只查主键
FROM `table_name`
WHERE 各种条件
LIMIT 0,10;
) temp_table ON temp_table.主键 = main_table.主键

찾을 원리 분석 : 제한하는 이유 MySQL의 성능에 영향을 미칠까요?

I. 서론

첫째, MySQL 버전 설명 :

mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.17    |
+-----------+
1 row in set (0.00 sec)

테이블 구조 :

mysql> desc test;
+--------+---------------------+------+-----+---------+----------------+
| Field  | Type                | Null | Key | Default | Extra          |
+--------+---------------------+------+-----+---------+----------------+
| id     | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| val    | int(10) unsigned    | NO   | MUL | 0       |                |
| source | int(10) unsigned    | NO   |     | 0       |                |
+--------+---------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

기본 키의 증가 이드, 발 고유하지 않은 인덱스입니다.

데이터 붓고 많은 양의 500 만 총 :

mysql> select count(*) from test;
+----------+
| count(*) |
+----------+
|  5242882 |
+----------+
1 row in set (4.25 sec)

우리는 오프셋 제한이 큰 행을 상쇄 할 때 효율성이있을 것이라는 점을 알고있다 :

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+
| 3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (15.98 sec)

동일한 목적을 달성하기 위해, 우리는 일반적으로 다음 문을 다시 작성합니다 :

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.38 sec)

시간의 차이는 분명하다.

이유는 위의 결과가? 우리는 시험에서 선택 * 보는 곳 발 = 4 제한 300000,5, 쿼리 프로세스 :

인덱스 리프 노드 데이터를 조회합니다. 인덱스 필드 값의 모든 쿼리는 리프 노드 클러스터에 마스터 키에 따라 필요합니다.

이 그림은 다음과 유사한 :

필요, 위처럼, 아이 노드 300005 번, 300005 번 쿼리 데이터는 최종 결과 지난 5를 제거하기 전에 30 만 필터링, 인덱스를 클러스터. 쿼리에 300000 랜덤 I / O 데이터 세트의 결과에 표시되지 않습니다 반면, 데이터 쿼리 클러스터 된 인덱스에 랜덤 I / O의 많은 비용 MYSQL.

분명히 누군가가 묻습니다 : 노드에 인덱스 리프 노드를 따라 먼저 확인하세요 마지막 다섯 개 필요하고 실제 데이터를 쿼리하는 클러스터 된 인덱스를 이동하는 이유 인덱스의 사용의 시작입니다. 그래서 5 랜덤 I / O, 프로세스는 다음 그림에 유사하다 :

사실, 나는이 질문을합니다.

확인

여기에서 우리는 위의 추론을 확인하기 위해 실제 동작을 보면 :

확인하기 위해 select * from test where val=4 limit 300000,5인덱스에 스캔 데이터 노드 것은 300 005 300 005 클러스터형 인덱스 노드, 우리는 SQL의 노드의 인덱스 노드를 통해 MySQL의 쿼리 데이터의 수를 계산하는 방법은 없습니다 알아야합니다. 내가 먼저 조건을 충족하는 불행하게도, * 변수를 일련의 Handler_read_을하지했습니다.

나는 단지 간접적 인 방법을 통해 확인할 수 있습니다 :

수영장이 버퍼 이노 디비. 어떤 데이터와 인덱스 페이지를 포함하여 최근에 방문한 데이터 페이지가 포함되어 있습니다. 우리는이 SQL을 실행해야합니다 그래서, 버퍼 풀 데이터 페이지의 수를 비교합니다. 결과는 실행 전망이다 select * from test a inner join (select id from test where val=4 limit 300000,5); 버퍼 풀 데이터 페이지 수 후에하는 것이 훨씬 덜보다 select * from test where val=4 limit 300000,5;이전 SQL 대응의 수를 다섯 번 데이터 액세스 페이지, 다음 SQL 데이터 액세스 페이지 300005 번 때문이다.

select * from test where val=4 limit 300000,5
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'group by index_name;
Empty set (0.04 sec)

볼 수 있듯이, 테스트 테이블에는 버퍼 풀 데이터 페이지가 없습니다.

mysql> select * from test where val=4 limit 300000,5;
+---------+-----+--------+
| id      | val | source |
+---------+-----+--------+|
3327622 |   4 |      4 |
| 3327632 |   4 |      4 |
| 3327642 |   4 |      4 |
| 3327652 |   4 |      4 |
| 3327662 |   4 |      4 |
+---------+-----+--------+
5 rows in set (26.19 sec)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |     4098 |
| val        |      208 |
+------------+----------+2 rows in set (0.04 sec)

알 수있는 바와 같이, 테스트 테이블이 경우 버퍼 풀에 데이터 페이지 4098, 페이지 인덱스 (208)를 갖는다.

select * from test a inner join (select id from test where val=4 limit 300000,5) ;마지막 테스트의 효과를 방지하기 위해 버퍼 풀, 다시 시작 MySQL의 빈해야합니다.

mysqladmin shutdown
/usr/local/bin/mysqld_safe &
mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'group by index_name;

Empty set (0.03 sec)

SQL 실행 :

mysql> select * from test a inner join (select id from test where val=4 limit 300000,5) b on a.id=b.id;
+---------+-----+--------+---------+
| id      | val | source | id      |
+---------+-----+--------+---------+
| 3327622 |   4 |      4 | 3327622 |
| 3327632 |   4 |      4 | 3327632 |
| 3327642 |   4 |      4 | 3327642 |
| 3327652 |   4 |      4 | 3327652 |
| 3327662 |   4 |      4 | 3327662 |
+---------+-----+--------+---------+
5 rows in set (0.09 sec)

mysql> select index_name,count(*) from information_schema.INNODB_BUFFER_PAGE where INDEX_NAME in('val','primary') and TABLE_NAME like'%test%'group by index_name;
+------------+----------+
| index_name | count(*) |
+------------+----------+
| PRIMARY    |        5 |
| val        |      390 |
+------------+----------+
2 rows in set (0.03 sec)

데이터의 다섯 페이지가 버퍼 풀에로드 된 두 번째 SQL하면서, 버퍼 풀 데이터의 4098 개 페이지에로드 된 첫 번째 SQL : 우리는 둘 사이의 차이를 볼 명확하게 볼 수 있습니다. 우리의 예측과 일치합니다. 또한 첫 번째 SQL이 느린 이유 확인 : 쓸모없는 데이터 라인 (30)의 많은 양을 읽을 수 있지만 결국 폐기. 그리고이 문제가 발생합니다 핫스팟의 많은 페이지 버퍼 풀에 데이터의 매우 높은 로딩 아니라, 오염 버퍼 풀 버퍼 풀의 풋 프린트를 일으킬 수 있습니다. 문제가 발생

데이터베이스가 열려있는 동안 데이터 버퍼 풀을 폐쇄 및로드 데이터 백업 버퍼 풀 디스크에 밖으로 다시 시작할 때마다, 우리 가까이 innodb_buffer_pool_dump_at_shutdown 및 innodb_buffer_pool_load_at_startup 할 필요가 있음을 보장하기 위해 버퍼 풀을 비우려면,이 두 옵션은 데이터베이스 덤프를 제어 할 수 있습니다.

참고 자료

1.https : //explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/

2.https : //dev.mysql.com/doc/refman/5.7/en/innodb-information-schema-buffer-pool-tables.html

게시 50 개 원래 기사 · 원 찬양 1706 · 조회수 2,220,000 +

추천

출처blog.csdn.net/zl1zl2zl3/article/details/105325510