Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

[Pgday.Seoul 2020] SQL Tuning

199 views

Published on

[Pgday.Seoul 2020] SQL Tuning - 유재근

Published in: Software
  • Be the first to comment

[Pgday.Seoul 2020] SQL Tuning

  1. 1. PostgreSQL SQL Tuning 유 재 근 mail: naivety1@naver.com PGDay.Seoul.2020
  2. 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. 3. 2 I make PostgreSQL database faster and more reliable with sql tuning and data modeling I. 발표자 • 데이터 모델링 • SQL 튜닝 • DAP
  4. 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. 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. 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. 7. 6 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 테스트 데이터 ERD V. 테스트 데이터
  8. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 43. 42 I make PostgreSQL database faster and more reliable with sql tuning and data modeling 참고서적 PostgreSQL 9.6 성능이야기 시연아카데미 SQL Server 튜닝 원리와 해법 b2en
  44. 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.

×