인덱스에 대한 자세한 설명은 인덱스 에서 다루었습니다. 이번에는 인덱스를 직접 적용해서 쿼리 성능을 높여보겠습니다.
0️⃣ 인덱스 사전 지식
적합한 인덱스가 있는 경우, MySQL은 옵티마이저가 쿼리 실행계획을 통해 자동으로 인덱스를 선택합니다.
인덱스는 항상 정렬되어 있는 상태를 유지하기 때문에 해당 컬럼을 이용해 검색할 경우 속도가 향상됩니다.
✔️ 성능 향상
- 검색
- where절에서 칼럼을 이용한 조건 검색이 빨라집니다. where절에서 사용하지 않을 시 인덱스를 타지 않고 full-scan을 하게 됩니다.
- 조인
- join절에서 인덱스를 활용하면 매칭되는 행을 효율적으로 찾을 수 있습니다.
- 정렬/그룹화
- order by나 group by절에서 인덱스를 사용하면 빨라집니다.
- 범위 검색
- 날짜나 숫자 범위 등에서 인덱스를 사용할 때 효과적입니다.
✔️ 고려 조건
- 카디널리티 수치
- 여러 컬럼을 고려해야 한다면, 카디널리티 수치(고유한 값을 가지는 정도)가 높은 컬럼을 사용합니다. (예) 이메일, 주민번호, 고유이름
- UPDATE, INSERT, DELETE 빈도
- UPDATE, INSERT, DELETE 작업의 빈도가 낮은 컬럼을 사용합니다.
- 인덱스는 위의 작업이 일어날 때마다 자료구조를 정렬하기 때문에 성능이 떨어지게 됩니다.
1️⃣인덱스 생성
조회 쿼리
select
b1_0.*,
c1_0.*,
from
board b1_0
join
category c1_0 on c1_0.category_id=b1_0.category_id
where
b1_0.subject like '%8%' // where 조건은 동적으로 변경
order by
b1_0.created_at desc
limit 0,10;
인덱스 후보 컬럼
- join절에서 사용되는 category
- where절에서 사용되는 subject
- order by에서 사용되는 created_at
카디널리티 확인
SELECT
CONCAT(ROUND(COUNT(DISTINCT board_id) / COUNT(*) * 100, 2), '%') AS id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT created_at) / COUNT(*) * 100, 2), '%') AS created_at_cardinality,
CONCAT(ROUND(COUNT(DISTINCT updated_at) / COUNT(*) * 100, 2), '%') AS updated_at_cardinality,
CONCAT(ROUND(COUNT(DISTINCT content) / COUNT(*) * 100, 2), '%') AS content_cardinality,
CONCAT(ROUND(COUNT(DISTINCT deleted) / COUNT(*) * 100, 2), '%') AS deleted_cardinality,
CONCAT(ROUND(COUNT(DISTINCT read_count) / COUNT(*) * 100, 2), '%') AS read_count_cardinality,
CONCAT(ROUND(COUNT(DISTINCT subject) / COUNT(*) * 100, 2), '%') AS subject_cardinality,
CONCAT(ROUND(COUNT(DISTINCT tags) / COUNT(*) * 100, 2), '%') AS tags_cardinality,
CONCAT(ROUND(COUNT(DISTINCT category_id) / COUNT(*) * 100, 2), '%') AS category_id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT writer_id) / COUNT(*) * 100, 2), '%') AS writer_id_cardinality
FROM board;
결론
- DML 이 잦은 subject 제외
- category_id에 비해 카디널리티가 높고 DML이 잦지 않은 created_at으로 인덱스 생성
2️⃣실행계획으로 확인하기
explain 명령어를 통해 쿼리 실행계획을 확인할 수 있습니다.
**explain**
select
b1_0.*,
c1_0.*,
from
board b1_0
join
category c1_0 on c1_0.category_id=b1_0.category_id
where
b1_0.subject like '%8%' // where 조건은 동적으로 변경
order by
b1_0.created_at desc
limit 0,10;
✔️비교
실행계획 결과에서 key와 extra를 확인하면, 현재 쿼리가 어떤 인덱스를 사용하는 지 확인가능합니다.
인덱스 미사용시

filesort 사용
인덱스 사용시

