SlideShare a Scribd company logo
1 of 42
Download to read offline
SQL 쿼리 최적화 맛보기
Full Stack Programmer
SPOQA conference 2021 : Always Evolving!
Jhuni 박종훈
SPOQA conference 2021 : Always Evolving!
발표자 소개
• 스포카 도도 포인트 팀 3년 근무
• 풀 스택 프로그래머
• 클린한 인프라 및 아키텍쳐 설계, 최적화에 관심
• GitHub/Jhuni0123
Jhuni 박종훈
SPOQA conference 2021 : Always Evolving!
시작하기 전에...
• PostgreSQL 기준입니다.
(다른 DBMS에도 비슷한 개념이 있어요)
• 맨 뒤에 참고 자료 링크들이 있습니다.
• 궁금하신 것이 있다면 채팅으로 남겨주세요!
SPOQA conference 2021 : Always Evolving!
테이블과 인덱스 맛보기
테이블과 인덱스의 내부 구조
SPOQA conference 2021 : Always Evolving!
테이블
1
2
3
42
.
.
.
ID A B C D
(편의상 ctid = ID)
1
2
3
42
ctid
책의 쪽 번호
SPOQA conference 2021 : Always Evolving!
테이블의 PK
1
2
3
42
.
.
.
ID A B C D
Primary Key
(뒤에 인덱스 있어요)
SPOQA conference 2021 : Always Evolving!
속도를 위한 인덱스
1
2
3
42
.
.
.
ID A B C D
SELECT
UPDATE
DELETE
INSERT
초당 수십 번의 쿼리
SPOQA conference 2021 : Always Evolving!
PK 인덱스의 B-tree
5 9
11 13
3 4
7
2
1 6 8 10 12 14
.
.
.
SPOQA conference 2021 : Always Evolving!
인덱스의 원리
1 2
< 3
< 4
< 5
< 6
< 7
< 8
< 9
<
ID
검색 / 삽입 / 삭제 O(log n)
Row 수: 1 .. 10 .. 100
시간: 1 .. 2 .. 3
SPOQA conference 2021 : Always Evolving!
인덱스의 원리
1 2
< 3
< 4
< 5
< 6
< 7
< 8
< 9
<
ID
1 2 3 4 5 6 7 8 9
ctid
SPOQA conference 2021 : Always Evolving!
인덱스의 원리
1 2
< 3
< 4
< 5
< 6
< 7
< 8
< 9
<
ID
1 2 3 4 5 6 7 8 9
ctid
SELECT table.*
FROM table
WHERE ID = 4
O(log n)
ctid로 Row 조회
4
SPOQA conference 2021 : Always Evolving!
간단한 Index Scan 분석
EXPLAIN ANALYZE
SELECT *
FROM big_table
WHERE id = 123456789;
2억 row 이상
Index Scan using big_table_pkey on big_table (cost=...)
Index Cond: (id = 123456789)
Planning time: 0.802 ms
Execution time: 2.660 ms
SPOQA conference 2021 : Always Evolving!
인덱스 구조 요약
• 인덱스란 단어 뜻 그대로, 책의 색인, 목차와 같은 역할
• 테이블에는 각 row의 물리적(또는 논리적) 위치를 나타내는 내부 컬럼이 있다
• 인덱스의 각 노드에는 인덱스를 건 컬럼의 값과 해당 row의 위치를 나타내는 값을 가지고 있다
SPOQA conference 2021 : Always Evolving!
인덱스 응용 맛보기
Multi-column index와 Index on expression
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스
1 2 3
2 7 1
3 9 1
42 3 3
.
.
.
ID A B C D
2
4
1
5
1
2
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스
CREATE INDEX ix_table_a_b ON table (A, B);
1
1 < <
<
<
< <
2
3
3
1
4
2
4
5
5
2
A
B <
<
4
2
1
5
4
5
2
4
2
3
5
2
1
2
3
1
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스
CREATE INDEX ix_table_a_b ON table (B, A);
1
1
<
<
<
< < <
<
A
B
<
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스 사용 예
CREATE TABLE purchase (
id int PRIMARY KEY,
store_id int,
created_at timestamp,
...
);
매장에서 상품을 구매한 내역을 저장.
(store_id) 매장에서
(created_at)에 물건을 구매함.
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스 사용 예
CREATE INDEX ix_purchase_store_id_created_at
ON purchase (store_id, created_at);
02/06
13:03
02/06
15:42
02/07
09:22
store_id
created_at
1 번 매장
02/06
12:11
02/07
08:59
02/07
10:30
2 번 매장
02/06
19:41
02/06
21:02
3번 매장
< < < < <
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스 사용 예
02/06
13:03
02/06
15:42
02/07
09:22
store_id
created_at
1 번 매장
02/06
12:11
02/07
08:59
02/07
10:30
2 번 매장
02/06
19:41
02/06
21:02
3번 매장
2번 매장의 최근 구매내역 10개를
가져오고 싶어요
SELECT purchase.*
FROM purchase
WHERE store_id = 2
ORDER BY created_at DESC
LIMIT 10
< < < < <
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스 분석
Limit (cost=...)
-> Index Scan Backward using ix_purchase_store_id_created_at on purchase (cost=...)
Index Cond: (store_id = 2)
Planning time: 0.782 ms
Execution time: 1.460 ms
EXPLAIN ANALYZE
SELECT purchase.*
FROM purchase
WHERE store_id = 2
ORDER BY created_at DESC
LIMIT 10
SPOQA conference 2021 : Always Evolving!
Multi-column 인덱스 요약
• 앞에서 부터 정렬되기 때문에 순서에 매우 유의!
• (A, B) 인덱스가 (A) 인덱스를 대체할 수 있음 (인덱스 수 절약)
• 뒤쪽 컬럼에서 부등식이나 order by를 사용하면 효율적
SPOQA conference 2021 : Always Evolving!
Index on expression
1 2 3 -1
2 7 1 6
3 9 1 8
42 3 3 0
.
.
.
ID A B C D A - B
SPOQA conference 2021 : Always Evolving!
Index on expression
CREATE INDEX ix_table_a_b ON table ((A - B));
1
1
0
< < = = < < = <
3
1
2
1
2
-1
4
2
2
5
2
3
2
3
-1
2
4
-2
1
5
-4
4
5
-1
A
B
A - B
SPOQA conference 2021 : Always Evolving!
Index on expression 사용 예
CREATE TABLE store (
id int PRIMARY KEY,
...
updated_at timestamp,
synced_at timestamp,
);
외부 서비스에 동기화하는 테이블
updated_at > synced_at인
row만 골라서 동기화한다.
SPOQA conference 2021 : Always Evolving!
Index on expression 사용 예
CREATE INDEX ix_store_update_at_minus_synced_at
ON store ((updated_at - synced_at))
updated_at
synced_at
updated_at
- synced_at
02/06
12:00
02/06
9:00
3시간
02/06
15:20
02/06
11:10
4시간 10분
02/06
12:30
02/06
12:00
30분
02/05
12:10
02/05
12:10
0분
02/04
17:42
02/06
17:42
0분
02/06
09:30
02/06
08:30
1시간
<
< < <
=
SPOQA conference 2021 : Always Evolving!
Index on expression 사용 예
SELECT store.*
FROM store
WHERE updated_at - synced_at > INTERVAL ‘0 day’
LIMIT 100
updated_at
synced_at
updated_at
- synced_at
02/06
12:00
02/06
9:00
3시간
02/06
15:20
02/06
11:10
4시간 10분
02/06
12:30
02/06
12:00
30분
02/05
12:10
02/05
12:10
0분
02/04
17:42
02/06
17:42
0분
02/06
09:30
02/06
08:30
1시간
<
< < <
=
SPOQA conference 2021 : Always Evolving!
Index on expression 분석
EXPLAIN ANALYZE
SELECT store.*
FROM store
WHERE updated_at - synced_at > INTERVAL ‘0 day’
LIMIT 100
Limit (cost=...)
-> Index Scan using ix_store_updated_at_minus_synced_at on store (cost=...)
Index Cond: ((updated_at - synced_at) > '00:00:00'::interval)
Planning time: 0.598 ms
Execution time: 4.711 ms
SPOQA conference 2021 : Always Evolving!
Index on expression 요약
• 컬럼 그 자체의 값이 아닌, 함수를 적용한 값,
여러 컬럼으로 계산된 값에도 인덱스를 걸 수 있다
SPOQA conference 2021 : Always Evolving!
JOIN 맛보기
INNER JOIN과 OUTER JOIN
SPOQA conference 2021 : Always Evolving!
두 테이블의 JOIN
a_id b_id
1 2
2
3 1
4
A
b_id b_attr
1 spoqa
2 con
3 2021
4 !
B
1 : 0, 1
SPOQA conference 2021 : Always Evolving!
두 테이블의 JOIN
SELECT * FROM A INNER JOIN B ON A.b_id = B.b_id
SELECT * FROM A LEFT OUTER JOIN B ON A.b_id = B.b_id
SPOQA conference 2021 : Always Evolving!
INNER JOIN
• 양쪽에 모두 존재하는 데이터만 보여 줌
• 대충 쿼리해도 null 값 없이 원하는 값이 나옴
a_id b_id b_attr
1 2 con
3 1 spoqa
SELECT * FROM A INNER JOIN B ON A.b_id = B.b_id
SPOQA conference 2021 : Always Evolving!
OUTER JOIN
• 기준이 되는 A 테이블은 모든 데이터가 나옴
• B 테이블은 있으면 붙고 없으면 null로 대체 됨
• B의 존재 유무가 A의 결과에 영향을 끼치지 않기 때문에
데이터 로딩에 많이 사용 됨 (sqlalchemy joinedload)
a_id b_id b_attr
1 2 con
2
3 1 spoqa
4
SELECT * FROM A LEFT OUTER JOIN B ON A.b_id = B.b_id
SPOQA conference 2021 : Always Evolving!
매우 느린 백오피스
SPOQA conference 2021 : Always Evolving!
매우 느린 백오피스 쿼리
SELECT COUNT(*)
FROM coupon_promotion cp
JOIN coupon ON cp.coupon_id = coupon.id
• 백오피스 페이지네이션에 쓰이는 쿼리
• coupon_promotion과 coupon은 1:1 관계
• JOIN 없으면 1초 정도 걸림
SPOQA conference 2021 : Always Evolving!
백오피스 쿼리 분석
EXPLAIN ANALYZE
SELECT COUNT(*)
FROM coupon_promotion cp
JOIN coupon ON cp.coupon_id = coupon.id
...(생략)
-> Parallel Seq Scan on coupon_promotion (…)
...
-> Index Only Scan using coupon_pkey on coupon (...)
...
Planning time: 0.697 ms
Execution time: 94362.596 ms
1:1 관계임에도 불구하고
각 coupon_promotion 마다
coupon의 존재를 확인함
* 타 DBMS에서는 다를지도..?
foreign key + not null
SPOQA conference 2021 : Always Evolving!
OUTER JOIN으로 변경
EXPLAIN ANALYZE
SELECT COUNT(*)
FROM coupon_promotion cp
LEFT OUTER JOIN coupon ON cp.coupon_id = coupon.id
...(생략)
-> Parallel Seq Scan on coupon_promotion (cost=...)
Planning time: 0.410 ms
Execution time: 1047.600 ms
coupon의 존재여부를 확인하지 않음
SPOQA conference 2021 : Always Evolving!
INNER / OUTER JOIN 요약
• 데이터 로딩에는 OUTER JOIN이 유용하게 쓰인다
• OUTER JOIN이 쿼리 플래너에게 큰 힌트가 될 수 있다
SPOQA conference 2021 : Always Evolving!
감사합니다
SPOQA conference 2021 : Always Evolving!
참고자료들
• ctid: postgresql - system-columns
• Btree 등 self-balancing balanced search tree
• Wiki - Self-balancing binary search tree
• Wiki - B-tree
• postgresql - btree
• 시간복잡도: Wiki - Time complexity
• Multi-column index (composite index): postgresql - indexes-multicolumn
• Index on expression: postgresql - indexes-expressional
• mysql 에서는 virtual column + index / function based index
• 쿼리 플랜 유도
• postgresql - planner-optimizer
• postgresql - explicit-joins
[제3회 스포카콘] SQL 쿼리 최적화 맛보기

