SlideShare a Scribd company logo
1 of 44
Download to read offline
PostgreSQL SQL Tuning
유 재 근
mail: naivety1@naver.com
PGDay.Seoul.2020
1 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
I. 발표자
II. 시스템 성능 저하 요인
III. 시스템 부하 비교
V. 테스트 데이터
VI. SQL 성능 개선 방안
목 차
IV. SQL이 느린 이유
2 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
I. 발표자
• 데이터 모델링
• SQL 튜닝
• DAP
3 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
II. 시스템 성능 저하 요인
• CPU, Memory, HDD, Network
• System Design
• Application Design
• Data Modeling
• SQL
80 ~ 90 %
less CPU, less Memory
4 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
• The answer is not in the load graph.
• The answer is not in the expensive DBMS.
• The answer is in your query.
• Queries cause load.
III. 시스템 부하 비교
0
10
20
30
40
50
60
70
80
90
100
0:00
0:50
1:40
2:30
3:20
4:10
5:00
5:50
6:40
7:30
8:20
9:10
10:00
10:50
11:40
12:30
13:20
14:10
15:00
15:50
16:40
17:30
18:20
19:10
20:00
20:50
21:40
22:30
23:20
CPU load
0
10
20
30
40
50
60
70
80
90
100
0:00
0:50
1:40
2:30
3:20
4:10
5:00
5:50
6:40
7:30
8:20
9:10
10:00
10:50
11:40
12:30
13:20
14:10
15:00
15:50
16:40
17:30
18:20
19:10
20:00
20:50
21:40
22:30
23:20
CPU load
BEFORE TUNING AFTER TUNING
**시 정보자원 통합인프라 구축 및 시스템 보강 사업
5 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
IV. SQL이 느린 이유
• Optimizer algorithm limitations
- index page random access cost, heap seq access cost, operator cost
- CPU, DISK I/O, Network bandwidth
- parsing time
- genetic query optimizer, exhaustive-search algorithm
• PostgreSQL doesn’t know the data the way you know.
• Developers lacking in skills
- Common mistakes developers make
- Locks
- LOOP, WHILE, CURSOR
• Inappropriate Data Modelling
- Outer Joins, Distinct, Group By, Coalesce
- attribute duplication
6 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
테스트 데이터 ERD
V. 테스트 데이터
7 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
create table ORDERS (ord_no bigint,cust_id
varchar(20),comment varchar(100),ord_date varchar(8));
ALTER TABLE ORDERS ADD CONSTRAINT PK_ORDERS
PRIMARY KEY(ORD_NO);
CREATE TABLE ORDERS_DETAIL(ORD_LINE_NO BIGINT
NOT NULL,ORD_NO BIGINT NOT NULL,PROD_ID
VARCHAR(10) NOT NULL,COMMENT
VARCHAR(100),ORD_AMT BIGINT);
ALTER TABLE ORDERS_DETAIL ADD CONSTRAINT
PK_ORDERS_DETAIL PRIMARY KEY(ORD_LINE_NO);
CREATE INDEX ORDERS_DETAIL_X01 ON
ORDERS_DETAIL(ORD_NO, PROD_ID);
CREATE INDEX ORDERS_DETAIL_X02 ON
ORDERS_DETAIL(ORD_NO, ORD_AMT);
CREATE TABLE PROD (PROD_ID VARCHAR(10) NOT
NULL,PROD_NM VARCHAR(100) NOT NULL);
ALTER TABLE PROD ADD CONSTRAINT PK_PROD PRIMARY
KEY(PROD_ID);
insert into ORDERS
select i as ord_no
, 'C'||mod(i,10) as cust_id
, lpad('X',10,'Y') as comment
,
to_char(to_date('20191001','YYYYMMDD')+mod(i,60),'yyyym
mdd') as ord_date
from generate_series(1,1000000) a(i);
INSERT INTO ORDERS_DETAIL
SELECT i as ORD_LINE_NO
, mod(i,1000000) AS ORD_NO
, 'PP'||MOD(i,5) AS PROD_ID
, lpad('X',10,'Y') as comment
, case when i < 1000 then i*100 else i end as prod_amt
FROM generate_series(1,10000000) a(i);
INSERT INTO PROD
SELECT PROD_ID
, MAX(ORD_NO)||'TEST_NAME'
FROM ORDERS_DETAIL
GROUP BY PROD_ID;
테스트 데이터 생성 스크립트
V. 테스트 데이터
8 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
VI. SQL 성능 개선 방안
“Aside from shared_buffers, the most
important memory-allocation
parameter is work_mem. Raising this
value can dramatically improve the
performance of certain queries.”
Maybe….
9 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
○ LOOP 안에 사용된 SQL이나 함수의 경우 최소한으로 수행되도록 작성해야 한다.
○ 아래 예제에서는 1000회 수행하던 SQL을 1회 수행하도록 수정함
VI. SQL 성능 개선 방안
1. 함수/프로시저 개선
1.1 불필요한 LOOP 제거
CREATE OR REPLACE PROCEDURE P_SEL()
LANGUAGE plpgsql
AS $$
DECLARE
l_prod_name varchar;
REC varchar;
BEGIN
--orders_detail테이블의 내용으로 1000번 반복 LOOP 수행
FOR REC IN (SELECT PROD_ID FROM ORDERS_DETAIL limit 1000)
LOOP
BEGIN
-- 상품명 확인 위해 prod_id로 반복 조회
SELECT PROD_NM
INTO l_prod_name
FROM PROD A
WHERE A.PROD_ID = REC;
END;
RAISE NOTICE '상품명:% ', l_prod_name;
END LOOP;
END;
$$;
CREATE OR REPLACE PROCEDURE P_SEL()
LANGUAGE plpgsql
AS $$
DECLARE
l_prod_name varchar;
REC varchar;
BEGIN
--LOOP안의 SQL을 스칼라서브쿼리로 변경
FOR REC IN (SELECT (SELECT PROD_NM
FROM PROD A
WHERE A.PROD_ID = B.PROD_ID) AS PROD_NM
FROM ORDERS_DETAIL B
LIMIT 1000)
LOOP
RAISE NOTICE '상품명:% ', REC;
END LOOP;
END;
$$;
Elapsed time :
1.23 sec  0.21 sec
10 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
○ 간단한 로직을 함수로 구현하면 실행계획에 재약이 발생하여 성능이 느려진다.
○ 아래 예제는 PROD_ID를 입력 받아 PROD_NM을 출력하는 함수를 조인으로 수정하여 성능 개선한 사례
VI. SQL 성능 개선 방안
1. 함수/프로시저 개선
1.2 간단한 로직은 함수 대신 조인으로 구현
SELECT A.ORD_NO
,F_GET_PROD_NM(A.PROD_ID) AS PROD_NM
FROM ORDERS_DETAIL A
WHERE A.ORD_NO BETWEEN 1 AND 100000;
SELECT A.ORD_NO
,B.PROD_NM
FROM ORDERS_DETAIL A, PROD B
WHERE A.PROD_ID = B.PROD_ID
AND A.ORD_NO BETWEEN 1 AND 100000
Elapsed time :
3.13 sec  0.78 sec
11 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
○ 함수를 꼭 사용해야 한다면 수행 횟수를 줄여야 한다.
SELECT A.ORD_LINE_NO, A.ORD_NO, B.COMMENT
FROM ORDERS_DETAIL A, ORDERS B
WHERE A.ORD_NO = B.ORD_NO
AND B.ORD_NO BETWEEN 1006 AND 2005
AND F_GET_PROD_NM(A.PROD_ID) =
'999999TEST_NAME'
SELECT A.ORD_LINE_NO, A.ORD_NO, COMMENT
FROM (
SELECT A.ORD_LINE_NO, A.ORD_NO
, A.COMMENT
, F_GET_PROD_NM(A.PROD_ID) AS FN
FROM ORDERS_DETAIL A, ORDERS B
WHERE A.ORD_NO = B.ORD_NO
AND B.ORD_NO BETWEEN 1006 AND 2005
) A
WHERE FN = '999999TEST_NAME'
Elapsed time :
37.19 sec  1.38 sec 함수 10000 회 수행
VI. SQL 성능 개선 방안
1. 함수/프로시저 개선
1.3 함수 수행 횟수 감소 함수 2000000+800000 회 수행
12 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
○ 아래와 같이 Lateral View를 사용해서 Index Access를 유도할 수 있다..
인덱스 컬럼 : PROD_ID, ORD_NO
VI. SQL 성능 개선 방안
2. LATERVAL View를 이용한 성능 개선
2.1 Complex View Merge 대체
SELECT A.*, B.*
FROM PROD A,
(SELECT PROD_ID, AVG(ORD_AMT)
FROM ORDERS_DETAIL
WHERE ORD_NO < 8000
GROUP BY PROD_ID) B
WHERE A.PROD_ID = B.PROD_ID
AND A.PROD_NM = '999998TEST_NAME';
SELECT A.*, B.*
FROM PROD A,
LATERAL (SELECT PROD_ID, AVG(ORD_AMT)
FROM ORDERS_DETAIL B
WHERE ORD_NO < 8000
AND B.PROD_ID = A.PROD_ID
GROUP BY PROD_ID) B
WHERE A.PROD_NM = '999998TEST_NAME';
쿼리 수정
0.05 sec 소요
1.8 sec 소요
orders_detail 을 full scan함
13 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
3. 중복테이블 제거
○ SQL 작성시 같은 테이블을 반복 사용하지 마라.
○ 수정 후 SQL은 ORDERS_DETAIL을 1회만 ACCESS한다.
VI. SQL 성능 개선 방안
SELECT O.ORD_NO, O. ORD_DATE, D.ORD_AMT
FROM (SELECT O.ORD_NO, O.ORD_DATE
FROM ORDERS O
WHERE O.CUST_ID = 'C0'
UNION ALL
SELECT O.ORD_NO, O.ORD_DATE
FROM ORDERS O
WHERE O.ORD_DATE = ‘20190102'
) O, ORDERS_DETAIL D
WHERE O.ORD_NO = D.ORD_NO
SELECT O.ORD_NO, O.ORD_DATE, D.ORD_AMT
FROM ORDERS O, ORDERS_DETAIL D
WHERE O.ORD_NO = D.ORD_NO
AND O.CUST_ID = 'C0‘
UNION ALL
SELECT O.ORD_NO, O. ORD_DATE, D.ORD_AMT
FROM ORDERS O, ORDERS_DETAIL D
WHERE O.ORD_NO = D.ORD_NO
AND O.ORD_DATE = ‘20190102'
7.1 sec  4.4 sec
14 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
○ 스칼라서브쿼리는 메인 쿼리의 결과 건수 만큼 반복 수행된다.
○ 스칼라서브쿼리를 일반 조인으로 변경하면, 옵티마이저는 NL조인, HASH 조인 또는 MERGE 조인 방식 중 최적의 조인 방법을
선택할 수 있다.
VI. SQL 성능 개선 방안
4. 스칼라서브쿼리를 조인으로 변경한 성능 개선
SELECT A.ORD_NO
,(SELECT PROD_NM
FROM PROD
WHERE PROD_ID=A.PROD_ID) AS PROD_NM
FROM ORDERS_DETAIL A
WHERE A.ORD_NO BETWEEN 1 AND 100000
SELECT A.ORD_NO
,B.PROD_NM
FROM ORDERS_DETAIL A LEFT JOIN PROD B
ON A.PROD_ID = B.PROD_ID
WHERE A.ORD_NO BETWEEN 1 AND 100000
쿼리 수정
0.39 sec 소요
0.95 sec 소요
1 row당 0.001msec
0.001*1000000=1sec
15 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.1 부분 페이징 처리 방식(1)
○ 개선 전 쿼리는 인덱스 컬럼이 (ORD_AMT, ORD_NO) 아니기 때문에 테이블 전체를 읽은 후 SORT해서 10건을 추출한다.
○ 개선 후 쿼리는 아래의 순서로 처리한다.
ORD_AMT 컬럼 인덱스를 이용해서 10건만 읽는다.
GROUP BY를 해서 중복된 ORD_AMT 값을 제거한다.
ORDERS_DETAIL 테이블과 조인 후 ORD_AMT DESC, ORD_NO DESC 로 정렬 후 10건을 추출한다.
○ ORACLE은 인라인 뷰 내에서 ROWID를 추출하여 RANDOM ACCESS량을 더 줄일 수 있으나, PostgreSQL은 불가능하다.
VI. SQL 성능 개선 방안
SELECT C.ORD_NO, C.PROD_ID, C.COMMENT, C.ORD_AMT
FROM (
SELECT ORD_AMT
FROM(
SELECT ORD_AMT
FROM ORDERS_DETAIL
WHERE PROD_ID = 'PP2'
ORDER BY ORD_AMT DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
) A
GROUP BY ORD_AMT
) B, ORDERS_DETAIL C
WHERE B.ORD_AMT = C.ORD_AMT
AND C.PROD_ID = ‘PP2’
ORDER BY C.ORD_AMT DESC, C.ORD_NO DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM ORDERS_DETAIL
WHERE PROD_ID = 'PP2‘
ORDER BY ORD_AMT DESC, ORD_NO DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY
1.46 sec -> 0.1sec
CREATE INDEX ORDERS_X02 ON ORDERS(ORD_AMT);
ORD_AMT 인덱스 사용 못함
16 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.1 부분 페이징 처리 방식(2)
○ 개선 전 쿼리는 A집합 947959건과 B집합 10,000,000 건을 조인하고, Sorting 후에 20건을 추출한다.
○ 개선 후 쿼리는 아래의 순서로 처리한다.
K 인라인 뷰에서 20 로우의 ORD_DATE를 추출하고 중복값을 제거하기 위해 GROUP BY를 수행한다.(1건 추출)
해당 ORD_DATE 값으로 ORDERS 테이블을 조회한다. K 인라인 뷰로부터 20 개 이내의 ORD_DATE값을 제공 받으므로 건수가
매우 적다.(2739건)
ORDERS_DTAIL과 조인을 수행하여 최종 데이터를 추출하며(27390건) A.ORD_DATE DESC, B.ORD_AMT로 정렬을 수행한다.
최종 20건의 데이터를 출력한다.
VI. SQL 성능 개선 방안
/*+ leading(((v a) b)) NestLoop(v a) NestLoop(v a b) */
SELECT *
FROM (
SELECT ORD_DATE
FROM (
SELECT ORD_DATE
FROM ORDERS A, ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
AND A.ORD_DATE < '20191213‘
ORDER BY A.ORD_DATE DESC
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY
) K
GROUP BY ORD_DATE
) V, ORDERS A, ORDERS_DETAIL B
WHERE V.ORD_DATE = A.ORD_DATE
AND A.ORD_NO = B.ORD_NO
AND A.ORD_DATE < '20191213'
ORDER BY A.ORD_DATE DESC, B.ORD_AMT
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
SELECT *
FROM ORDERS A, ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
AND A.ORD_DATE < '20191213‘
ORDER BY A.ORD_DATE DESC, B.ORD_AMT
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
9.3 sec -> 0.5sec
CREATE INDEX ORDERS_X02 ON ORDERS(ORD_DATE);
17 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.1 부분 페이징 처리 방식(3)
○ 앞 장의 쿼리에서 B.PROD_ID=‘PP2’ 조건 추가
○ 개선 후 쿼리에서 A.ORD_DATE로 정렬하여 20건 추출 시
B.PROD_ID=‘PP2’만 만족하는 데이터만 추출되도록 EXISTS
절을 사용하였다.
VI. SQL 성능 개선 방안
/*+ leading(((v a) b)) NestLoop(v a) NestLoop(v a b) */
SELECT *
FROM (
SELECT ORD_DATE
FROM (
SELECT ORD_DATE
FROM ORDERS A, ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
AND A.ORD_DATE < '20191213‘
AND EXISTS (SELECT 1
FROM ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
AND B.PROD_ID = 'PP2')
ORDER BY A.ORD_DATE DESC
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY
) K
GROUP BY ORD_DATE
) V, ORDERS A, ORDERS_DETAIL B
WHERE V.ORD_DATE = A.ORD_DATE
AND A.ORD_NO = B.ORD_NO
AND A.ORD_DATE < '20191213'
AND B.PROD_ID = ‘PP2’
ORDER BY A.ORD_DATE DESC, B.ORD_AMT
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
SELECT *
FROM ORDERS A, ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
AND A.ORD_DATE < '20191213‘
AND B.PROD_ID = ‘PP2’
ORDER BY A.ORD_DATE DESC, B.ORD_AMT
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
3.6 sec -> 0.6sec
개선 후 실행계획
20건만 추출
SORT 오퍼레이션 없음
18 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.2 union all 을 이용한 페이징 처리
○ 개선 전 쿼리는 UNION ALL 위쪽 집합과 아래 집합 결과를 합한 후 SORTING하여 10건을 추출한다.
○ 개선 후 쿼리는 위쪽 집합에서 20건, 아래쪽 집합에서 20건을 추출하여 합친 후 다시 Sorting을 하고 10건을 추출한다.
VI. SQL 성능 개선 방안
SELECT ORD_NO, COMMENT
FROM (
SELECT *
FROM (SELECT ORD_NO, COMMENT
FROM ORDERS
WHERE ORD_NO > 5000
ORDER BY ORD_NO DESC
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY
) A
UNION ALL
SELECT *
FROM (SELECT ORD_NO, COMMENT
FROM ORDERS_DETAIL
WHERE ORD_NO > 5000
ORDER BY ORD_NO DESC
OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY
) B
) C
ORDER BY ORD_NO DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
SELECT *
FROM (
SELECT ORD_NO, COMMENT
FROM ORDERS
WHERE ORD_NO > 5000
UNION ALL
SELECT ORD_NO, COMMENT
FROM ORDERS_DETAIL
WHERE ORD_NO > 5000
) A
ORDER BY ORD_NO DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
3.5 sec -> 0.2sec
19 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.3 인라인 뷰를 이용한 페이징 처리
○ 개선 전 쿼리는 스칼라서브쿼리가 40010회 수행되나, 개선 후에는 10회만 수행된다.
VI. SQL 성능 개선 방안
SELECT V.ORD_NO, V.ORD_DATE, V.COMMENT
,(SELECT B.PROD_NM
FROM PROD B
WHERE B.PROD_ID = V.PROD_ID) AS PROD_NAME
FROM (
SELECT A.ORD_NO, A.ORD_DATE, C.COMMENT, C.PROD_ID
FROM ORDERS A, ORDERS_DETAIL C
WHERE A.ORD_NO = C.ORD_NO
AND C.ORD_AMT > 9900000
ORDER BY A.ORD_NO, A.ORD_DATE
OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY
) V;
SELECT A.ORD_NO, A.ORD_DATE, C.COMMENT
, (SELECT B.PROD_NM
FROM PROD B
WHERE B.PROD_ID = C.PROD_ID) AS PROD_NAME
FROM ORDERS A, ORDERS_DETAIL C
WHERE A.ORD_NO = C.ORD_NO
AND C.ORD_AMT > 9900000
ORDER BY A.ORD_NO, A.ORD_DATE
OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY
스칼라서브쿼리 수
행 횟수 감소로
Block I/O, elapsed
time 감소
20 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.4 OUTER 조인을 이용한 페이징 처리
○ 아래 SQL에서 OUTER 조인은 데이터를 증가시키지 않으며 단지 B.ORD.DATE를 출력하기 위한 용도이다.
○ OUTER 조인에서는 A집합 전체와 B 집합이 조인을 수행하나, 개선 후에는 A집합 에서 10건만 추출하여 B 집합과 조인을 한다.
VI. SQL 성능 개선 방안
SELECT V.ORD_LINE_NO, V.ORD_NO, V.PROD_ID, B.ORD_DATE
FROM (
SELECT A.ORD_LINE_NO, A.ORD_NO
, A.ORD_AMT, A.PROD_ID
FROM ORDERS_DETAIL A
ORDER BY A.ORD_AMT DESC
OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY
) V LEFT JOIN ORDERS B
ON V.ORD_NO = B.ORD_NO
ORDER BY V.ORD_AMT DESC;
SELECT A.ORD_LINE_NO, A.ORD_NO, A.PROD_ID
, B.ORD_DATE
FROM ORDERS_DETAIL A LEFT JOIN ORDERS B
ON A.ORD_NO = B.ORD_NO
ORDER BY A.ORD_AMT DESC
OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY;
10.6 sec
-> 5.4sec
21 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.5 웹화면 페이징 쿼리(1)
○ 아래 웹화면 예시에서 10 페이지까지 만을 숫자로 표시하고, 그 다음은 <다음> 버튼을 통해 뒤로 이동해야 한다.
○ 하나의 페이지에는 10개 제목을 표시한다.
VI. SQL 성능 개선 방안
--첫번째 화면에 수행되는 SQL
SELECT ORD_NO, CUST_ID, COMMENT, ORD_DATE
FROM ORDERS
WHERE CUST_ID = ‘C’
ORDER BY ORD_DATE DESC
OFFSET 0 ROWS FETCH NEXT 101 ROWS ONLY;
--101건의 데이터를 추출하여 100건은 화면에 추출하며 마지막
101번째의 데이터는 2번째 페이지가 존재한다는 의미로 사용
--이와 같이 수행을 계속하여 마지막에 추출되는 데이터가 101건을
만족시키지 못하는 경우 해당 화면은 마지막 페이지
--첫번째 화면에 수행되는 SQL
SELECT CEIL(COUNT(*)/100) CNT
FROM ORDERS
WHERE CUST_ID = ‘C3’;
--첫번째 화면 데이터 출력
SELECT ORD_NO, CUST_ID, COMMENT, ORD_DATE
FROM ORDERS
WHERE CUST_ID = ‘C3’
ORDER BY ORD_DATE DESC
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONY;
--CNT < 2 이면, <다음> 콤보 박스를 INACTIVE로 변경
--CNT >= 2 이면, <다음> 콤보박스를 ACTIVE로 변경
--사용자가 <다음> 을 클릭하면 다음 101 ~ 200 ROW 에 대해서
동일한 패턴의 쿼리 수행
COUNT 쿼리 제거로 성능 향상
22 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.5 웹화면 페이징 쿼리(2)
○ 개선 전에는 뒤쪽 페이지로 갈 수록 BLOCK I/O가 증가한다.
○ 개선 후에는 11번째 ROW의 데이터를 이용하여 읽기
시작점을 찾고 그 이후만 읽어서 항상 일정한 BLOCK I/O가
발생한다.
○ (PROD_ID, ORD_AMT) UNIQUE하지 않으면, PK 컬럼인
ORD_LINE_NO를 인덱스와 SQL에 추가해야 한다.
: 오라클은 ROWID를 이용해서 인덱스 수정 필요 없음
VI. SQL 성능 개선 방안
--첫번째 화면에 수행되는 SQL
SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT, ORD_LINE_NO
FROM ORDERS_DETAIL
WHERE PROD_ID = ‘PP1’
ORDER BY ORD_AMT DESC
OFFSET 0 ROWS FETCH NEXT 11 ROWS ONLY;
--2번째 페이지 조회
--1번째 쿼리 조회 결과 11번째 row의 ORD_AMT=9999946
SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM (SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM ORDERS_DETAIL
WHERE (PROD_ID, ORD_AMT) IN ( 'PP1‘, 9999946)
ORDER BY ORD_AMT DESC LIMIT 11) X
UNION ALL
SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM (SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM ORDERS_DETAIL
WHERE PROD_ID = ‘PP1’
AND ORD_AMT< 9999946
ORDER BY ORD_AMT DESC LIMIT 11 ) Y
ORDER BY ORD_AMT DESC
FETCH NEXT 11 ROWS ONLY
--1번째 페이지 조회
SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM ORDERS_DETAIL a
WHERE PROD_ID = 'PP1'
ORDER BY ORD_AMT DESC
OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY;
--2번째 페이지 조회
(20 ROW를 추출하여 앞의 10 ROW는 버린다.)
SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT
FROM ORDERS_DETAIL a
WHERE PROD_ID = 'PP1'
ORDER BY ORD_AMT DESC
OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY;
CREATE INDEX ORDERS_DETAIL_X01 ON
ORDERS_DETAIL (PROD_ID, ORD_AMT);
Keyset Pagination
뒤쪽 페이지 조회 시에도 BLOCK I/O 일정
23 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
5. 페이징 쿼리 최적화
5.5 웹화면 페이징 쿼리(3)
○ 전체 건수와 첫 페이지의 데이터를 함께 구하기 위한 쿼리이다.
○ ORACLE에서는 ROWNUM seudo column을 사용 개선할 수 있으나, PostgreSQL에서는 OFFSET 절을 사용한다.
VI. SQL 성능 개선 방안
SELECT *
FROM (SELECT COUNT(*) OVER () AS CNT
, D.ORD_LINE_NO, D.ORD_NO
, D.PROD_ID, O.COMMENT
FROM ORDERS_DETAIL D, ORDERS O
WHERE D.ORD_NO = O.ORD_NO
AND D.ORD_NO BETWEEN 1000 AND 100000
ORDER BY D.ORD_NO, D.PROD_ID ) A
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;
SELECT *
FROM ( SELECT COUNT(*) OVER () AS CNT
,ROW_NUMBER() OVER
(ORDER BY D.ORD_NO, D.PROD_ID) AS RNUM
,D.ORD_LINE_NO, D.ORD_NO
, D.PROD_ID, O.COMMENT
FROM ORDERS_DETAIL D, ORDERS O
WHERE D.ORD_NO = O.ORD_NO
AND D.ORD_NO BETWEEN 1000
AND 100000) A
WHERE RNUM BETWEEN 21 AND 30
1.84 sec
-> 0.48sec
WINDOW 함수 2개 수행
24 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
6. 검색 최적화
6.1 중간 값 검색
○ 지명 등에서 중간 값을 검색해야 하면 FULL TABLE SCAN을 피할 수 없다.
(영등포구를 찾기 위해 ‘%등포%’로 검색)
○ 검색창에서 사용자에게 중간 값을 찾을 경우에는 ‘%’를 입력하도록 규칙을 정하고 아래와 같이 쿼리를 작성하면 중간 값 검색을
하지 않는 경우는 빠른 성능을 확보할 수 있다.
인덱스 컬럼 : COMMENT
VI. SQL 성능 개선 방안
SELECT *
FROM ORDERS_DETAIL
WHERE :'var1' NOT LIKE CONCAT('%',:'var1')
AND COMMENT LIKE CONCAT(:'var1','%')
UNION ALL
SELECT *
FROM ORDERS_DETAIL
WHERE :'var1' LIKE CONCAT('%',:'var1')
AND COMMENT LIKE :'var1';
검색창에 %로 시작하는 문자가 입력되면 UNION ALL
아래 부분만 수행되며 FULL TABLE SCAN 발생
검색창에 % 이외의 문자로 시작하면 UNION ALL 위 부
분만 수행되며 INDEX SCAN 발생
SELECT *
FROM ORDERS_DETAIL
WHERE COMMENT LIKE CONCAT(‘%’,:'var1‘,’%’)
쿼리 수정
25 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
7. 분석함수를 이용한 성능개선
7.1 상관 서브쿼리
○ 상관쿼리는 서브쿼리 내에 메인 쿼리와의 조인절이 있다.
○ 개선 전 쿼리는 ORDERS_DETAIL을 2번 액세스하는 비효율이 있다.
○ 개선 후 쿼리는 WINDOW FUNCTION을 사용해서 ORDERS_DETAIL을 1회만 액세스 하도록 했다.
VI. SQL 성능 개선 방안
SELECT ORD_NO, ORD_AMT, CUST_ID
FROM (SELECT D.ORD_NO, D.ORD_AMT, O.CUST_ID
,CASE D.ORD_NO WHEN MAX(D.ORD_NO) OVER
(PARTITION BY D.ORD_NO) THEN 'X' END M_ORD_NO
FROM ORDERS_DETAIL D, ORDERS O
WHERE D.ORD_NO = O.ORD_NO
AND D.ORD_AMT > 9900000 ) Z
WHERE M_ORD_NO IS NOT NULL
SELECT D.ORD_NO, D.ORD_AMT, O.CUST_ID
FROM ORDERS_DETAIL D, ORDERS O
WHERE D.ORD_NO = O.ORD_NO
AND D.ORD_NO = (SELECT MAX(ORD_NO)
FROM ORDERS_DETAIL D
WHERE D.ORD_NO = O.ORD_NO)
AND D.ORD_AMT > 9900000
쿼리 수정
291 sec
-> 1.17sec
26 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
7. 분석함수를 이용한 성능개선
7.2 비상관 서브쿼리
○ 개선 전 쿼리는 ORDERS_DETAIL을 2번 액세스하는 비효율이 있다.
○ 개선 후 쿼리는 WINDOW FUNCTION을 사용해서 ORDERS_DETAIL을 1회만 액세스 하도록 했다.
VI. SQL 성능 개선 방안
SELECT O.ORD_NO, O.COMMENT, v.ORD_AMT
FROM ORDERS O,
(SELECT ORD_NO, SUM(ORD_AMT) ORD_AMT
, MAX(SUM(ORD_AMT)) OVER () MAX_S
FROM ORDERS_DETAIL
WHERE COMMENT LIKE '9%‘
GROUP BY ORD_NO) V
WHERE O.ORD_NO = v.ORD_NO
AND v.ORD_AMT = v.MAX_S
WITH v AS NOT MATERIALIZED (
SELECT ORD_NO, SUM(ORD_AMT) AS ORD_AMT
FROM ORDERS_DETAIL D
WHERE COMMENT LIKE '9%‘
GROUP BY ORD_NO)
SELECT O.ORD_NO, O.COMMENT, v.ORD_AMT
FROM ORDERS O, v
WHERE O.ORD_NO = v.ORD_NO
AND v.ORD_AMT = (SELECT MAX(v.ORD_AMT)
FROM v)
쿼리 수정
2.2 sec ->
0.9sec
2회 ACCESS
1회 ACCESS
27 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
8. 서브쿼리를 조인으로 변경
8.1 NOT IN 절 개선
○ NOT IN 절은 subquery collapse가 동작하지 않는다.
○ 성능이 좋지 않을 경우 NOT EXISTS 절을 사용하여 Anti Join을 유도한다.
VI. SQL 성능 개선 방안
SELECT A.ORD_LINE_NO, A.ORD_AMT
FROM ORDERS_DETAIL A
WHERE NOT EXISTS (SELECT 1
FROM ORDERS B
WHERE A.ORD_NO = B.ORD_NO
AND ORD_DATE > '20181220')
SELECT A.ORD_LINE_NO, A.ORD_AMT
FROM ORDERS_DETAIL A
WHERE A.ORD_NO NOT IN (SELECT B.ORD_NO
FROM ORDERS B
WHERE ORD_DATE > '20181220')
쿼리 수정
180 sec -> 4.4 sec
NOT IN 절이 filter 조건으로
반복 수행됨
28 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
8. 서브쿼리를 조인으로 변경
8.2 IN 절 개선
○ PostgreSQL은 Complex Subquery인 경우 subquery collapse가 동작하지 않는다.
○ 아래와 같이 조인 으로 쿼리를 변환한다.
VI. SQL 성능 개선 방안
SELECT ORD_NO, CUST_ID, ORD_DATE, ORD_AMT
FROM ORDERS O,
(SELECT C.CUST_ID, MAX(C.ORD_DATE) M_ORD_DATE
FROM ORDERS C
WHERE C.ORD_NO BETWEEN 137000 AND 138000
GROUP BY C.CUST_ID) C
WHERE O.CUST_ID = C.CUST_ID
AND O.ORD_DATE = C.M_ORD_DATE
AND O.ORD_NO BETWEEN 137000 AND 138000
SELECT ORD_NO, CUST_ID, ORD_DATE, ORD_AMT
FROM ORDERS O
WHERE ORD_DATE IN (SELECT MAX(C.ORD_DATE)
FROM ORDERS C
WHERE C.CUST_ID = O.CUST_ID)
AND O.ORD_NO BETWEEN 137000 AND 138000
쿼리 수정
1.79 sec
-> 0.04 sec
서브쿼리 1000회 수행
29 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
8. 서브쿼리를 조인으로 변경
8.3 서브쿼리를 조인으로 변경
○ Oracle/SQL Server의 부분범위 처리 개념 사용 불가능
○ PostgreSQL은 쿼리 수행 후 전체 결과를 클라이언트로 전송하므로 조인 수행하도록 해야 속도 향상
SELECT A.CUST_ID, A.COMMENT
FROM ORDERS A, ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
AND A.ORD_DATE LIKE '201902%‘
GROUP BY A.ORD_NO
HAVING SUM(B.ORD_AMT) > 7900000;
SELECT A.CUST_ID, A.COMMENT
FROM ORDERS A
WHERE A.ORD_DATE LIKE '201902%’
AND EXISTS (SELECT 1
FROM ORDERS_DETAIL B
WHERE A.ORD_NO = B.ORD_NO
GROUP BY B.ORD_NO
HAVING SUM(B.ORD_AMT) > 7900000);
서브쿼리 제거
5.30 sec -> 1.57 sec
서브쿼리 76720회 반복 수행
0.066*76720=5063
VI. SQL 성능 개선 방안
30 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
8. 서브쿼리를 조인으로 변경
8.4 조인 방법 개선
○ 아래 쿼리는 Anti-Join 으로 수행하면서 ORD_NO가 서브쿼리 집합으로 파고들지 못했다.
○ ORD_NO가 서브쿼리 집합으로 파고들도록 조인방법을 힌트로 바꾸었다.
VI. SQL 성능 개선 방안
/*+ NestLoop(o d p) */
좌측과 동일 쿼리
SELECT O.ORD_NO, O.ORD_DATE, O.COMMENT
FROM ORDERS O
WHERE O.CUST_ID = 'C0‘
AND ORD_NO < 137199
AND NOT EXISTS (SELECT 1
FROM ORDERS_DETAIL D, PROD P
WHERE D.ORD_NO = O.ORD_NO
AND D.PROD_ID = P.PROD_ID
AND D.ORD_LINE_NO BETWEEN
136844 AND 999999 )
Join Method 변경
0.44 sec
-> 0.09sec
Access하는 인덱스 변경됨
31 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
9. 사전에 GROUP BY 수행
9.1 GROUPING SETS 개선
○ 개선 전에는 테이블 전체를 읽은 후, CUST_ID, ORD_DATE 각각 GROUPING 한다.
○ 개선 후에는 (CUST_ID, ORD_DATE)로 GROUP BY하여 건수를 줄인 상태에서 CUST_ID, ORD_DATE 각각 GROUPING한다.
VI. SQL 성능 개선 방안
SELECT CUST_ID, ORD_DATE
, SUM(S_AMT)/SUM(CNT) AS AVG_AMT
FROM (
SELECT CUST_ID, ORD_DATE
, SUM(ORD_AMT) S_AMT
, COUNT(ORD_AMT) CNT
FROM ORDERS
GROUP BY CUST_ID, ORD_DATE
) A
GROUP BY GROUPING SETS (CUST_ID, ORD_DATE)
SELECT CUST_ID, ORD_DATE, AVG(ORD_AMT) AS AVG_AMT
FROM ORDERS
GROUP BY GROUPING SETS (CUST_ID, ORD_DATE)
쿼리 수정
0.36 sec -> 0.25 sec
32 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
9. 사전에 GROUP BY 수행
9.2 Group By Placement
○ 조인을 수행하기 전에 Group By를 먼저 수행하여 건수를 줄인 후 조인을 수행함으로서, 조인 건수를 획기적으로 감소시킨다.
○ OLTP 보다는 DW의 대용량 시스템에서 사용할 경우 성능 향상을 극대화 할 수 있다.
VI. SQL 성능 개선 방안
SELECT P.PROD_NM, SUM(D.S_AMT)
FROM PROD P,
(SELECT PROD_ID, SUM(ORD_AMT) AS S_AMT
FROM ORDERS_DETAIL
WHERE PROD_ID IN ('PP0', 'PP1')
GROUP BY PROD_ID) D
WHERE P.PROD_ID = D.PROD_ID
GROUP BY PROD_NM
SELECT P.PROD_NM, SUM(D.ORD_AMT)
FROM PROD P, ORDERS_DETAIL D
WHERE P.PROD_ID = D.PROD_ID
AND D.PROD_ID IN ( 'PP0', 'PP1')
GROUP BY PROD_NM
쿼리 수정
2.4 sec
-> 1.6 sec
인라인 뷰
33 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
9. 사전에 GROUP BY 수행
9.3 대용량 테이블 집계
○ 아래 쿼리는 ORD_DATE 컬럼에 BTREE INDEX 있어도 건수가 많아서 속도향상이 안된다.
○ ORD_DATE를 년월별로 파티션을 구성하면 FULL SCAN 범위를 줄일 수 있다.
○ PostgreSQL의 Block Range Index를 구성해도 파티션과 유사한 성능을 확보할 수 있다.
○ BRIN을 사용하기 위해서는 데이터가 ORD_DATE 컬럼 순으로 INSERT가 되어야 하고, 테이블에 update 가 발생하지 않아야
한다.
VI. SQL 성능 개선 방안
--ORDERS 는 1960년부터 2019년 10월까지의 1억 건 보유
SELECT CUST_ID, COUNT(*)
FROM ORDERS
WHERE ORD_DATE BETWEEN '20190101‘
AND '20191031‘
GROUP BY CUST_ID;
BRIN 생성
193 msec
-> 20 msec
--Block Range Index 생성
CREATE INDEX ORDER_DETAIL_X01 ON ORDERS_DETAIL
USING BRIN (ORD_DATE);
34 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
9. 사전에 GROUP BY 수행
9.4 HashAggregate 유도
○ ORD_DATE별로 ORD_AMT가 가장 큰 주문정보를 출력하는 쿼리이다.
○ 수정 후에는 ORD_DATE 를 기준으로 HashAggregate 수행하여 작업대상 건수를 363건으로 줄인 후 Sorting을 수행한다.
○ 인라인 뷰에서 PK인 ORD_NO를 추출 후 ORDERS테이블에 PK index scan으로 access하여 원하는 컬럼을 출력하였다.
SELECT ORD_DATE, ORD_AMT, CUST_ID
FROM (SELECT ROW_NUMBER() OVER (PARTITION BY
ORD_DATE ORDER BY ORD_AMT DESC) AS RN
,ORD_AMT, ORD_DATE, CUST_ID
FROM ORDERS) A
WHERE RN = 1
1.4 sec -> 0.12 sec
SELECT B.ORD_DATE, B.ORD_AMT, B.CUST_ID
FROM ( SELECT ORD_DATE
, (MAX(ARRAY[ORD_AMT, ORD_NO]))[2] AS ORD_NO
FROM ORDERS
GROUP BY ORD_DATE) A
JOIN ORDERS B
ON A.ORD_NO = B.ORD_NO;
temp table 발생
VI. SQL 성능 개선 방안
35 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
10. Filter 조건 이동하기
10.1 Complex View에 Filter 밀어 넣기
○ PostgreSQL은 Complex View에 Filter 밀어 넣기가 제한적으로 동작한다.
○ 아래 예제에서는 salary 컬럼에 인덱스가 존재해도 table full scan이 발생하였다.
employee 테이블 인덱스 컬럼 : salary
VI. SQL 성능 개선 방안
SELECT d.department_id, d.department_name, max_sal
FROM department d,
(SELECT e.department_id, MAX (e.salary) max_sal
FROM employee e
WHERE e.salary > 15200
GROUP BY e.department_id) e1
WHERE d.department_id = e1.department_id;
SELECT d.department_id, d.department_name, max_sal
FROM department d,
(SELECT e.department_id, MAX (e.salary) max_sal
FROM employee e
GROUP BY e.department_id) e1
WHERE d.department_id = e1.department_id
AND e1.max_sal > 15200
쿼리 수정
0.12 sec
-> 0.02 sec
Table full scan 발생 index scan 발생
36 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
11. OR 조건을 UNION 으로 변경
○ 아래와 같이 where 절에서 OR 조건으로 분기되는 컬럼이 다른 경우 UNION 을 사용하여 변환할 수 있다.
○ OR로 분기된 집합간에 중복데이터가 없는 것이 확실하다면 UNION ALL을 사용한다.
VI. SQL 성능 개선 방안
SELECT o.ord_no, o.comment, d.ord_line_no, d.comment
FROM orders o, orders_detail d
WHERE o.ord_no = d.ord_no
AND d.ord_line_no in (136844,136845)
UNION
SELECT o.ord_no, o.comment, d.ord_line_no, d.comment
FROM orders o, orders_detail d
WHERE o.ord_no = d.ord_no
AND o.ord_date in ('20190817', '20190816')
SELECT o.ord_no, o.comment, d.ord_line_no, d.comment
FROM orders o, orders_detail d
WHERE o.ord_no = d.ord_no
AND (d.ord_line_no in (136844,136845)
OR o.ord_date in ('20190817', '20190816'))
쿼리 수정
6.1 sec ->
2.1 sec
index scan 발생
조인 후 OR 조건을 Filter 적용
37 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
12. 인덱스 액세스 범위 최소화
VI. SQL 성능 개선 방안
Nested Loop (actual time=0.096..1.069 rows=2740 loops=1)
Buffers: shared hit=83
-> HashAggregate (actual time=0.036..0.039 rows=8 loops=1)
Group Key: to_char((('2019-08-25'::date + (generate_series(1,
8))))::timestamp with time zone, 'YYYYMMDD'::text)
-> Result (actual time=0.025..0.030 rows=8 loops=1)
-> ProjectSet (actual time=0.002..0.003 rows=8 loops=1)
-> Result (actual time=0.001..0.001 rows=1 loops=1)
-> Index Scan using orders_x01 on orders
(actual time=0.015..0.105 rows=342 loops=8)
Index Cond: (((ord_date)::text = (to_char((('2019-08-25'::date +
(generate_series(1, 8))))::timestamp with time zone,
'YYYYMMDD'::text))) AND ((cust_id)::text = 'C9'::text))
Buffers: shared hit=83
Index Scan using orders_x01 on orders
(actual time=2.045..9.163 rows=2740 loops=1)
Index Cond: (((ord_date)::text >= '20190826'::text)
AND ((ord_date)::text <= '20190902'::text)
AND ((cust_id)::text = 'C9'::text))
Buffers: shared hit=125
○ 인덱스 선행컬럼이 BETWEEN 조건이면, 인덱스 후행 컬럼은 ACCESS 조건으로 사용되지 않는다.
○ BETWEEN 조건 대신에 IN 조건을 사용하면, 인덱스 후행 컬럼도 ACCESS 조건으로 사용되어 BLOCK I/O를 줄일 수 있다.
인덱스 컬럼 : ORD_DATE, CUST_ID
SELECT COUNT(*)
FROM ORDERS
WHERE CUST_ID = ‘C9’
AND ORD_DATE
IN (SELECT TO_CHAR(DATE '20190826'-
1+generate_series(1,DATE '20190902'-'20190826'+1)
, YYYYMMDD')
)
SELECT COUNT(*)
FROM ORDERS
WHERE CUST_ID = ‘C9’
AND ORD_DATE BETWEEN '20190826' AND '20190902'
쿼리 수정
9.27 msec ->
1.22 msec
38 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
13. CTE 활용
13.1 최대값/최소값 추출
○ 특정 컬럼의 DISTINCT 값이나, 특정 컬럼 기준으로 MIN/MAX 값을 출력
인덱스 컬럼 : PROD_ID
VI. SQL 성능 개선 방안
SELECT PROD_ID, MAX(ORD_NO)
FROM ORDERS_DETAIL
GROUP BY PROD_ID
ORDER BY 1;
TABLE FULL SCAN 발생
3.2 sec  0.2 msec
WITH RECURSIVE W(N) AS (
SELECT MIN(PROD_ID)
FROM ORDERS_DETAIL
UNION ALL
SELECT (SELECT PROD_ID
FROM ORDERS_DETAIL
WHERE PROD_ID > N
ORDER BY PROD_ID LIMIT 1) PROD_ID
FROM W
WHERE N IS NOT NULL
)
SELECT N, (SELECT MAX(B.ORD_NO)
FROM ORDERS_DETAIL B
WHERE B.PROD_ID = A.N)
FROM W A
WHERE N IS NOT NULL;
39 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
ORD_NO CUST_ID COMMENT ORD_DATE ORD_AMT
8000001C400 QUICK DELIVERY 20200101 30
8000002C200 NO SIGNABURE 20200101 50
8000003C300 ETC 20200102 40
8000004C400 BLACK 20200103 200
8000005C500 20200103 500
8000006C600 20200104 30
8000023C400 20201018 450
8000024C500 NO SIGNABURE 20201019 80
8000025C600 ETC 20201019 250
8000026C200 BLACK 20201020 400
8000027C300 NO DELIVERY 20201021 5000
8000028C100 XX LARGE 20201022 990
Primary Key 시간순으로 데이터 입력되며 update는 발생하지 않음
13. CTE 활용
13.2 로그데이터 추출
VI. SQL 성능 개선 방안
……………………….
40 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
13. CTE 활용
13.2 로그데이터 추출(1)
○ 데이터가 Primary Key 컬럼 순으로 입력되고, ORD_DATE 컬럼은 UPDATE가 발생하지 않는 테이블이다.
○ 아래 예제에서 ORD_DATE에 인덱스가 없는 경우, 다른 DBMS 에서는 파티션 구성 외에는 개선 방법이 없다.
VI. SQL 성능 개선 방안
SELECT *
FROM ORDERS2
WHERE ORD_DATE BETWEEN DATE '20201018'
AND DATE '20201022';
WITH RECURSIVE
w_min (min, max, middle, level) AS (
SELECT min(ord_no), max(ord_no), (min(ord_no)+max(ord_no))/2, 0
FROM orders2
UNION ALL
SELECT v.min, v.max, (v.min + v.max)/2, w_min.level+1
FROM w_min, LATERAL (
SELECT ORD_NO, ORD_DATE
FROM orders2 AS e
WHERE e.ord_no >= w_min.middle
ORDER BY e.ord_no
FETCH FIRST ROW ONLY
) AS e
,LATERAL (VALUES (
CASE WHEN e.ord_date <= date '20201018' THEN e.ord_no ELSE w_min.min END,
CASE WHEN e.ord_date <= date '20201018' THEN w_min.max ELSE e.ord_no END
)
) AS v (min, max)
WHERE (v.min + v.max)/2 NOT IN (v.min, v.max)
),
w_max (min, max, middle, level) AS (
SELECT min(ord_no), max(ord_no), (min(ord_no)+max(ord_no))/2, 0
FROM orders2
UNION ALL
SELECT v.min, v.max, (v.min + v.max)/2, w_max.level+1
FROM w_max, LATERAL (
SELECT ORD_NO, ORD_DATE
FROM orders2 AS e
WHERE e.ord_no >= w_max.middle
ORDER BY e.ord_no
FETCH FIRST ROW ONLY
) AS e
,LATERAL (VALUES (
CASE WHEN e.ord_date <= date '20201022' THEN e.ord_no ELSE w_max.min END,
CASE WHEN e.ord_date <= date '20201022' THEN w_max.max ELSE e.ord_no END
)
) AS v (min, max)
WHERE (v.min + v.max)/2 NOT IN (v.min, v.max)
)
SELECT *
FROM orders2
WHERE ORD_NO >= (SELECT middle FROM w_min ORDER BY level DESC FETCH FIRST
ROWS ONLY)
AND ORD_NO <= (SELECT middle FROM w_max ORDER BY level DESC FETCH FIRST
ROWS ONLY)
;
쿼리 수정
978 msec  2 msec
41 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
13. CTE 활용
13.2 로그데이터 추출(2)
○ 아래는 개선 후 실행계획의 일부분이다.
○ 인덱스의 1번째와 마지막 블록만 access해서 ORD_NO를 찾는다.
○ ORD_NO의 middle 값을 이용해 recursive query가 중단되도록 한다.
○ block I/O는 93,000 에서 204로 줄었다.
VI. SQL 성능 개선 방안
인덱스 1번째 블록만 access
인덱스 마지막 블록만 access
42 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
참고서적
PostgreSQL 9.6 성능이야기 시연아카데미
SQL Server 튜닝 원리와 해법 b2en
43 I make PostgreSQL database faster and more reliable with sql tuning and data modeling
감사합니다
All rights not reserved. Any part of this material may be reproduced, stored in a retrieval system, or transmitted in any form or by any
means, without any permission from me.
I will not be held liable for any damage caused or alleged to have been caused directly or indirectly by applying the skills described in
this presentation.
I would appreciate it if you could make your database server run faster with the technique in this material.

More Related Content

What's hot

[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
[Pgday.Seoul 2021] 2. Porting Oracle UDF and OptimizationPgDay.Seoul
 
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱PgDay.Seoul
 
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오PgDay.Seoul
 
MySQL Performance Tuning: Top 10 Tips
MySQL Performance Tuning: Top 10 TipsMySQL Performance Tuning: Top 10 Tips
MySQL Performance Tuning: Top 10 TipsOSSCube
 
Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]
Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]
Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]MongoDB
 
[Pgday.Seoul 2018] 이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
[Pgday.Seoul 2018]  이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG[Pgday.Seoul 2018]  이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
[Pgday.Seoul 2018] 이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PGPgDay.Seoul
 
Introduction to MongoDB
Introduction to MongoDBIntroduction to MongoDB
Introduction to MongoDBMongoDB
 
Pgday bdr 천정대
Pgday bdr 천정대Pgday bdr 천정대
Pgday bdr 천정대PgDay.Seoul
 
PostgreSQL Performance Tuning
PostgreSQL Performance TuningPostgreSQL Performance Tuning
PostgreSQL Performance Tuningelliando dias
 
PostgreSQLの関数属性を知ろう
PostgreSQLの関数属性を知ろうPostgreSQLの関数属性を知ろう
PostgreSQLの関数属性を知ろうkasaharatt
 
まずやっとくPostgreSQLチューニング
まずやっとくPostgreSQLチューニングまずやっとくPostgreSQLチューニング
まずやっとくPostgreSQLチューニングKosuke Kida
 
[pgday.Seoul 2022] PostgreSQL with Google Cloud
[pgday.Seoul 2022] PostgreSQL with Google Cloud[pgday.Seoul 2022] PostgreSQL with Google Cloud
[pgday.Seoul 2022] PostgreSQL with Google CloudPgDay.Seoul
 
Patroni - HA PostgreSQL made easy
Patroni - HA PostgreSQL made easyPatroni - HA PostgreSQL made easy
Patroni - HA PostgreSQL made easyAlexander Kukushkin
 
PostgreSQL, performance for queries with grouping
PostgreSQL, performance for queries with groupingPostgreSQL, performance for queries with grouping
PostgreSQL, performance for queries with groupingAlexey Bashtanov
 
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱PgDay.Seoul
 
問合せ最適化インサイド
問合せ最適化インサイド問合せ最適化インサイド
問合せ最適化インサイドTakahiro Itagaki
 
How the Postgres Query Optimizer Works
How the Postgres Query Optimizer WorksHow the Postgres Query Optimizer Works
How the Postgres Query Optimizer WorksEDB
 
Deep dive into PostgreSQL statistics.
Deep dive into PostgreSQL statistics.Deep dive into PostgreSQL statistics.
Deep dive into PostgreSQL statistics.Alexey Lesovsky
 

What's hot (20)

[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
[Pgday.Seoul 2021] 2. Porting Oracle UDF and Optimization
 
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
[Pgday.Seoul 2017] 2. PostgreSQL을 위한 리눅스 커널 최적화 - 김상욱
 
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
[Pgday.Seoul 2017] 3. PostgreSQL WAL Buffers, Clog Buffers Deep Dive - 이근오
 
MySQL Performance Tuning: Top 10 Tips
MySQL Performance Tuning: Top 10 TipsMySQL Performance Tuning: Top 10 Tips
MySQL Performance Tuning: Top 10 Tips
 
Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]
Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]
Naver속도의, 속도에 의한, 속도를 위한 몽고DB (네이버 컨텐츠검색과 몽고DB) [Naver]
 
[Pgday.Seoul 2018] 이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
[Pgday.Seoul 2018]  이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG[Pgday.Seoul 2018]  이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
[Pgday.Seoul 2018] 이기종 DB에서 PostgreSQL로의 Migration을 위한 DB2PG
 
Introduction to MongoDB
Introduction to MongoDBIntroduction to MongoDB
Introduction to MongoDB
 
Pgday bdr 천정대
Pgday bdr 천정대Pgday bdr 천정대
Pgday bdr 천정대
 
Indexes in postgres
Indexes in postgresIndexes in postgres
Indexes in postgres
 
PostgreSQL Performance Tuning
PostgreSQL Performance TuningPostgreSQL Performance Tuning
PostgreSQL Performance Tuning
 
PostgreSQLの関数属性を知ろう
PostgreSQLの関数属性を知ろうPostgreSQLの関数属性を知ろう
PostgreSQLの関数属性を知ろう
 
まずやっとくPostgreSQLチューニング
まずやっとくPostgreSQLチューニングまずやっとくPostgreSQLチューニング
まずやっとくPostgreSQLチューニング
 
PostgreSQL and RAM usage
PostgreSQL and RAM usagePostgreSQL and RAM usage
PostgreSQL and RAM usage
 
[pgday.Seoul 2022] PostgreSQL with Google Cloud
[pgday.Seoul 2022] PostgreSQL with Google Cloud[pgday.Seoul 2022] PostgreSQL with Google Cloud
[pgday.Seoul 2022] PostgreSQL with Google Cloud
 
Patroni - HA PostgreSQL made easy
Patroni - HA PostgreSQL made easyPatroni - HA PostgreSQL made easy
Patroni - HA PostgreSQL made easy
 
PostgreSQL, performance for queries with grouping
PostgreSQL, performance for queries with groupingPostgreSQL, performance for queries with grouping
PostgreSQL, performance for queries with grouping
 
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
[pgday.Seoul 2022] POSTGRES 테스트코드로 기여하기 - 이동욱
 
問合せ最適化インサイド
問合せ最適化インサイド問合せ最適化インサイド
問合せ最適化インサイド
 
How the Postgres Query Optimizer Works
How the Postgres Query Optimizer WorksHow the Postgres Query Optimizer Works
How the Postgres Query Optimizer Works
 
Deep dive into PostgreSQL statistics.
Deep dive into PostgreSQL statistics.Deep dive into PostgreSQL statistics.
Deep dive into PostgreSQL statistics.
 

Similar to [Pgday.Seoul 2020] SQL Tuning

[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3Seok-joon Yun
 
배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle
배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle
배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle엑셈
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQLJAEGEUN YU
 
From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQLI Goo Lee
 
효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차희동 강
 
MySQL_MariaDB로의_전환_기술요소-202212.pptx
MySQL_MariaDB로의_전환_기술요소-202212.pptxMySQL_MariaDB로의_전환_기술요소-202212.pptx
MySQL_MariaDB로의_전환_기술요소-202212.pptxNeoClova
 
효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차희동 강
 
MySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxMySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxNeoClova
 
MariaDB 마이그레이션 - 네오클로바
MariaDB 마이그레이션 - 네오클로바MariaDB 마이그레이션 - 네오클로바
MariaDB 마이그레이션 - 네오클로바NeoClova
 
[Pgday.Seoul 2019] Advanced FDW
[Pgday.Seoul 2019] Advanced FDW[Pgday.Seoul 2019] Advanced FDW
[Pgday.Seoul 2019] Advanced FDWPgDay.Seoul
 
제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancements제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancementsbeamofhope
 
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드cranbe95
 
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기Kenu, GwangNam Heo
 
SPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracle
SPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracleSPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracle
SPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracle엑셈
 
효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차희동 강
 
Laravel 로 배우는 서버사이드 #4
Laravel 로 배우는 서버사이드 #4Laravel 로 배우는 서버사이드 #4
Laravel 로 배우는 서버사이드 #4성일 한
 
SQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracleSQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracle엑셈
 
My sql tde kr_v1.0
My sql tde kr_v1.0My sql tde kr_v1.0
My sql tde kr_v1.0Sumi Ryu
 
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4Seok-joon Yun
 
파이썬 데이터베이스 연결 2탄
파이썬 데이터베이스 연결 2탄파이썬 데이터베이스 연결 2탄
파이썬 데이터베이스 연결 2탄SeongHyun Ahn
 

Similar to [Pgday.Seoul 2020] SQL Tuning (20)

[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
[2015-06-26] Oracle 성능 최적화 및 품질 고도화 3
 
배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle
배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle
배치 프로그램에서 튜닝대상 SQL 추출하기_Wh oracle
 
Fundamentals of Oracle SQL
Fundamentals of Oracle SQLFundamentals of Oracle SQL
Fundamentals of Oracle SQL
 
From MSSQL to MySQL
From MSSQL to MySQLFrom MSSQL to MySQL
From MSSQL to MySQL
 
효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차효율적인Sql작성방법 2주차
효율적인Sql작성방법 2주차
 
MySQL_MariaDB로의_전환_기술요소-202212.pptx
MySQL_MariaDB로의_전환_기술요소-202212.pptxMySQL_MariaDB로의_전환_기술요소-202212.pptx
MySQL_MariaDB로의_전환_기술요소-202212.pptx
 
효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차효율적인 SQL 작성방법 1주차
효율적인 SQL 작성방법 1주차
 
MySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptxMySQL_MariaDB-성능개선-202201.pptx
MySQL_MariaDB-성능개선-202201.pptx
 
MariaDB 마이그레이션 - 네오클로바
MariaDB 마이그레이션 - 네오클로바MariaDB 마이그레이션 - 네오클로바
MariaDB 마이그레이션 - 네오클로바
 
[Pgday.Seoul 2019] Advanced FDW
[Pgday.Seoul 2019] Advanced FDW[Pgday.Seoul 2019] Advanced FDW
[Pgday.Seoul 2019] Advanced FDW
 
제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancements제1회 Tech Net Sql Server 2005 T Sql Enhancements
제1회 Tech Net Sql Server 2005 T Sql Enhancements
 
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
Ndc2011 성능 향상을_위한_데이터베이스_아키텍쳐_구축_및_개발_가이드
 
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
ecdevday8 웹개발자의 약한고리 SQL 뛰어넘기
 
SPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracle
SPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracleSPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracle
SPA(SQL Performance Analyze)를 이용한 통계 정보 수집_Wh oracle
 
효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차효율적인Sql작성방법 3주차
효율적인Sql작성방법 3주차
 
Laravel 로 배우는 서버사이드 #4
Laravel 로 배우는 서버사이드 #4Laravel 로 배우는 서버사이드 #4
Laravel 로 배우는 서버사이드 #4
 
SQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracleSQL Profile을 이용한 SQL Plan 변경_Wh oracle
SQL Profile을 이용한 SQL Plan 변경_Wh oracle
 
My sql tde kr_v1.0
My sql tde kr_v1.0My sql tde kr_v1.0
My sql tde kr_v1.0
 
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
[2015 07-06-윤석준] Oracle 성능 최적화 및 품질 고도화 4
 
파이썬 데이터베이스 연결 2탄
파이썬 데이터베이스 연결 2탄파이썬 데이터베이스 연결 2탄
파이썬 데이터베이스 연결 2탄
 

More from PgDay.Seoul

[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기PgDay.Seoul
 
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기PgDay.Seoul
 
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스PgDay.Seoul
 
[Pgday.Seoul 2018] PostgreSQL 11 새 기능 소개
[Pgday.Seoul 2018]  PostgreSQL 11 새 기능 소개[Pgday.Seoul 2018]  PostgreSQL 11 새 기능 소개
[Pgday.Seoul 2018] PostgreSQL 11 새 기능 소개PgDay.Seoul
 
[Pgday.Seoul 2018] PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
[Pgday.Seoul 2018]  PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha[Pgday.Seoul 2018]  PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
[Pgday.Seoul 2018] PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposhaPgDay.Seoul
 
[Pgday.Seoul 2018] PostgreSQL Authentication with FreeIPA
[Pgday.Seoul 2018]  PostgreSQL Authentication with FreeIPA[Pgday.Seoul 2018]  PostgreSQL Authentication with FreeIPA
[Pgday.Seoul 2018] PostgreSQL Authentication with FreeIPAPgDay.Seoul
 
[Pgday.Seoul 2018] AWS Cloud 환경에서 PostgreSQL 구축하기
[Pgday.Seoul 2018]  AWS Cloud 환경에서 PostgreSQL 구축하기[Pgday.Seoul 2018]  AWS Cloud 환경에서 PostgreSQL 구축하기
[Pgday.Seoul 2018] AWS Cloud 환경에서 PostgreSQL 구축하기PgDay.Seoul
 
[Pgday.Seoul 2018] Greenplum의 노드 분산 설계
[Pgday.Seoul 2018]  Greenplum의 노드 분산 설계[Pgday.Seoul 2018]  Greenplum의 노드 분산 설계
[Pgday.Seoul 2018] Greenplum의 노드 분산 설계PgDay.Seoul
 
[Pgday.Seoul 2018] replacing oracle with edb postgres
[Pgday.Seoul 2018] replacing oracle with edb postgres[Pgday.Seoul 2018] replacing oracle with edb postgres
[Pgday.Seoul 2018] replacing oracle with edb postgresPgDay.Seoul
 
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우PgDay.Seoul
 
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종PgDay.Seoul
 
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진PgDay.Seoul
 
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명PgDay.Seoul
 
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기PgDay.Seoul
 
PostgreSQL 9.6 새 기능 소개
PostgreSQL 9.6 새 기능 소개PostgreSQL 9.6 새 기능 소개
PostgreSQL 9.6 새 기능 소개PgDay.Seoul
 
pg_hba.conf 이야기
pg_hba.conf 이야기pg_hba.conf 이야기
pg_hba.conf 이야기PgDay.Seoul
 
Pg report 20161010_02
Pg report 20161010_02Pg report 20161010_02
Pg report 20161010_02PgDay.Seoul
 

More from PgDay.Seoul (17)

[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
[Pgday.Seoul 2020] 포스트그레스큐엘 자국어화 이야기
 
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
[Pgday.Seoul 2019] AppOS 고성능 I/O 확장 모듈로 성능 10배 향상시키기
 
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
[Pgday.Seoul 2019] Citus를 이용한 분산 데이터베이스
 
[Pgday.Seoul 2018] PostgreSQL 11 새 기능 소개
[Pgday.Seoul 2018]  PostgreSQL 11 새 기능 소개[Pgday.Seoul 2018]  PostgreSQL 11 새 기능 소개
[Pgday.Seoul 2018] PostgreSQL 11 새 기능 소개
 
[Pgday.Seoul 2018] PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
[Pgday.Seoul 2018]  PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha[Pgday.Seoul 2018]  PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
[Pgday.Seoul 2018] PostgreSQL 성능을 위해 개발된 라이브러리 OS 소개 apposha
 
[Pgday.Seoul 2018] PostgreSQL Authentication with FreeIPA
[Pgday.Seoul 2018]  PostgreSQL Authentication with FreeIPA[Pgday.Seoul 2018]  PostgreSQL Authentication with FreeIPA
[Pgday.Seoul 2018] PostgreSQL Authentication with FreeIPA
 
[Pgday.Seoul 2018] AWS Cloud 환경에서 PostgreSQL 구축하기
[Pgday.Seoul 2018]  AWS Cloud 환경에서 PostgreSQL 구축하기[Pgday.Seoul 2018]  AWS Cloud 환경에서 PostgreSQL 구축하기
[Pgday.Seoul 2018] AWS Cloud 환경에서 PostgreSQL 구축하기
 
[Pgday.Seoul 2018] Greenplum의 노드 분산 설계
[Pgday.Seoul 2018]  Greenplum의 노드 분산 설계[Pgday.Seoul 2018]  Greenplum의 노드 분산 설계
[Pgday.Seoul 2018] Greenplum의 노드 분산 설계
 
[Pgday.Seoul 2018] replacing oracle with edb postgres
[Pgday.Seoul 2018] replacing oracle with edb postgres[Pgday.Seoul 2018] replacing oracle with edb postgres
[Pgday.Seoul 2018] replacing oracle with edb postgres
 
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
[Pgday.Seoul 2017] 6. GIN vs GiST 인덱스 이야기 - 박진우
 
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
[Pgday.Seoul 2017] 5. 테드폴허브(올챙이) PostgreSQL 확장하기 - 조현종
 
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
[Pgday.Seoul 2017] 1. PostGIS의 사례로 본 PostgreSQL 확장 - 장병진
 
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
[Pgday.Seoul 2017] 4. Composite Type/JSON 파라미터를 활용한 TVP구현(with C#, JAVA) - 지현명
 
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
[Pgday.Seoul 2017] 8. PostgreSQL 10 새기능 소개 - 김상기
 
PostgreSQL 9.6 새 기능 소개
PostgreSQL 9.6 새 기능 소개PostgreSQL 9.6 새 기능 소개
PostgreSQL 9.6 새 기능 소개
 
pg_hba.conf 이야기
pg_hba.conf 이야기pg_hba.conf 이야기
pg_hba.conf 이야기
 
Pg report 20161010_02
Pg report 20161010_02Pg report 20161010_02
Pg report 20161010_02
 

[Pgday.Seoul 2020] SQL Tuning

  • 1. PostgreSQL SQL Tuning 유 재 근 mail: naivety1@naver.com PGDay.Seoul.2020
  • 2. 1 I make PostgreSQL database faster and more reliable with sql tuning and data modeling I. 발표자 II. 시스템 성능 저하 요인 III. 시스템 부하 비교 V. 테스트 데이터 VI. SQL 성능 개선 방안 목 차 IV. SQL이 느린 이유
  • 3. 2 I make PostgreSQL database faster and more reliable with sql tuning and data modeling I. 발표자 • 데이터 모델링 • SQL 튜닝 • DAP
  • 4. 3 I make PostgreSQL database faster and more reliable with sql tuning and data modeling II. 시스템 성능 저하 요인 • CPU, Memory, HDD, Network • System Design • Application Design • Data Modeling • SQL 80 ~ 90 % less CPU, less Memory
  • 5. 4 I make PostgreSQL database faster and more reliable with sql tuning and data modeling • The answer is not in the load graph. • The answer is not in the expensive DBMS. • The answer is in your query. • Queries cause load. III. 시스템 부하 비교 0 10 20 30 40 50 60 70 80 90 100 0:00 0:50 1:40 2:30 3:20 4:10 5:00 5:50 6:40 7:30 8:20 9:10 10:00 10:50 11:40 12:30 13:20 14:10 15:00 15:50 16:40 17:30 18:20 19:10 20:00 20:50 21:40 22:30 23:20 CPU load 0 10 20 30 40 50 60 70 80 90 100 0:00 0:50 1:40 2:30 3:20 4:10 5:00 5:50 6:40 7:30 8:20 9:10 10:00 10:50 11:40 12:30 13:20 14:10 15:00 15:50 16:40 17:30 18:20 19:10 20:00 20:50 21:40 22:30 23:20 CPU load BEFORE TUNING AFTER TUNING **시 정보자원 통합인프라 구축 및 시스템 보강 사업
  • 6. 5 I make PostgreSQL database faster and more reliable with sql tuning and data modeling IV. SQL이 느린 이유 • Optimizer algorithm limitations - index page random access cost, heap seq access cost, operator cost - CPU, DISK I/O, Network bandwidth - parsing time - genetic query optimizer, exhaustive-search algorithm • PostgreSQL doesn’t know the data the way you know. • Developers lacking in skills - Common mistakes developers make - Locks - LOOP, WHILE, CURSOR • Inappropriate Data Modelling - Outer Joins, Distinct, Group By, Coalesce - attribute duplication
  • 7. 6 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 테스트 데이터 ERD V. 테스트 데이터
  • 8. 7 I make PostgreSQL database faster and more reliable with sql tuning and data modeling create table ORDERS (ord_no bigint,cust_id varchar(20),comment varchar(100),ord_date varchar(8)); ALTER TABLE ORDERS ADD CONSTRAINT PK_ORDERS PRIMARY KEY(ORD_NO); CREATE TABLE ORDERS_DETAIL(ORD_LINE_NO BIGINT NOT NULL,ORD_NO BIGINT NOT NULL,PROD_ID VARCHAR(10) NOT NULL,COMMENT VARCHAR(100),ORD_AMT BIGINT); ALTER TABLE ORDERS_DETAIL ADD CONSTRAINT PK_ORDERS_DETAIL PRIMARY KEY(ORD_LINE_NO); CREATE INDEX ORDERS_DETAIL_X01 ON ORDERS_DETAIL(ORD_NO, PROD_ID); CREATE INDEX ORDERS_DETAIL_X02 ON ORDERS_DETAIL(ORD_NO, ORD_AMT); CREATE TABLE PROD (PROD_ID VARCHAR(10) NOT NULL,PROD_NM VARCHAR(100) NOT NULL); ALTER TABLE PROD ADD CONSTRAINT PK_PROD PRIMARY KEY(PROD_ID); insert into ORDERS select i as ord_no , 'C'||mod(i,10) as cust_id , lpad('X',10,'Y') as comment , to_char(to_date('20191001','YYYYMMDD')+mod(i,60),'yyyym mdd') as ord_date from generate_series(1,1000000) a(i); INSERT INTO ORDERS_DETAIL SELECT i as ORD_LINE_NO , mod(i,1000000) AS ORD_NO , 'PP'||MOD(i,5) AS PROD_ID , lpad('X',10,'Y') as comment , case when i < 1000 then i*100 else i end as prod_amt FROM generate_series(1,10000000) a(i); INSERT INTO PROD SELECT PROD_ID , MAX(ORD_NO)||'TEST_NAME' FROM ORDERS_DETAIL GROUP BY PROD_ID; 테스트 데이터 생성 스크립트 V. 테스트 데이터
  • 9. 8 I make PostgreSQL database faster and more reliable with sql tuning and data modeling VI. SQL 성능 개선 방안 “Aside from shared_buffers, the most important memory-allocation parameter is work_mem. Raising this value can dramatically improve the performance of certain queries.” Maybe….
  • 10. 9 I make PostgreSQL database faster and more reliable with sql tuning and data modeling ○ LOOP 안에 사용된 SQL이나 함수의 경우 최소한으로 수행되도록 작성해야 한다. ○ 아래 예제에서는 1000회 수행하던 SQL을 1회 수행하도록 수정함 VI. SQL 성능 개선 방안 1. 함수/프로시저 개선 1.1 불필요한 LOOP 제거 CREATE OR REPLACE PROCEDURE P_SEL() LANGUAGE plpgsql AS $$ DECLARE l_prod_name varchar; REC varchar; BEGIN --orders_detail테이블의 내용으로 1000번 반복 LOOP 수행 FOR REC IN (SELECT PROD_ID FROM ORDERS_DETAIL limit 1000) LOOP BEGIN -- 상품명 확인 위해 prod_id로 반복 조회 SELECT PROD_NM INTO l_prod_name FROM PROD A WHERE A.PROD_ID = REC; END; RAISE NOTICE '상품명:% ', l_prod_name; END LOOP; END; $$; CREATE OR REPLACE PROCEDURE P_SEL() LANGUAGE plpgsql AS $$ DECLARE l_prod_name varchar; REC varchar; BEGIN --LOOP안의 SQL을 스칼라서브쿼리로 변경 FOR REC IN (SELECT (SELECT PROD_NM FROM PROD A WHERE A.PROD_ID = B.PROD_ID) AS PROD_NM FROM ORDERS_DETAIL B LIMIT 1000) LOOP RAISE NOTICE '상품명:% ', REC; END LOOP; END; $$; Elapsed time : 1.23 sec  0.21 sec
  • 11. 10 I make PostgreSQL database faster and more reliable with sql tuning and data modeling ○ 간단한 로직을 함수로 구현하면 실행계획에 재약이 발생하여 성능이 느려진다. ○ 아래 예제는 PROD_ID를 입력 받아 PROD_NM을 출력하는 함수를 조인으로 수정하여 성능 개선한 사례 VI. SQL 성능 개선 방안 1. 함수/프로시저 개선 1.2 간단한 로직은 함수 대신 조인으로 구현 SELECT A.ORD_NO ,F_GET_PROD_NM(A.PROD_ID) AS PROD_NM FROM ORDERS_DETAIL A WHERE A.ORD_NO BETWEEN 1 AND 100000; SELECT A.ORD_NO ,B.PROD_NM FROM ORDERS_DETAIL A, PROD B WHERE A.PROD_ID = B.PROD_ID AND A.ORD_NO BETWEEN 1 AND 100000 Elapsed time : 3.13 sec  0.78 sec
  • 12. 11 I make PostgreSQL database faster and more reliable with sql tuning and data modeling ○ 함수를 꼭 사용해야 한다면 수행 횟수를 줄여야 한다. SELECT A.ORD_LINE_NO, A.ORD_NO, B.COMMENT FROM ORDERS_DETAIL A, ORDERS B WHERE A.ORD_NO = B.ORD_NO AND B.ORD_NO BETWEEN 1006 AND 2005 AND F_GET_PROD_NM(A.PROD_ID) = '999999TEST_NAME' SELECT A.ORD_LINE_NO, A.ORD_NO, COMMENT FROM ( SELECT A.ORD_LINE_NO, A.ORD_NO , A.COMMENT , F_GET_PROD_NM(A.PROD_ID) AS FN FROM ORDERS_DETAIL A, ORDERS B WHERE A.ORD_NO = B.ORD_NO AND B.ORD_NO BETWEEN 1006 AND 2005 ) A WHERE FN = '999999TEST_NAME' Elapsed time : 37.19 sec  1.38 sec 함수 10000 회 수행 VI. SQL 성능 개선 방안 1. 함수/프로시저 개선 1.3 함수 수행 횟수 감소 함수 2000000+800000 회 수행
  • 13. 12 I make PostgreSQL database faster and more reliable with sql tuning and data modeling ○ 아래와 같이 Lateral View를 사용해서 Index Access를 유도할 수 있다.. 인덱스 컬럼 : PROD_ID, ORD_NO VI. SQL 성능 개선 방안 2. LATERVAL View를 이용한 성능 개선 2.1 Complex View Merge 대체 SELECT A.*, B.* FROM PROD A, (SELECT PROD_ID, AVG(ORD_AMT) FROM ORDERS_DETAIL WHERE ORD_NO < 8000 GROUP BY PROD_ID) B WHERE A.PROD_ID = B.PROD_ID AND A.PROD_NM = '999998TEST_NAME'; SELECT A.*, B.* FROM PROD A, LATERAL (SELECT PROD_ID, AVG(ORD_AMT) FROM ORDERS_DETAIL B WHERE ORD_NO < 8000 AND B.PROD_ID = A.PROD_ID GROUP BY PROD_ID) B WHERE A.PROD_NM = '999998TEST_NAME'; 쿼리 수정 0.05 sec 소요 1.8 sec 소요 orders_detail 을 full scan함
  • 14. 13 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 3. 중복테이블 제거 ○ SQL 작성시 같은 테이블을 반복 사용하지 마라. ○ 수정 후 SQL은 ORDERS_DETAIL을 1회만 ACCESS한다. VI. SQL 성능 개선 방안 SELECT O.ORD_NO, O. ORD_DATE, D.ORD_AMT FROM (SELECT O.ORD_NO, O.ORD_DATE FROM ORDERS O WHERE O.CUST_ID = 'C0' UNION ALL SELECT O.ORD_NO, O.ORD_DATE FROM ORDERS O WHERE O.ORD_DATE = ‘20190102' ) O, ORDERS_DETAIL D WHERE O.ORD_NO = D.ORD_NO SELECT O.ORD_NO, O.ORD_DATE, D.ORD_AMT FROM ORDERS O, ORDERS_DETAIL D WHERE O.ORD_NO = D.ORD_NO AND O.CUST_ID = 'C0‘ UNION ALL SELECT O.ORD_NO, O. ORD_DATE, D.ORD_AMT FROM ORDERS O, ORDERS_DETAIL D WHERE O.ORD_NO = D.ORD_NO AND O.ORD_DATE = ‘20190102' 7.1 sec  4.4 sec
  • 15. 14 I make PostgreSQL database faster and more reliable with sql tuning and data modeling ○ 스칼라서브쿼리는 메인 쿼리의 결과 건수 만큼 반복 수행된다. ○ 스칼라서브쿼리를 일반 조인으로 변경하면, 옵티마이저는 NL조인, HASH 조인 또는 MERGE 조인 방식 중 최적의 조인 방법을 선택할 수 있다. VI. SQL 성능 개선 방안 4. 스칼라서브쿼리를 조인으로 변경한 성능 개선 SELECT A.ORD_NO ,(SELECT PROD_NM FROM PROD WHERE PROD_ID=A.PROD_ID) AS PROD_NM FROM ORDERS_DETAIL A WHERE A.ORD_NO BETWEEN 1 AND 100000 SELECT A.ORD_NO ,B.PROD_NM FROM ORDERS_DETAIL A LEFT JOIN PROD B ON A.PROD_ID = B.PROD_ID WHERE A.ORD_NO BETWEEN 1 AND 100000 쿼리 수정 0.39 sec 소요 0.95 sec 소요 1 row당 0.001msec 0.001*1000000=1sec
  • 16. 15 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.1 부분 페이징 처리 방식(1) ○ 개선 전 쿼리는 인덱스 컬럼이 (ORD_AMT, ORD_NO) 아니기 때문에 테이블 전체를 읽은 후 SORT해서 10건을 추출한다. ○ 개선 후 쿼리는 아래의 순서로 처리한다. ORD_AMT 컬럼 인덱스를 이용해서 10건만 읽는다. GROUP BY를 해서 중복된 ORD_AMT 값을 제거한다. ORDERS_DETAIL 테이블과 조인 후 ORD_AMT DESC, ORD_NO DESC 로 정렬 후 10건을 추출한다. ○ ORACLE은 인라인 뷰 내에서 ROWID를 추출하여 RANDOM ACCESS량을 더 줄일 수 있으나, PostgreSQL은 불가능하다. VI. SQL 성능 개선 방안 SELECT C.ORD_NO, C.PROD_ID, C.COMMENT, C.ORD_AMT FROM ( SELECT ORD_AMT FROM( SELECT ORD_AMT FROM ORDERS_DETAIL WHERE PROD_ID = 'PP2' ORDER BY ORD_AMT DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY ) A GROUP BY ORD_AMT ) B, ORDERS_DETAIL C WHERE B.ORD_AMT = C.ORD_AMT AND C.PROD_ID = ‘PP2’ ORDER BY C.ORD_AMT DESC, C.ORD_NO DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM ORDERS_DETAIL WHERE PROD_ID = 'PP2‘ ORDER BY ORD_AMT DESC, ORD_NO DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY 1.46 sec -> 0.1sec CREATE INDEX ORDERS_X02 ON ORDERS(ORD_AMT); ORD_AMT 인덱스 사용 못함
  • 17. 16 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.1 부분 페이징 처리 방식(2) ○ 개선 전 쿼리는 A집합 947959건과 B집합 10,000,000 건을 조인하고, Sorting 후에 20건을 추출한다. ○ 개선 후 쿼리는 아래의 순서로 처리한다. K 인라인 뷰에서 20 로우의 ORD_DATE를 추출하고 중복값을 제거하기 위해 GROUP BY를 수행한다.(1건 추출) 해당 ORD_DATE 값으로 ORDERS 테이블을 조회한다. K 인라인 뷰로부터 20 개 이내의 ORD_DATE값을 제공 받으므로 건수가 매우 적다.(2739건) ORDERS_DTAIL과 조인을 수행하여 최종 데이터를 추출하며(27390건) A.ORD_DATE DESC, B.ORD_AMT로 정렬을 수행한다. 최종 20건의 데이터를 출력한다. VI. SQL 성능 개선 방안 /*+ leading(((v a) b)) NestLoop(v a) NestLoop(v a b) */ SELECT * FROM ( SELECT ORD_DATE FROM ( SELECT ORD_DATE FROM ORDERS A, ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO AND A.ORD_DATE < '20191213‘ ORDER BY A.ORD_DATE DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY ) K GROUP BY ORD_DATE ) V, ORDERS A, ORDERS_DETAIL B WHERE V.ORD_DATE = A.ORD_DATE AND A.ORD_NO = B.ORD_NO AND A.ORD_DATE < '20191213' ORDER BY A.ORD_DATE DESC, B.ORD_AMT OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY SELECT * FROM ORDERS A, ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO AND A.ORD_DATE < '20191213‘ ORDER BY A.ORD_DATE DESC, B.ORD_AMT OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY 9.3 sec -> 0.5sec CREATE INDEX ORDERS_X02 ON ORDERS(ORD_DATE);
  • 18. 17 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.1 부분 페이징 처리 방식(3) ○ 앞 장의 쿼리에서 B.PROD_ID=‘PP2’ 조건 추가 ○ 개선 후 쿼리에서 A.ORD_DATE로 정렬하여 20건 추출 시 B.PROD_ID=‘PP2’만 만족하는 데이터만 추출되도록 EXISTS 절을 사용하였다. VI. SQL 성능 개선 방안 /*+ leading(((v a) b)) NestLoop(v a) NestLoop(v a b) */ SELECT * FROM ( SELECT ORD_DATE FROM ( SELECT ORD_DATE FROM ORDERS A, ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO AND A.ORD_DATE < '20191213‘ AND EXISTS (SELECT 1 FROM ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO AND B.PROD_ID = 'PP2') ORDER BY A.ORD_DATE DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY ) K GROUP BY ORD_DATE ) V, ORDERS A, ORDERS_DETAIL B WHERE V.ORD_DATE = A.ORD_DATE AND A.ORD_NO = B.ORD_NO AND A.ORD_DATE < '20191213' AND B.PROD_ID = ‘PP2’ ORDER BY A.ORD_DATE DESC, B.ORD_AMT OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY SELECT * FROM ORDERS A, ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO AND A.ORD_DATE < '20191213‘ AND B.PROD_ID = ‘PP2’ ORDER BY A.ORD_DATE DESC, B.ORD_AMT OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY 3.6 sec -> 0.6sec 개선 후 실행계획 20건만 추출 SORT 오퍼레이션 없음
  • 19. 18 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.2 union all 을 이용한 페이징 처리 ○ 개선 전 쿼리는 UNION ALL 위쪽 집합과 아래 집합 결과를 합한 후 SORTING하여 10건을 추출한다. ○ 개선 후 쿼리는 위쪽 집합에서 20건, 아래쪽 집합에서 20건을 추출하여 합친 후 다시 Sorting을 하고 10건을 추출한다. VI. SQL 성능 개선 방안 SELECT ORD_NO, COMMENT FROM ( SELECT * FROM (SELECT ORD_NO, COMMENT FROM ORDERS WHERE ORD_NO > 5000 ORDER BY ORD_NO DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY ) A UNION ALL SELECT * FROM (SELECT ORD_NO, COMMENT FROM ORDERS_DETAIL WHERE ORD_NO > 5000 ORDER BY ORD_NO DESC OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY ) B ) C ORDER BY ORD_NO DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY SELECT * FROM ( SELECT ORD_NO, COMMENT FROM ORDERS WHERE ORD_NO > 5000 UNION ALL SELECT ORD_NO, COMMENT FROM ORDERS_DETAIL WHERE ORD_NO > 5000 ) A ORDER BY ORD_NO DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY 3.5 sec -> 0.2sec
  • 20. 19 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.3 인라인 뷰를 이용한 페이징 처리 ○ 개선 전 쿼리는 스칼라서브쿼리가 40010회 수행되나, 개선 후에는 10회만 수행된다. VI. SQL 성능 개선 방안 SELECT V.ORD_NO, V.ORD_DATE, V.COMMENT ,(SELECT B.PROD_NM FROM PROD B WHERE B.PROD_ID = V.PROD_ID) AS PROD_NAME FROM ( SELECT A.ORD_NO, A.ORD_DATE, C.COMMENT, C.PROD_ID FROM ORDERS A, ORDERS_DETAIL C WHERE A.ORD_NO = C.ORD_NO AND C.ORD_AMT > 9900000 ORDER BY A.ORD_NO, A.ORD_DATE OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY ) V; SELECT A.ORD_NO, A.ORD_DATE, C.COMMENT , (SELECT B.PROD_NM FROM PROD B WHERE B.PROD_ID = C.PROD_ID) AS PROD_NAME FROM ORDERS A, ORDERS_DETAIL C WHERE A.ORD_NO = C.ORD_NO AND C.ORD_AMT > 9900000 ORDER BY A.ORD_NO, A.ORD_DATE OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY 스칼라서브쿼리 수 행 횟수 감소로 Block I/O, elapsed time 감소
  • 21. 20 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.4 OUTER 조인을 이용한 페이징 처리 ○ 아래 SQL에서 OUTER 조인은 데이터를 증가시키지 않으며 단지 B.ORD.DATE를 출력하기 위한 용도이다. ○ OUTER 조인에서는 A집합 전체와 B 집합이 조인을 수행하나, 개선 후에는 A집합 에서 10건만 추출하여 B 집합과 조인을 한다. VI. SQL 성능 개선 방안 SELECT V.ORD_LINE_NO, V.ORD_NO, V.PROD_ID, B.ORD_DATE FROM ( SELECT A.ORD_LINE_NO, A.ORD_NO , A.ORD_AMT, A.PROD_ID FROM ORDERS_DETAIL A ORDER BY A.ORD_AMT DESC OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY ) V LEFT JOIN ORDERS B ON V.ORD_NO = B.ORD_NO ORDER BY V.ORD_AMT DESC; SELECT A.ORD_LINE_NO, A.ORD_NO, A.PROD_ID , B.ORD_DATE FROM ORDERS_DETAIL A LEFT JOIN ORDERS B ON A.ORD_NO = B.ORD_NO ORDER BY A.ORD_AMT DESC OFFSET 40000 ROWS FETCH NEXT 10 ROWS ONLY; 10.6 sec -> 5.4sec
  • 22. 21 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.5 웹화면 페이징 쿼리(1) ○ 아래 웹화면 예시에서 10 페이지까지 만을 숫자로 표시하고, 그 다음은 <다음> 버튼을 통해 뒤로 이동해야 한다. ○ 하나의 페이지에는 10개 제목을 표시한다. VI. SQL 성능 개선 방안 --첫번째 화면에 수행되는 SQL SELECT ORD_NO, CUST_ID, COMMENT, ORD_DATE FROM ORDERS WHERE CUST_ID = ‘C’ ORDER BY ORD_DATE DESC OFFSET 0 ROWS FETCH NEXT 101 ROWS ONLY; --101건의 데이터를 추출하여 100건은 화면에 추출하며 마지막 101번째의 데이터는 2번째 페이지가 존재한다는 의미로 사용 --이와 같이 수행을 계속하여 마지막에 추출되는 데이터가 101건을 만족시키지 못하는 경우 해당 화면은 마지막 페이지 --첫번째 화면에 수행되는 SQL SELECT CEIL(COUNT(*)/100) CNT FROM ORDERS WHERE CUST_ID = ‘C3’; --첫번째 화면 데이터 출력 SELECT ORD_NO, CUST_ID, COMMENT, ORD_DATE FROM ORDERS WHERE CUST_ID = ‘C3’ ORDER BY ORD_DATE DESC OFFSET 0 ROWS FETCH NEXT 100 ROWS ONY; --CNT < 2 이면, <다음> 콤보 박스를 INACTIVE로 변경 --CNT >= 2 이면, <다음> 콤보박스를 ACTIVE로 변경 --사용자가 <다음> 을 클릭하면 다음 101 ~ 200 ROW 에 대해서 동일한 패턴의 쿼리 수행 COUNT 쿼리 제거로 성능 향상
  • 23. 22 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.5 웹화면 페이징 쿼리(2) ○ 개선 전에는 뒤쪽 페이지로 갈 수록 BLOCK I/O가 증가한다. ○ 개선 후에는 11번째 ROW의 데이터를 이용하여 읽기 시작점을 찾고 그 이후만 읽어서 항상 일정한 BLOCK I/O가 발생한다. ○ (PROD_ID, ORD_AMT) UNIQUE하지 않으면, PK 컬럼인 ORD_LINE_NO를 인덱스와 SQL에 추가해야 한다. : 오라클은 ROWID를 이용해서 인덱스 수정 필요 없음 VI. SQL 성능 개선 방안 --첫번째 화면에 수행되는 SQL SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT, ORD_LINE_NO FROM ORDERS_DETAIL WHERE PROD_ID = ‘PP1’ ORDER BY ORD_AMT DESC OFFSET 0 ROWS FETCH NEXT 11 ROWS ONLY; --2번째 페이지 조회 --1번째 쿼리 조회 결과 11번째 row의 ORD_AMT=9999946 SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM (SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM ORDERS_DETAIL WHERE (PROD_ID, ORD_AMT) IN ( 'PP1‘, 9999946) ORDER BY ORD_AMT DESC LIMIT 11) X UNION ALL SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM (SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM ORDERS_DETAIL WHERE PROD_ID = ‘PP1’ AND ORD_AMT< 9999946 ORDER BY ORD_AMT DESC LIMIT 11 ) Y ORDER BY ORD_AMT DESC FETCH NEXT 11 ROWS ONLY --1번째 페이지 조회 SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM ORDERS_DETAIL a WHERE PROD_ID = 'PP1' ORDER BY ORD_AMT DESC OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY; --2번째 페이지 조회 (20 ROW를 추출하여 앞의 10 ROW는 버린다.) SELECT ORD_NO, PROD_ID, COMMENT, ORD_AMT FROM ORDERS_DETAIL a WHERE PROD_ID = 'PP1' ORDER BY ORD_AMT DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY; CREATE INDEX ORDERS_DETAIL_X01 ON ORDERS_DETAIL (PROD_ID, ORD_AMT); Keyset Pagination 뒤쪽 페이지 조회 시에도 BLOCK I/O 일정
  • 24. 23 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 5. 페이징 쿼리 최적화 5.5 웹화면 페이징 쿼리(3) ○ 전체 건수와 첫 페이지의 데이터를 함께 구하기 위한 쿼리이다. ○ ORACLE에서는 ROWNUM seudo column을 사용 개선할 수 있으나, PostgreSQL에서는 OFFSET 절을 사용한다. VI. SQL 성능 개선 방안 SELECT * FROM (SELECT COUNT(*) OVER () AS CNT , D.ORD_LINE_NO, D.ORD_NO , D.PROD_ID, O.COMMENT FROM ORDERS_DETAIL D, ORDERS O WHERE D.ORD_NO = O.ORD_NO AND D.ORD_NO BETWEEN 1000 AND 100000 ORDER BY D.ORD_NO, D.PROD_ID ) A OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; SELECT * FROM ( SELECT COUNT(*) OVER () AS CNT ,ROW_NUMBER() OVER (ORDER BY D.ORD_NO, D.PROD_ID) AS RNUM ,D.ORD_LINE_NO, D.ORD_NO , D.PROD_ID, O.COMMENT FROM ORDERS_DETAIL D, ORDERS O WHERE D.ORD_NO = O.ORD_NO AND D.ORD_NO BETWEEN 1000 AND 100000) A WHERE RNUM BETWEEN 21 AND 30 1.84 sec -> 0.48sec WINDOW 함수 2개 수행
  • 25. 24 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 6. 검색 최적화 6.1 중간 값 검색 ○ 지명 등에서 중간 값을 검색해야 하면 FULL TABLE SCAN을 피할 수 없다. (영등포구를 찾기 위해 ‘%등포%’로 검색) ○ 검색창에서 사용자에게 중간 값을 찾을 경우에는 ‘%’를 입력하도록 규칙을 정하고 아래와 같이 쿼리를 작성하면 중간 값 검색을 하지 않는 경우는 빠른 성능을 확보할 수 있다. 인덱스 컬럼 : COMMENT VI. SQL 성능 개선 방안 SELECT * FROM ORDERS_DETAIL WHERE :'var1' NOT LIKE CONCAT('%',:'var1') AND COMMENT LIKE CONCAT(:'var1','%') UNION ALL SELECT * FROM ORDERS_DETAIL WHERE :'var1' LIKE CONCAT('%',:'var1') AND COMMENT LIKE :'var1'; 검색창에 %로 시작하는 문자가 입력되면 UNION ALL 아래 부분만 수행되며 FULL TABLE SCAN 발생 검색창에 % 이외의 문자로 시작하면 UNION ALL 위 부 분만 수행되며 INDEX SCAN 발생 SELECT * FROM ORDERS_DETAIL WHERE COMMENT LIKE CONCAT(‘%’,:'var1‘,’%’) 쿼리 수정
  • 26. 25 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 7. 분석함수를 이용한 성능개선 7.1 상관 서브쿼리 ○ 상관쿼리는 서브쿼리 내에 메인 쿼리와의 조인절이 있다. ○ 개선 전 쿼리는 ORDERS_DETAIL을 2번 액세스하는 비효율이 있다. ○ 개선 후 쿼리는 WINDOW FUNCTION을 사용해서 ORDERS_DETAIL을 1회만 액세스 하도록 했다. VI. SQL 성능 개선 방안 SELECT ORD_NO, ORD_AMT, CUST_ID FROM (SELECT D.ORD_NO, D.ORD_AMT, O.CUST_ID ,CASE D.ORD_NO WHEN MAX(D.ORD_NO) OVER (PARTITION BY D.ORD_NO) THEN 'X' END M_ORD_NO FROM ORDERS_DETAIL D, ORDERS O WHERE D.ORD_NO = O.ORD_NO AND D.ORD_AMT > 9900000 ) Z WHERE M_ORD_NO IS NOT NULL SELECT D.ORD_NO, D.ORD_AMT, O.CUST_ID FROM ORDERS_DETAIL D, ORDERS O WHERE D.ORD_NO = O.ORD_NO AND D.ORD_NO = (SELECT MAX(ORD_NO) FROM ORDERS_DETAIL D WHERE D.ORD_NO = O.ORD_NO) AND D.ORD_AMT > 9900000 쿼리 수정 291 sec -> 1.17sec
  • 27. 26 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 7. 분석함수를 이용한 성능개선 7.2 비상관 서브쿼리 ○ 개선 전 쿼리는 ORDERS_DETAIL을 2번 액세스하는 비효율이 있다. ○ 개선 후 쿼리는 WINDOW FUNCTION을 사용해서 ORDERS_DETAIL을 1회만 액세스 하도록 했다. VI. SQL 성능 개선 방안 SELECT O.ORD_NO, O.COMMENT, v.ORD_AMT FROM ORDERS O, (SELECT ORD_NO, SUM(ORD_AMT) ORD_AMT , MAX(SUM(ORD_AMT)) OVER () MAX_S FROM ORDERS_DETAIL WHERE COMMENT LIKE '9%‘ GROUP BY ORD_NO) V WHERE O.ORD_NO = v.ORD_NO AND v.ORD_AMT = v.MAX_S WITH v AS NOT MATERIALIZED ( SELECT ORD_NO, SUM(ORD_AMT) AS ORD_AMT FROM ORDERS_DETAIL D WHERE COMMENT LIKE '9%‘ GROUP BY ORD_NO) SELECT O.ORD_NO, O.COMMENT, v.ORD_AMT FROM ORDERS O, v WHERE O.ORD_NO = v.ORD_NO AND v.ORD_AMT = (SELECT MAX(v.ORD_AMT) FROM v) 쿼리 수정 2.2 sec -> 0.9sec 2회 ACCESS 1회 ACCESS
  • 28. 27 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 8. 서브쿼리를 조인으로 변경 8.1 NOT IN 절 개선 ○ NOT IN 절은 subquery collapse가 동작하지 않는다. ○ 성능이 좋지 않을 경우 NOT EXISTS 절을 사용하여 Anti Join을 유도한다. VI. SQL 성능 개선 방안 SELECT A.ORD_LINE_NO, A.ORD_AMT FROM ORDERS_DETAIL A WHERE NOT EXISTS (SELECT 1 FROM ORDERS B WHERE A.ORD_NO = B.ORD_NO AND ORD_DATE > '20181220') SELECT A.ORD_LINE_NO, A.ORD_AMT FROM ORDERS_DETAIL A WHERE A.ORD_NO NOT IN (SELECT B.ORD_NO FROM ORDERS B WHERE ORD_DATE > '20181220') 쿼리 수정 180 sec -> 4.4 sec NOT IN 절이 filter 조건으로 반복 수행됨
  • 29. 28 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 8. 서브쿼리를 조인으로 변경 8.2 IN 절 개선 ○ PostgreSQL은 Complex Subquery인 경우 subquery collapse가 동작하지 않는다. ○ 아래와 같이 조인 으로 쿼리를 변환한다. VI. SQL 성능 개선 방안 SELECT ORD_NO, CUST_ID, ORD_DATE, ORD_AMT FROM ORDERS O, (SELECT C.CUST_ID, MAX(C.ORD_DATE) M_ORD_DATE FROM ORDERS C WHERE C.ORD_NO BETWEEN 137000 AND 138000 GROUP BY C.CUST_ID) C WHERE O.CUST_ID = C.CUST_ID AND O.ORD_DATE = C.M_ORD_DATE AND O.ORD_NO BETWEEN 137000 AND 138000 SELECT ORD_NO, CUST_ID, ORD_DATE, ORD_AMT FROM ORDERS O WHERE ORD_DATE IN (SELECT MAX(C.ORD_DATE) FROM ORDERS C WHERE C.CUST_ID = O.CUST_ID) AND O.ORD_NO BETWEEN 137000 AND 138000 쿼리 수정 1.79 sec -> 0.04 sec 서브쿼리 1000회 수행
  • 30. 29 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 8. 서브쿼리를 조인으로 변경 8.3 서브쿼리를 조인으로 변경 ○ Oracle/SQL Server의 부분범위 처리 개념 사용 불가능 ○ PostgreSQL은 쿼리 수행 후 전체 결과를 클라이언트로 전송하므로 조인 수행하도록 해야 속도 향상 SELECT A.CUST_ID, A.COMMENT FROM ORDERS A, ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO AND A.ORD_DATE LIKE '201902%‘ GROUP BY A.ORD_NO HAVING SUM(B.ORD_AMT) > 7900000; SELECT A.CUST_ID, A.COMMENT FROM ORDERS A WHERE A.ORD_DATE LIKE '201902%’ AND EXISTS (SELECT 1 FROM ORDERS_DETAIL B WHERE A.ORD_NO = B.ORD_NO GROUP BY B.ORD_NO HAVING SUM(B.ORD_AMT) > 7900000); 서브쿼리 제거 5.30 sec -> 1.57 sec 서브쿼리 76720회 반복 수행 0.066*76720=5063 VI. SQL 성능 개선 방안
  • 31. 30 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 8. 서브쿼리를 조인으로 변경 8.4 조인 방법 개선 ○ 아래 쿼리는 Anti-Join 으로 수행하면서 ORD_NO가 서브쿼리 집합으로 파고들지 못했다. ○ ORD_NO가 서브쿼리 집합으로 파고들도록 조인방법을 힌트로 바꾸었다. VI. SQL 성능 개선 방안 /*+ NestLoop(o d p) */ 좌측과 동일 쿼리 SELECT O.ORD_NO, O.ORD_DATE, O.COMMENT FROM ORDERS O WHERE O.CUST_ID = 'C0‘ AND ORD_NO < 137199 AND NOT EXISTS (SELECT 1 FROM ORDERS_DETAIL D, PROD P WHERE D.ORD_NO = O.ORD_NO AND D.PROD_ID = P.PROD_ID AND D.ORD_LINE_NO BETWEEN 136844 AND 999999 ) Join Method 변경 0.44 sec -> 0.09sec Access하는 인덱스 변경됨
  • 32. 31 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 9. 사전에 GROUP BY 수행 9.1 GROUPING SETS 개선 ○ 개선 전에는 테이블 전체를 읽은 후, CUST_ID, ORD_DATE 각각 GROUPING 한다. ○ 개선 후에는 (CUST_ID, ORD_DATE)로 GROUP BY하여 건수를 줄인 상태에서 CUST_ID, ORD_DATE 각각 GROUPING한다. VI. SQL 성능 개선 방안 SELECT CUST_ID, ORD_DATE , SUM(S_AMT)/SUM(CNT) AS AVG_AMT FROM ( SELECT CUST_ID, ORD_DATE , SUM(ORD_AMT) S_AMT , COUNT(ORD_AMT) CNT FROM ORDERS GROUP BY CUST_ID, ORD_DATE ) A GROUP BY GROUPING SETS (CUST_ID, ORD_DATE) SELECT CUST_ID, ORD_DATE, AVG(ORD_AMT) AS AVG_AMT FROM ORDERS GROUP BY GROUPING SETS (CUST_ID, ORD_DATE) 쿼리 수정 0.36 sec -> 0.25 sec
  • 33. 32 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 9. 사전에 GROUP BY 수행 9.2 Group By Placement ○ 조인을 수행하기 전에 Group By를 먼저 수행하여 건수를 줄인 후 조인을 수행함으로서, 조인 건수를 획기적으로 감소시킨다. ○ OLTP 보다는 DW의 대용량 시스템에서 사용할 경우 성능 향상을 극대화 할 수 있다. VI. SQL 성능 개선 방안 SELECT P.PROD_NM, SUM(D.S_AMT) FROM PROD P, (SELECT PROD_ID, SUM(ORD_AMT) AS S_AMT FROM ORDERS_DETAIL WHERE PROD_ID IN ('PP0', 'PP1') GROUP BY PROD_ID) D WHERE P.PROD_ID = D.PROD_ID GROUP BY PROD_NM SELECT P.PROD_NM, SUM(D.ORD_AMT) FROM PROD P, ORDERS_DETAIL D WHERE P.PROD_ID = D.PROD_ID AND D.PROD_ID IN ( 'PP0', 'PP1') GROUP BY PROD_NM 쿼리 수정 2.4 sec -> 1.6 sec 인라인 뷰
  • 34. 33 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 9. 사전에 GROUP BY 수행 9.3 대용량 테이블 집계 ○ 아래 쿼리는 ORD_DATE 컬럼에 BTREE INDEX 있어도 건수가 많아서 속도향상이 안된다. ○ ORD_DATE를 년월별로 파티션을 구성하면 FULL SCAN 범위를 줄일 수 있다. ○ PostgreSQL의 Block Range Index를 구성해도 파티션과 유사한 성능을 확보할 수 있다. ○ BRIN을 사용하기 위해서는 데이터가 ORD_DATE 컬럼 순으로 INSERT가 되어야 하고, 테이블에 update 가 발생하지 않아야 한다. VI. SQL 성능 개선 방안 --ORDERS 는 1960년부터 2019년 10월까지의 1억 건 보유 SELECT CUST_ID, COUNT(*) FROM ORDERS WHERE ORD_DATE BETWEEN '20190101‘ AND '20191031‘ GROUP BY CUST_ID; BRIN 생성 193 msec -> 20 msec --Block Range Index 생성 CREATE INDEX ORDER_DETAIL_X01 ON ORDERS_DETAIL USING BRIN (ORD_DATE);
  • 35. 34 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 9. 사전에 GROUP BY 수행 9.4 HashAggregate 유도 ○ ORD_DATE별로 ORD_AMT가 가장 큰 주문정보를 출력하는 쿼리이다. ○ 수정 후에는 ORD_DATE 를 기준으로 HashAggregate 수행하여 작업대상 건수를 363건으로 줄인 후 Sorting을 수행한다. ○ 인라인 뷰에서 PK인 ORD_NO를 추출 후 ORDERS테이블에 PK index scan으로 access하여 원하는 컬럼을 출력하였다. SELECT ORD_DATE, ORD_AMT, CUST_ID FROM (SELECT ROW_NUMBER() OVER (PARTITION BY ORD_DATE ORDER BY ORD_AMT DESC) AS RN ,ORD_AMT, ORD_DATE, CUST_ID FROM ORDERS) A WHERE RN = 1 1.4 sec -> 0.12 sec SELECT B.ORD_DATE, B.ORD_AMT, B.CUST_ID FROM ( SELECT ORD_DATE , (MAX(ARRAY[ORD_AMT, ORD_NO]))[2] AS ORD_NO FROM ORDERS GROUP BY ORD_DATE) A JOIN ORDERS B ON A.ORD_NO = B.ORD_NO; temp table 발생 VI. SQL 성능 개선 방안
  • 36. 35 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 10. Filter 조건 이동하기 10.1 Complex View에 Filter 밀어 넣기 ○ PostgreSQL은 Complex View에 Filter 밀어 넣기가 제한적으로 동작한다. ○ 아래 예제에서는 salary 컬럼에 인덱스가 존재해도 table full scan이 발생하였다. employee 테이블 인덱스 컬럼 : salary VI. SQL 성능 개선 방안 SELECT d.department_id, d.department_name, max_sal FROM department d, (SELECT e.department_id, MAX (e.salary) max_sal FROM employee e WHERE e.salary > 15200 GROUP BY e.department_id) e1 WHERE d.department_id = e1.department_id; SELECT d.department_id, d.department_name, max_sal FROM department d, (SELECT e.department_id, MAX (e.salary) max_sal FROM employee e GROUP BY e.department_id) e1 WHERE d.department_id = e1.department_id AND e1.max_sal > 15200 쿼리 수정 0.12 sec -> 0.02 sec Table full scan 발생 index scan 발생
  • 37. 36 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 11. OR 조건을 UNION 으로 변경 ○ 아래와 같이 where 절에서 OR 조건으로 분기되는 컬럼이 다른 경우 UNION 을 사용하여 변환할 수 있다. ○ OR로 분기된 집합간에 중복데이터가 없는 것이 확실하다면 UNION ALL을 사용한다. VI. SQL 성능 개선 방안 SELECT o.ord_no, o.comment, d.ord_line_no, d.comment FROM orders o, orders_detail d WHERE o.ord_no = d.ord_no AND d.ord_line_no in (136844,136845) UNION SELECT o.ord_no, o.comment, d.ord_line_no, d.comment FROM orders o, orders_detail d WHERE o.ord_no = d.ord_no AND o.ord_date in ('20190817', '20190816') SELECT o.ord_no, o.comment, d.ord_line_no, d.comment FROM orders o, orders_detail d WHERE o.ord_no = d.ord_no AND (d.ord_line_no in (136844,136845) OR o.ord_date in ('20190817', '20190816')) 쿼리 수정 6.1 sec -> 2.1 sec index scan 발생 조인 후 OR 조건을 Filter 적용
  • 38. 37 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 12. 인덱스 액세스 범위 최소화 VI. SQL 성능 개선 방안 Nested Loop (actual time=0.096..1.069 rows=2740 loops=1) Buffers: shared hit=83 -> HashAggregate (actual time=0.036..0.039 rows=8 loops=1) Group Key: to_char((('2019-08-25'::date + (generate_series(1, 8))))::timestamp with time zone, 'YYYYMMDD'::text) -> Result (actual time=0.025..0.030 rows=8 loops=1) -> ProjectSet (actual time=0.002..0.003 rows=8 loops=1) -> Result (actual time=0.001..0.001 rows=1 loops=1) -> Index Scan using orders_x01 on orders (actual time=0.015..0.105 rows=342 loops=8) Index Cond: (((ord_date)::text = (to_char((('2019-08-25'::date + (generate_series(1, 8))))::timestamp with time zone, 'YYYYMMDD'::text))) AND ((cust_id)::text = 'C9'::text)) Buffers: shared hit=83 Index Scan using orders_x01 on orders (actual time=2.045..9.163 rows=2740 loops=1) Index Cond: (((ord_date)::text >= '20190826'::text) AND ((ord_date)::text <= '20190902'::text) AND ((cust_id)::text = 'C9'::text)) Buffers: shared hit=125 ○ 인덱스 선행컬럼이 BETWEEN 조건이면, 인덱스 후행 컬럼은 ACCESS 조건으로 사용되지 않는다. ○ BETWEEN 조건 대신에 IN 조건을 사용하면, 인덱스 후행 컬럼도 ACCESS 조건으로 사용되어 BLOCK I/O를 줄일 수 있다. 인덱스 컬럼 : ORD_DATE, CUST_ID SELECT COUNT(*) FROM ORDERS WHERE CUST_ID = ‘C9’ AND ORD_DATE IN (SELECT TO_CHAR(DATE '20190826'- 1+generate_series(1,DATE '20190902'-'20190826'+1) , YYYYMMDD') ) SELECT COUNT(*) FROM ORDERS WHERE CUST_ID = ‘C9’ AND ORD_DATE BETWEEN '20190826' AND '20190902' 쿼리 수정 9.27 msec -> 1.22 msec
  • 39. 38 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 13. CTE 활용 13.1 최대값/최소값 추출 ○ 특정 컬럼의 DISTINCT 값이나, 특정 컬럼 기준으로 MIN/MAX 값을 출력 인덱스 컬럼 : PROD_ID VI. SQL 성능 개선 방안 SELECT PROD_ID, MAX(ORD_NO) FROM ORDERS_DETAIL GROUP BY PROD_ID ORDER BY 1; TABLE FULL SCAN 발생 3.2 sec  0.2 msec WITH RECURSIVE W(N) AS ( SELECT MIN(PROD_ID) FROM ORDERS_DETAIL UNION ALL SELECT (SELECT PROD_ID FROM ORDERS_DETAIL WHERE PROD_ID > N ORDER BY PROD_ID LIMIT 1) PROD_ID FROM W WHERE N IS NOT NULL ) SELECT N, (SELECT MAX(B.ORD_NO) FROM ORDERS_DETAIL B WHERE B.PROD_ID = A.N) FROM W A WHERE N IS NOT NULL;
  • 40. 39 I make PostgreSQL database faster and more reliable with sql tuning and data modeling ORD_NO CUST_ID COMMENT ORD_DATE ORD_AMT 8000001C400 QUICK DELIVERY 20200101 30 8000002C200 NO SIGNABURE 20200101 50 8000003C300 ETC 20200102 40 8000004C400 BLACK 20200103 200 8000005C500 20200103 500 8000006C600 20200104 30 8000023C400 20201018 450 8000024C500 NO SIGNABURE 20201019 80 8000025C600 ETC 20201019 250 8000026C200 BLACK 20201020 400 8000027C300 NO DELIVERY 20201021 5000 8000028C100 XX LARGE 20201022 990 Primary Key 시간순으로 데이터 입력되며 update는 발생하지 않음 13. CTE 활용 13.2 로그데이터 추출 VI. SQL 성능 개선 방안 ……………………….
  • 41. 40 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 13. CTE 활용 13.2 로그데이터 추출(1) ○ 데이터가 Primary Key 컬럼 순으로 입력되고, ORD_DATE 컬럼은 UPDATE가 발생하지 않는 테이블이다. ○ 아래 예제에서 ORD_DATE에 인덱스가 없는 경우, 다른 DBMS 에서는 파티션 구성 외에는 개선 방법이 없다. VI. SQL 성능 개선 방안 SELECT * FROM ORDERS2 WHERE ORD_DATE BETWEEN DATE '20201018' AND DATE '20201022'; WITH RECURSIVE w_min (min, max, middle, level) AS ( SELECT min(ord_no), max(ord_no), (min(ord_no)+max(ord_no))/2, 0 FROM orders2 UNION ALL SELECT v.min, v.max, (v.min + v.max)/2, w_min.level+1 FROM w_min, LATERAL ( SELECT ORD_NO, ORD_DATE FROM orders2 AS e WHERE e.ord_no >= w_min.middle ORDER BY e.ord_no FETCH FIRST ROW ONLY ) AS e ,LATERAL (VALUES ( CASE WHEN e.ord_date <= date '20201018' THEN e.ord_no ELSE w_min.min END, CASE WHEN e.ord_date <= date '20201018' THEN w_min.max ELSE e.ord_no END ) ) AS v (min, max) WHERE (v.min + v.max)/2 NOT IN (v.min, v.max) ), w_max (min, max, middle, level) AS ( SELECT min(ord_no), max(ord_no), (min(ord_no)+max(ord_no))/2, 0 FROM orders2 UNION ALL SELECT v.min, v.max, (v.min + v.max)/2, w_max.level+1 FROM w_max, LATERAL ( SELECT ORD_NO, ORD_DATE FROM orders2 AS e WHERE e.ord_no >= w_max.middle ORDER BY e.ord_no FETCH FIRST ROW ONLY ) AS e ,LATERAL (VALUES ( CASE WHEN e.ord_date <= date '20201022' THEN e.ord_no ELSE w_max.min END, CASE WHEN e.ord_date <= date '20201022' THEN w_max.max ELSE e.ord_no END ) ) AS v (min, max) WHERE (v.min + v.max)/2 NOT IN (v.min, v.max) ) SELECT * FROM orders2 WHERE ORD_NO >= (SELECT middle FROM w_min ORDER BY level DESC FETCH FIRST ROWS ONLY) AND ORD_NO <= (SELECT middle FROM w_max ORDER BY level DESC FETCH FIRST ROWS ONLY) ; 쿼리 수정 978 msec  2 msec
  • 42. 41 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 13. CTE 활용 13.2 로그데이터 추출(2) ○ 아래는 개선 후 실행계획의 일부분이다. ○ 인덱스의 1번째와 마지막 블록만 access해서 ORD_NO를 찾는다. ○ ORD_NO의 middle 값을 이용해 recursive query가 중단되도록 한다. ○ block I/O는 93,000 에서 204로 줄었다. VI. SQL 성능 개선 방안 인덱스 1번째 블록만 access 인덱스 마지막 블록만 access
  • 43. 42 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 참고서적 PostgreSQL 9.6 성능이야기 시연아카데미 SQL Server 튜닝 원리와 해법 b2en
  • 44. 43 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 감사합니다 All rights not reserved. Any part of this material may be reproduced, stored in a retrieval system, or transmitted in any form or by any means, without any permission from me. I will not be held liable for any damage caused or alleged to have been caused directly or indirectly by applying the skills described in this presentation. I would appreciate it if you could make your database server run faster with the technique in this material.