일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 바탕화면 정리 자바
- spring security 설정
- CPU
- spring security
- 자바의정석
- 혼공얄코
- 다형성
- 멀티프로세싱
- userdetailsservice 설정
- 오블완
- 오버로딩
- SQL Mapper
- 로그인 핸들러 구현
- 입출력
- 달리기 경주 자바
- over()
- spring security 커스텀
- 프로그래머스
- 객체지향
- 오버라이딩
- 캡슐화
- 개인정보 수집 유효기간 자바
- authenticationprovider 설정
- 리눅스
- 멀티태스킹
- java
- 티스토리챌린지
- 쿠키
- 자바의 정석
- hackerrank
- Today
- Total
쉽게 쉽게
[MYSQL] 쿼리 속도개선 본문
1. 이슈
속도가 너무 오래 걸려 불편을 주는 Mysql 쿼리를 발견하여 이를 개선하고자 했다.
속도저하의 원인으로는 Join시 데이터가 많은 테이블의 값을 읽어오는데 오래 걸렸던 것으로 짐작된다.
이를 해결하고자 인덱스, 쿼리개선, DB 격리수준 변경 등 다양한 방법을 진행했다.
2. 쿼리
SELECT
A.name , B.code, C.text
FROM SmallTable A
INNER JOIN
MiddleTable B ON A.user_code = B.user_code
INNER JOIN
LargeTable C ON A.user_code = C.user_code
AND C.ROLE = (SELECT ROLE FROM LargeTable
WHERE user_code = A.user_code AND STATUS = '1')
WHERE A.user_code = (값);
예를 들어 데이터 개수가 A < B < C인 테이블들을 조인한다고 가정하자
이 쿼리는 C 테이블을 조인하면서 서브쿼리도 탐색해야 하기에 상당한 시간이 걸리게 된다.
이 쿼리에 대한 설명을 얻고자 한다면 EXPLAIN을 사용할 수 있다.
EXPLAIN SELECT
A.name , B.code, C.text
FROM SmallTable A
INNER JOIN
MiddleTable B ON A.user_code = B.user_code
INNER JOIN
LargeTable C ON A.user_code = C.user_code
AND C.ROLE = (SELECT ROLE FROM LargeTable
WHERE user_code = A.user_code AND STATUS = '1')
WHERE A.user_code = (값);
이런 식으로 JOIN이 어떤 식으로 이뤄지는지 확인할 수 있다.
이 중 유의있게 봐야 할 것은 type이다.
이 type이 ALL이라면 테이블의 처음부터 끝까지 조회하는 것이기에 데이터가 많을경우 오랜 시간이 걸리게 된다.
(필자는 이미 index를 사용한 상황이라 ALL타입은 나오지 않았다.)
또한 MYSQL 옵티마이저는 JOIN순서를 변경하기 때문에 사용자가 원하는 A -> B -> C 순서로 JOIN이 진행되지 않고
변경되어 진행됨을 알 수 있다.
이를 개선하기위해 먼저 첫 번째로 인덱스를 활용할 수 있다.
3. 인덱스 활용
어쩌면 쿼리의 속도를 개선하기위해 가장 먼저 해야 하는 단계라고 볼 수 있다.
인덱스는 아래처럼 생성할 수 있다.
CREATE [UNIQUE] INDEX 인덱스_이름 ON 테이블_이름 (열 이름)
-- SmallTable 인덱스 생성
CREATE INDEX USER_CODE_IDX ON SmallTable (user_code);
효율적인 INDEX를 만들기 위해서는 아래와 같은 컬럼에 인덱스를 걸면 좋다.
- WHERE 절에 자주 등장하는 컬럼
- ORDER BY 절에 자주 등장하는 컬럼
- JOIN이 자주 사용되는 열
위의 쿼리의 속도개선을 위해서는 JOIN절의 조건으로 사용되는 user_code들을 인덱스로 설정하는 것이 좋다.
추가적으로 서브쿼리의 속도도 개선시키려면 WHERE 조건에 사용되는 user_code와 STATUS를 묶어 인덱스를 설정하는 방법도 있다.
INDEX 장단점
장점
- 검색 속도가 상승
- 해당 쿼리의 부하가 줄어들어서, 결국 시스템 전체의 성능이 향상
단점
- 인덱스는 대략 테이블 크기의 10% 공간이 추가로 필요 (인덱스 페이지 때문)
- SELECT 가 아닌 데이터의 변경 작업 (INSERT, UPDATE, DELETE)이 자주 일어나면 오히려 성능에 악영향 (페이지 정렬 작업 때문)
4. 쿼리개선
두 번째 방법으로는 쿼리 자체를 개선하는 방법이 있다.
먼저는 테이블들의 조인순서를 올바르게 수정할 필요가 있다.
테이블의 정보를 담고있는 순서는 C < B < A이지만 JOIN순서는 A -> B -> C이다.
때문에 A가 가진 모든 데이터를 조회 -> B가 가진 모든 데이터 조회 -> ... 순으로 반복하게 된다.
이를 개선하여 처음부터 C 테이블을 위주로 조인하는 방법이 있다.
SELECT
A.name , B.code, C.text
FROM LargeTable C
INNER JOIN
SmallTable A ON A.user_code = C.user_code
AND C.ROLE = (SELECT ROLE FROM LargeTable
WHERE user_code = A.user_code AND STATUS = '1')
INNER JOIN
MiddleTable B ON A.user_code = B.user_code
WHERE A.user_code = (값);
그러나 위에 언급했듯이 MYSQL 옵티마이저가 JOIN 순서를 바꾸기 때문에 사용자가 의도한대로 쿼리가 진행되지 않을 수도 있다.
이럴 때는 STARIGHT JOIN을 사용할 수 있다.
SELECT
STRAIGHT_JOIN
A.name , B.code, C.text
FROM SmallTable A
INNER JOIN
MiddleTable B ON A.user_code = B.user_code
INNER JOIN
LargeTable C ON A.user_code = C.user_code
AND C.ROLE = (SELECT ROLE FROM LargeTable
WHERE user_code = A.user_code AND STATUS = '1')
WHERE A.user_code = (값);
이렇게 사용한다면 사용자가 의도한대로 JOIN순서가 A -> B -> C 로 작동하게 된다.
index를 사용하고 쿼리를 개선한 후에도 개선할 점이 있다면 MYSQL 자체의 설정을 변경해보는 방법이다.
5. MYSQL 격리 수준(isolation level) 변경
이는 획기적인 속도의 개선을 가져오진 않지만 고려해 볼 만 방법이다.
격리수준(isolation level)이란 트랜잭션끼리 얼마나 서로 고립되어 있는지를 나타내는 수준
즉, 한 트랜잭션이 다른 트랜잭션이 변경한 데이터에 대한 접근 강도를 의미한다.
레벨이 높아질수록 트랜잭션간 고립정도가 높아지며, 성능저하도 야기된다.
오라클 등 많은 곳에서는 격리 수준이 READ COMMITTED이지만 MYSQL에는 REPEATABLE READ가 기본 수준이다.
MYSQL의 격리수준이 더 높기 때문에 이를 READ COMMITTED으로 수정해 볼 수 있다.
https://marobiana.tistory.com/35
잘못된 내용이 있다면 지적부탁드립니다. 방문해주셔서 감사합니다. |
'DB > MYSQL와 MariaDB' 카테고리의 다른 글
[MariaDB]MariaDB에서 오라클 DB 연동 (0) | 2024.08.12 |
---|---|
[MariaDB] 이클립스 MariaDB와 연동하기 (0) | 2024.08.06 |
[MYSQL] 윈도우 함수 정리 (0) | 2024.07.08 |
[MYSQL] select문에 count 여러개 (0) | 2024.07.04 |
MariaDB 대소구분 해결 (0) | 2024.03.21 |