Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

7 - 포케 #41

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions real-my-sql/fromitive/ch-09.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@

쿼리 실행계획 : `EXPLAIN [쿼리문]` 을 입력하면 확인가능
쿼리 최적화 정보 보기 : `information_schema.optimizer_trace 테이블 확인`

쿼리 실행 과정

1. 쿼리를 잘개 쪼개서 파싱
2. 파싱한 정보를 토대로 어떤 인덱스와 테이블을 읽을지 계획
3. 계획한 정보를 통해 스토리지 엔진에서 데이터를 읽는다

``` java
ParseTree tree = sqlParser.parse(sqlQuery);

// 연산 단순화..
// 테이블 조회 순서
// 인덱스 선택
// 임시 테이블 가공 여부 결정
ExecutionPlan plan = optimizer.read(tree);
```

옵티마이징 방식

1. 비용 기반 최적화(MySQL 채택)
- 여러 순서를 계산하여 비용응 최소화 하는 방향으로 최적화
2. 규칙 기반 최적화
- 옵티마이저가 정한 규칙대로 최적화

쿼리 동작 원리

테이블 풀 스캔 원리

1. 레코드 건수가 인덱스를 읽는 것 보다 작은 경우
2. ON에 인덱스를 활용할 수 있는 적절한 조건이 없는 경우
3. 레인지 스캔을 사용해도 비용상 별 차이 없는 경우

리드 어 헤드 : 데이터가 앞으로 더 필요해질 때 백그라운드에서 미리 읽어서 버퍼 풀에 저장하는 것. `innodb_read_ahead_threashold` 값을 통해 페이지 양을 설정할 수 있음

---

병렬 처리 원리
`innodb_parallel_read_threads` 한 개의 커리를 몇개의 쓰레드를 이용해서 처리할지 결정

9.2.3 Order By 처리 원리

1. 인덱스 이용
2. Filesort이용 (확인 방법은 extra 컬럼에 filesort 여부 체크) - 인덱스로 이용하지 않고 실행되었다면 filesort로 뜬다고 함

Order by 할 때 모든 정렬을 인덱스를 이용하도록 튜닝하기 어려운 이유
```
1. 정렬 기준이 많아서 요건별로 인덱스를 생성하는 것이 힘들때
2. GROUP BY DISTINCT처리결과를 정렬해야 하는 경우
3. 임시테이블의 결과를 다시 정렬해야 하는 경우
4. 랜덤하게 결과 레코드를 가져와야 하는 경우
```

9.2.3.1 소트 버퍼

정렬을 수행하기 위한 별도의 메모리 공간을 의미. 버퍼의 크기는 정렬해야 할 레코드의 수에 따라 가변적이지만 `sort_buffer_size` 시스템 변수를 이용해 최대 소트 버퍼의 크기를 제어할 수 있음.

**소트 버퍼의 크기보다 큰 레코드를 정렬해야 할 경우**

정렬해야할 레코드의 용량이 소트 버퍼를 초과하게 되면 디스크에 임시저장 한 다음 병합하는 형태로 처리함 이 병합 작업을 멀티 머지(Multi-merge)라고 한다 수행된 멀티 머지 횟수는 `sort_message_passes`에 누적되어 집계된다.

**소트 버퍼의 최적 조건**

256KB에서 8MB사이에서 최적의 성능을 보였다. 하지만 무조건 크다고 해서 처리속도가 빨라지진 않는다. 소트 버퍼는 각 클라이언트 세션 마다 할당이 되기 때문에 크게 설정할 때 클라이언트 접속량이 많아지면 메모리 부족 현상을 겪을 가능성이 있다.

메모리 부족 현상을 겪으면 OOM-killer가 MySQL을 종료시킬 수 있기 때문에 소트 버퍼를 무조건 크게 증가시는 방법은 좋은 전략이 아니다.

**9.2.3.2 정렬 알고리즘**

**레코드 전체를 소트 버퍼에 담을 지 정렬 기준 칼럼만 소트 버퍼에 담을지**를 나눌 수 있다.

9.2.3.2.1 싱글 패스

select 대상이 되는 컬럼을 전부 담아서 정렬을 수행하는 방식