More Related Content

Recently uploaded

Recently uploaded (8)

실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
실험 설계의 평가 방법: Custom Design을 중심으로 반응인자 최적화 및 Criteria 해석
 
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
(독서광) 인간이 초대한 대형 참사 - 대형 참사가 일어날 때까지 사람들은 무엇을 하고 있었는가?
 
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement MethodologyJMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
JMP를 활용한 전자/반도체 산업 Yield Enhancement Methodology
 
JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례JMP를 활용한 가속열화 분석 사례
JMP를 활용한 가속열화 분석 사례
 
JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!JMP가 걸어온 여정, 새로운 도약 JMP 18!
JMP가 걸어온 여정, 새로운 도약 JMP 18!
 
데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법데이터 분석 문제 해결을 위한 나의 JMP 활용법
데이터 분석 문제 해결을 위한 나의 JMP 활용법
 
공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화공학 관점에서 바라본 JMP 머신러닝 최적화
공학 관점에서 바라본 JMP 머신러닝 최적화
 
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
JMP 기능의 확장 및 내재화의 핵심 JMP-Python 소개
 

Featured

Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 

Featured (20)

PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
 

[제3회 스포카콘] SQL 쿼리 최적화 맛보기

  • 1. SQL 쿼리 최적화 맛보기 Full Stack Programmer SPOQA conference 2021 : Always Evolving! Jhuni 박종훈
  • 2. SPOQA conference 2021 : Always Evolving! 발표자 소개 • 스포카 도도 포인트 팀 3년 근무 • 풀 스택 프로그래머 • 클린한 인프라 및 아키텍쳐 설계, 최적화에 관심 • GitHub/Jhuni0123 Jhuni 박종훈
  • 3. SPOQA conference 2021 : Always Evolving! 시작하기 전에... • PostgreSQL 기준입니다. (다른 DBMS에도 비슷한 개념이 있어요) • 맨 뒤에 참고 자료 링크들이 있습니다. • 궁금하신 것이 있다면 채팅으로 남겨주세요!
  • 4. SPOQA conference 2021 : Always Evolving! 테이블과 인덱스 맛보기 테이블과 인덱스의 내부 구조
  • 5. SPOQA conference 2021 : Always Evolving! 테이블 1 2 3 42 . . . ID A B C D (편의상 ctid = ID) 1 2 3 42 ctid 책의 쪽 번호
  • 6. SPOQA conference 2021 : Always Evolving! 테이블의 PK 1 2 3 42 . . . ID A B C D Primary Key (뒤에 인덱스 있어요)
  • 7. SPOQA conference 2021 : Always Evolving! 속도를 위한 인덱스 1 2 3 42 . . . ID A B C D SELECT UPDATE DELETE INSERT 초당 수십 번의 쿼리
  • 8. SPOQA conference 2021 : Always Evolving! PK 인덱스의 B-tree 5 9 11 13 3 4 7 2 1 6 8 10 12 14 . . .
  • 9. SPOQA conference 2021 : Always Evolving! 인덱스의 원리 1 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < ID 검색 / 삽입 / 삭제 O(log n) Row 수: 1 .. 10 .. 100 시간: 1 .. 2 .. 3
  • 10. SPOQA conference 2021 : Always Evolving! 인덱스의 원리 1 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < ID 1 2 3 4 5 6 7 8 9 ctid
  • 11. SPOQA conference 2021 : Always Evolving! 인덱스의 원리 1 2 < 3 < 4 < 5 < 6 < 7 < 8 < 9 < ID 1 2 3 4 5 6 7 8 9 ctid SELECT table.* FROM table WHERE ID = 4 O(log n) ctid로 Row 조회 4
  • 12. SPOQA conference 2021 : Always Evolving! 간단한 Index Scan 분석 EXPLAIN ANALYZE SELECT * FROM big_table WHERE id = 123456789; 2억 row 이상 Index Scan using big_table_pkey on big_table (cost=...) Index Cond: (id = 123456789) Planning time: 0.802 ms Execution time: 2.660 ms
  • 13. SPOQA conference 2021 : Always Evolving! 인덱스 구조 요약 • 인덱스란 단어 뜻 그대로, 책의 색인, 목차와 같은 역할 • 테이블에는 각 row의 물리적(또는 논리적) 위치를 나타내는 내부 컬럼이 있다 • 인덱스의 각 노드에는 인덱스를 건 컬럼의 값과 해당 row의 위치를 나타내는 값을 가지고 있다
  • 14. SPOQA conference 2021 : Always Evolving! 인덱스 응용 맛보기 Multi-column index와 Index on expression
  • 15. SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 1 2 3 2 7 1 3 9 1 42 3 3 . . . ID A B C D
  • 16. 2 4 1 5 1 2 SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 CREATE INDEX ix_table_a_b ON table (A, B); 1 1 < < < < < < 2 3 3 1 4 2 4 5 5 2 A B < <
  • 17. 4 2 1 5 4 5 2 4 2 3 5 2 1 2 3 1 SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 CREATE INDEX ix_table_a_b ON table (B, A); 1 1 < < < < < < < A B <
  • 18. SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 사용 예 CREATE TABLE purchase ( id int PRIMARY KEY, store_id int, created_at timestamp, ... ); 매장에서 상품을 구매한 내역을 저장. (store_id) 매장에서 (created_at)에 물건을 구매함.
  • 19. SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 사용 예 CREATE INDEX ix_purchase_store_id_created_at ON purchase (store_id, created_at); 02/06 13:03 02/06 15:42 02/07 09:22 store_id created_at 1 번 매장 02/06 12:11 02/07 08:59 02/07 10:30 2 번 매장 02/06 19:41 02/06 21:02 3번 매장 < < < < <
  • 20. SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 사용 예 02/06 13:03 02/06 15:42 02/07 09:22 store_id created_at 1 번 매장 02/06 12:11 02/07 08:59 02/07 10:30 2 번 매장 02/06 19:41 02/06 21:02 3번 매장 2번 매장의 최근 구매내역 10개를 가져오고 싶어요 SELECT purchase.* FROM purchase WHERE store_id = 2 ORDER BY created_at DESC LIMIT 10 < < < < <
  • 21. SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 분석 Limit (cost=...) -> Index Scan Backward using ix_purchase_store_id_created_at on purchase (cost=...) Index Cond: (store_id = 2) Planning time: 0.782 ms Execution time: 1.460 ms EXPLAIN ANALYZE SELECT purchase.* FROM purchase WHERE store_id = 2 ORDER BY created_at DESC LIMIT 10
  • 22. SPOQA conference 2021 : Always Evolving! Multi-column 인덱스 요약 • 앞에서 부터 정렬되기 때문에 순서에 매우 유의! • (A, B) 인덱스가 (A) 인덱스를 대체할 수 있음 (인덱스 수 절약) • 뒤쪽 컬럼에서 부등식이나 order by를 사용하면 효율적
  • 23. SPOQA conference 2021 : Always Evolving! Index on expression 1 2 3 -1 2 7 1 6 3 9 1 8 42 3 3 0 . . . ID A B C D A - B
  • 24. SPOQA conference 2021 : Always Evolving! Index on expression CREATE INDEX ix_table_a_b ON table ((A - B)); 1 1 0 < < = = < < = < 3 1 2 1 2 -1 4 2 2 5 2 3 2 3 -1 2 4 -2 1 5 -4 4 5 -1 A B A - B
  • 25. SPOQA conference 2021 : Always Evolving! Index on expression 사용 예 CREATE TABLE store ( id int PRIMARY KEY, ... updated_at timestamp, synced_at timestamp, ); 외부 서비스에 동기화하는 테이블 updated_at > synced_at인 row만 골라서 동기화한다.
  • 26. SPOQA conference 2021 : Always Evolving! Index on expression 사용 예 CREATE INDEX ix_store_update_at_minus_synced_at ON store ((updated_at - synced_at)) updated_at synced_at updated_at - synced_at 02/06 12:00 02/06 9:00 3시간 02/06 15:20 02/06 11:10 4시간 10분 02/06 12:30 02/06 12:00 30분 02/05 12:10 02/05 12:10 0분 02/04 17:42 02/06 17:42 0분 02/06 09:30 02/06 08:30 1시간 < < < < =
  • 27. SPOQA conference 2021 : Always Evolving! Index on expression 사용 예 SELECT store.* FROM store WHERE updated_at - synced_at > INTERVAL ‘0 day’ LIMIT 100 updated_at synced_at updated_at - synced_at 02/06 12:00 02/06 9:00 3시간 02/06 15:20 02/06 11:10 4시간 10분 02/06 12:30 02/06 12:00 30분 02/05 12:10 02/05 12:10 0분 02/04 17:42 02/06 17:42 0분 02/06 09:30 02/06 08:30 1시간 < < < < =
  • 28. SPOQA conference 2021 : Always Evolving! Index on expression 분석 EXPLAIN ANALYZE SELECT store.* FROM store WHERE updated_at - synced_at > INTERVAL ‘0 day’ LIMIT 100 Limit (cost=...) -> Index Scan using ix_store_updated_at_minus_synced_at on store (cost=...) Index Cond: ((updated_at - synced_at) > '00:00:00'::interval) Planning time: 0.598 ms Execution time: 4.711 ms
  • 29. SPOQA conference 2021 : Always Evolving! Index on expression 요약 • 컬럼 그 자체의 값이 아닌, 함수를 적용한 값, 여러 컬럼으로 계산된 값에도 인덱스를 걸 수 있다
  • 30. SPOQA conference 2021 : Always Evolving! JOIN 맛보기 INNER JOIN과 OUTER JOIN
  • 31. SPOQA conference 2021 : Always Evolving! 두 테이블의 JOIN a_id b_id 1 2 2 3 1 4 A b_id b_attr 1 spoqa 2 con 3 2021 4 ! B 1 : 0, 1
  • 32. SPOQA conference 2021 : Always Evolving! 두 테이블의 JOIN SELECT * FROM A INNER JOIN B ON A.b_id = B.b_id SELECT * FROM A LEFT OUTER JOIN B ON A.b_id = B.b_id
  • 33. SPOQA conference 2021 : Always Evolving! INNER JOIN • 양쪽에 모두 존재하는 데이터만 보여 줌 • 대충 쿼리해도 null 값 없이 원하는 값이 나옴 a_id b_id b_attr 1 2 con 3 1 spoqa SELECT * FROM A INNER JOIN B ON A.b_id = B.b_id
  • 34. SPOQA conference 2021 : Always Evolving! OUTER JOIN • 기준이 되는 A 테이블은 모든 데이터가 나옴 • B 테이블은 있으면 붙고 없으면 null로 대체 됨 • B의 존재 유무가 A의 결과에 영향을 끼치지 않기 때문에 데이터 로딩에 많이 사용 됨 (sqlalchemy joinedload) a_id b_id b_attr 1 2 con 2 3 1 spoqa 4 SELECT * FROM A LEFT OUTER JOIN B ON A.b_id = B.b_id
  • 35. SPOQA conference 2021 : Always Evolving! 매우 느린 백오피스
  • 36. SPOQA conference 2021 : Always Evolving! 매우 느린 백오피스 쿼리 SELECT COUNT(*) FROM coupon_promotion cp JOIN coupon ON cp.coupon_id = coupon.id • 백오피스 페이지네이션에 쓰이는 쿼리 • coupon_promotion과 coupon은 1:1 관계 • JOIN 없으면 1초 정도 걸림
  • 37. SPOQA conference 2021 : Always Evolving! 백오피스 쿼리 분석 EXPLAIN ANALYZE SELECT COUNT(*) FROM coupon_promotion cp JOIN coupon ON cp.coupon_id = coupon.id ...(생략) -> Parallel Seq Scan on coupon_promotion (…) ... -> Index Only Scan using coupon_pkey on coupon (...) ... Planning time: 0.697 ms Execution time: 94362.596 ms 1:1 관계임에도 불구하고 각 coupon_promotion 마다 coupon의 존재를 확인함 * 타 DBMS에서는 다를지도..? foreign key + not null
  • 38. SPOQA conference 2021 : Always Evolving! OUTER JOIN으로 변경 EXPLAIN ANALYZE SELECT COUNT(*) FROM coupon_promotion cp LEFT OUTER JOIN coupon ON cp.coupon_id = coupon.id ...(생략) -> Parallel Seq Scan on coupon_promotion (cost=...) Planning time: 0.410 ms Execution time: 1047.600 ms coupon의 존재여부를 확인하지 않음
  • 39. SPOQA conference 2021 : Always Evolving! INNER / OUTER JOIN 요약 • 데이터 로딩에는 OUTER JOIN이 유용하게 쓰인다 • OUTER JOIN이 쿼리 플래너에게 큰 힌트가 될 수 있다
  • 40. SPOQA conference 2021 : Always Evolving! 감사합니다
  • 41. SPOQA conference 2021 : Always Evolving! 참고자료들 • ctid: postgresql - system-columns • Btree 등 self-balancing balanced search tree • Wiki - Self-balancing binary search tree • Wiki - B-tree • postgresql - btree • 시간복잡도: Wiki - Time complexity • Multi-column index (composite index): postgresql - indexes-multicolumn • Index on expression: postgresql - indexes-expressional • mysql 에서는 virtual column + index / function based index • 쿼리 플랜 유도 • postgresql - planner-optimizer • postgresql - explicit-joins