created_at index를 사용
3️⃣ MySQL Profiling으로 개선된 정도 확인하기
MySQL은 Profiling 기능을 통해 쿼리가 처리되는 동안 단계별 작업 시간을 확인할 수 있습니다.
show profile for query {쿼리아이디}
결과 확인하기
0.002 → 0.0005로 쿼리 속도가 개선되었습니다.
'프로젝트 Project' 카테고리의 다른 글
static 내부 클래스로 DTO 관리하기 (0) | 2024.08.06 |
---|---|
Spring Security + JWT로 로그인 구현하기 (1) (0) | 2024.08.05 |
멀티 모듈로 프로젝트 구성하기 (0) | 2024.08.05 |
Ehcache로 캐싱하기 (0) | 2024.08.02 |
쿼리 튜닝하기 (0) | 2024.08.02 |
인덱스에 대한 자세한 설명은 인덱스 에서 다루었습니다. 이번에는 인덱스를 직접 적용해서 쿼리 성능을 높여보겠습니다.
0️⃣ 인덱스 사전 지식
적합한 인덱스가 있는 경우, MySQL은 옵티마이저가 쿼리 실행계획을 통해 자동으로 인덱스를 선택합니다.
인덱스는 항상 정렬되어 있는 상태를 유지하기 때문에 해당 컬럼을 이용해 검색할 경우 속도가 향상됩니다.
✔️ 성능 향상
- 검색
- where절에서 칼럼을 이용한 조건 검색이 빨라집니다. where절에서 사용하지 않을 시 인덱스를 타지 않고 full-scan을 하게 됩니다.
- 조인
- join절에서 인덱스를 활용하면 매칭되는 행을 효율적으로 찾을 수 있습니다.
- 정렬/그룹화
- order by나 group by절에서 인덱스를 사용하면 빨라집니다.
- 범위 검색
- 날짜나 숫자 범위 등에서 인덱스를 사용할 때 효과적입니다.
✔️ 고려 조건
- 카디널리티 수치
- 여러 컬럼을 고려해야 한다면, 카디널리티 수치(고유한 값을 가지는 정도)가 높은 컬럼을 사용합니다. (예) 이메일, 주민번호, 고유이름
- UPDATE, INSERT, DELETE 빈도
- UPDATE, INSERT, DELETE 작업의 빈도가 낮은 컬럼을 사용합니다.
- 인덱스는 위의 작업이 일어날 때마다 자료구조를 정렬하기 때문에 성능이 떨어지게 됩니다.
1️⃣인덱스 생성
조회 쿼리
select
b1_0.*,
c1_0.*,
from
board b1_0
join
category c1_0 on c1_0.category_id=b1_0.category_id
where
b1_0.subject like '%8%' // where 조건은 동적으로 변경
order by
b1_0.created_at desc
limit 0,10;
인덱스 후보 컬럼
- join절에서 사용되는 category
- where절에서 사용되는 subject
- order by에서 사용되는 created_at
카디널리티 확인
SELECT
CONCAT(ROUND(COUNT(DISTINCT board_id) / COUNT(*) * 100, 2), '%') AS id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT created_at) / COUNT(*) * 100, 2), '%') AS created_at_cardinality,
CONCAT(ROUND(COUNT(DISTINCT updated_at) / COUNT(*) * 100, 2), '%') AS updated_at_cardinality,
CONCAT(ROUND(COUNT(DISTINCT content) / COUNT(*) * 100, 2), '%') AS content_cardinality,
CONCAT(ROUND(COUNT(DISTINCT deleted) / COUNT(*) * 100, 2), '%') AS deleted_cardinality,
CONCAT(ROUND(COUNT(DISTINCT read_count) / COUNT(*) * 100, 2), '%') AS read_count_cardinality,
CONCAT(ROUND(COUNT(DISTINCT subject) / COUNT(*) * 100, 2), '%') AS subject_cardinality,
CONCAT(ROUND(COUNT(DISTINCT tags) / COUNT(*) * 100, 2), '%') AS tags_cardinality,
CONCAT(ROUND(COUNT(DISTINCT category_id) / COUNT(*) * 100, 2), '%') AS category_id_cardinality,
CONCAT(ROUND(COUNT(DISTINCT writer_id) / COUNT(*) * 100, 2), '%') AS writer_id_cardinality
FROM board;
결론
- DML 이 잦은 subject 제외
- category_id에 비해 카디널리티가 높고 DML이 잦지 않은 created_at으로 인덱스 생성
2️⃣실행계획으로 확인하기
explain 명령어를 통해 쿼리 실행계획을 확인할 수 있습니다.
**explain**
select
b1_0.*,
c1_0.*,
from
board b1_0
join
category c1_0 on c1_0.category_id=b1_0.category_id
where
b1_0.subject like '%8%' // where 조건은 동적으로 변경
order by
b1_0.created_at desc
limit 0,10;
✔️비교
실행계획 결과에서 key와 extra를 확인하면, 현재 쿼리가 어떤 인덱스를 사용하는 지 확인가능합니다.
인덱스 미사용시

filesort 사용
인덱스 사용시

created_at index를 사용
3️⃣ MySQL Profiling으로 개선된 정도 확인하기
MySQL은 Profiling 기능을 통해 쿼리가 처리되는 동안 단계별 작업 시간을 확인할 수 있습니다.
show profile for query {쿼리아이디}
결과 확인하기
0.002 → 0.0005로 쿼리 속도가 개선되었습니다.
'프로젝트 Project' 카테고리의 다른 글
static 내부 클래스로 DTO 관리하기 (0) | 2024.08.06 |
---|---|
Spring Security + JWT로 로그인 구현하기 (1) (0) | 2024.08.05 |
멀티 모듈로 프로젝트 구성하기 (0) | 2024.08.05 |
Ehcache로 캐싱하기 (0) | 2024.08.02 |
쿼리 튜닝하기 (0) | 2024.08.02 |