9.2.3.2.1 투 패스

정렬 대상 칼럼과 프라이머리 키 값만 소트 버퍼에 담아서 정렬을 수행 정렬을 수행한 후 추가로 필요한 컬럼 정보를 조회하여 쿼리 결과를 반환한다.

싱글 패스 vs 투 패스 : MySQL 8.0 는 일반적으로 싱글 패스 정렬 방식을 주로 사용함 하지만 아래의 조건에 따라 투 패스를 사용하기도 한다.

* 레코드의 크기가 `max_length_for_sort_data`시스템 변수에 설정된 값 보다 클 때
* BLOB 이나 TEXT 칼럼이 SELECT 대상에 포함 될 때

교훈 : 꼭 필요한 칼럼만 조회하는 것이 싱글 패스를 사용하기 때문에 더 효율적으로 가져오기 때문에 `*`는 지양하자.

9.2.3.3 정렬 처리 방법

Order by가 처리될 때 아래의 3가지 경우 중 하나로 정렬이 처리된다.

1. 인덱스를 사용한 정렬 : extra column에 아무 것도 안보임
2. 조인에서 드라이빙 테이블만 정렬 : Using filesort
3. 조인에서 조인 결과를 임시 테이블로 저장 후 정렬 : Using temporary; Using filesort

인덱스를 사용할 수 없을 경우 정렬 대상 레코드를 최소화 하기 위해 2가지 방법 중 하나를 사용한다.

1. 조인의 드라이빙 테이블만 정렬한 다음 조인 수행
2. 조인이 끝나고 일치하는 레코드를 모두 가져온 후 정렬 수행

9.2.3.3.1 인덱스를 이용한 정렬

인덱스가 B-Tree 키 값으로 로 저장되어있기 때문에 정렬이 된다.
조인이 걸린 쿼리를 실행할 경우 조인이 네스티드-루프 방식으로 실행되기 때문에 조인 때문에 드라이빙 테이블의 인덱스 읽기 순서가 흐트러지지 않는다.

9.2.3.3.2 조인의 드라이빙 테이블만 정렬

조인이 있는 테이블을 정렬할 때 드라이빙 테이블 만 order by 하면 성능을 개선시킬 수 있다.

```sql
select * from employees e, salaries s
where s.emp_no=e.emp_no and e.emp_no between 10002 and 100010 order by e.last_name;
```

실행 순서

1. e.emp_no 의 9건을 추출
2. last_name 정렬
3. 정렬된 결과를 순서대로 읽으면서 조인을 수행함

9.2.3.3.3 임시 테이블을 이용한 정렬

2개 이상의 테이블을 조인하면서 정렬해야 한다면 임시 테이블이 필요하다.

```sql
select * from employees e, salaries s
where s.emp_no=e.emp_no and e.emp_no between 10002 and 100010 order by s.salary; // 이부분만 바뀌었을 때 어떻게 정렬이 되나?
```

정렬 할 때 기준이 드리븐 테이블에 있기 때문에 어쩔 수 없이 조인한 테이블을 저장하기 위한 임시 테이블을 생성한 후 정렬이 진행된다.

9.2.3.3.4 정렬 처리 방법의 성능 비교

ORDER BY와 GROUP BY 작업을 WHERE 절을 만족하는 레코드를 LIMIT 건수 만큼만 가져와서 처리할 수 없다. 왜냐하면 조건을 만족하는 레코드를 모두 가져와서 정렬을 수행하거나 그루핑을 해야 비로소 LIMIT로 제한할 수 있기 때문이다.

이런 경우엔 스트리밍 방식 혹은 버퍼링 방식에 따라 처리 된다.

9.2.3.3.4.1 스트리밍 방식

조건이 만족할 때마다 레코드를 전달하는 방식

9.2.3.3.4.2 버퍼링 방식

정렬에 필요한 모든 레코드를 가져오고 한번에 처리하는 방식. 정렬 레코드가 많을 수록 사용자 응답이 느려진다.

9.2.4 GROUP BY 처리

인덱스를 활용하지 못하면 임시 테이블을 사용하여 처리한다.

9.2.4.1 인덱스 스캔을 이용하는 GROUP BY(타이트 인덱스 스캔)