database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
ㆍPostgreSQL
Firebird
Oracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
PostgreSQL Q&A 7468 게시물 읽기
No. 7468
union 에 order by 가 있을 경우 속도가 현저히 떨어 집니다.
작성자
letsgofast
작성일
2008-07-21 15:50ⓒ
2008-07-21 15:54ⓜ
조회수
10,913

audit_time에 인덱스를 주었구요


나머지 필드에서 복합 인덱스로(4~5)정도로 2개 주었습니다.


왜 union order by가 있을 경우에만 속도가 늦어 질까요?


하나의 테이블만 하면 괜찮습니다.


아래는 쿼리 실행 결과 입니다.


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

쿼리문 order by를 줬을 경우: explain analyze select a.audit_time

from (

     select *

     from audit_log_2008071718

     where audit_time >= '2008-07-01 00:00:00' and

           audit_time <= '2008-07-30 24:00:00'

     union all

     select *

     from audit_log_2008071717

     where audit_time >= '2008-07-01 00:00:00' and

           audit_time <= '2008-07-30 24:00:00'

     ) as a

order by a.audit_time limit 200;

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 Limit  (cost=167213.09..167213.59 rows=200 width=8) (actual time=98810.181..98810.514 rows=200 loops=1)

   ->  Sort  (cost=167213.09..167361.26 rows=59266 width=8) (actual time=98810.179..98810.288 rows=200 loops=1)

         Sort Key: a.audit_time

         ->  Result  (cost=833.33..162514.80 rows=59266 width=8) (actual time=5746.875..82797.287 rows=11853199 loops=1)

               ->  Append  (cost=833.33..162514.80 rows=59266 width=8) (actual time=5746.873..68909.234 rows=11853199 loops=1)

                     ->  Subquery Scan "*SELECT* 1"  (cost=833.33..106970.15 rows=39046 width=8) (actual time=5746.872..37592.558 rows=7809272 loops=1)

                           ->  Bitmap Heap Scan on audit_log_2008071718  (cost=833.33..106579.69 rows=39046 width=648) (actual time=5746.868..27718.774 rows=7809272 loops=1)

                                 Recheck Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))

                                 ->  Bitmap Index Scan on time_20080718  (cost=0.00..823.57 rows=39046 width=0) (actual time=5596.823..5596.823 rows=7809272 loops=1)

                                       Index Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))

                     ->  Subquery Scan "*SELECT* 2"  (cost=431.95..55544.66 rows=20220 width=8) (actual time=1652.335..18149.394 rows=4043927 loops=1)

                           ->  Bitmap Heap Scan on audit_log_2008071717  (cost=431.95..55342.46 rows=20220 width=648) (actual time=1652.331..13091.470 rows=4043927 loops=1)

                                 Recheck Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))

                                 ->  Bitmap Index Scan on time_2008071717  (cost=0.00..426.89 rows=20220 width=0) (actual time=1512.550..1512.550 rows=4043927 loops=1)

                                       Index Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))

 Total runtime: 98863.019 ms

(16 rows)

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

쿼리문 order by 를 주지 않았을 경우

explain analyze select a.audit_time

from (

     select *

     from audit_log_2008071718

     where audit_time >= '2008-07-01 00:00:00' and

           audit_time <= '2008-07-30 24:00:00'

     union all

     select *

     from audit_log_2008071717

     where audit_time >= '2008-07-01 00:00:00' and

           audit_time <= '2008-07-30 24:00:00'

     ) as a

limit 200;

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 Limit  (cost=0.00..766.27 rows=200 width=8) (actual time=45.695..70.891 rows=200 loops=1)

   ->  Result  (cost=0.00..227067.78 rows=59266 width=8) (actual time=45.692..70.670 rows=200 loops=1)

         ->  Append  (cost=0.00..227067.78 rows=59266 width=8) (actual time=45.690..70.439 rows=200 loops=1)

               ->  Subquery Scan "*SELECT* 1"  (cost=0.00..150668.49 rows=39046 width=8) (actual time=45.689..70.222 rows=200 loops=1)

                     ->  Index Scan using time_20080718 on audit_log_2008071718  (cost=0.00..150278.03 rows=39046 width=648) (actual time=45.686..69.974 rows=200 loops=1)

                           Index Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))

               ->  Subquery Scan "*SELECT* 2"  (cost=0.00..76399.29 rows=20220 width=8) (never executed)

                     ->  Index Scan using time_2008071717 on audit_log_2008071717  (cost=0.00..76197.09 rows=20220 width=648) (never executed)

                           Index Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))

 Total runtime: 71.083 ms

(10 rows)

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

이 글에 대한 댓글이 총 14건 있습니다.

버전이 어떻게 되나요?

 

쿼리 플랜을 보니 예측과 실제가 꽤 차이가 있네요. 데이터가 대략으로 갱신된 모양입니다. vacuum analyze 한 번 하고 해보시고요. 그래도 그러면 vacuum 해보시고 그래도 그러면 vacuum full (락걸림)을 해보세요.

신기배(소타)님이 2008-07-21 21:54에 작성한 댓글입니다.

Union을 한 결과의 소트에는 인덱스를 사용하지 않으니 느려지는 것은 당연합니다.

하지만 이 경우 각 서브쿼리에 인덱스를 사용하지 않는 것이 문제군요.

아마도 나중에 소트를 할 것을 생각하고 bitmap index scan을 사용한 듯 하네요.


ANALYZE는 하셨겠죠?


random_page_cost 설정값을 좀 줄여보셨나요? 보통 이 값이 보수적으로 되어 있어서 대부분의 하드에서는 좀 낮추는 것이 좋더군요.

박성철(gyumee)님이 2008-07-21 22:15에 작성한 댓글입니다.
이 댓글은 2008-07-21 22:16에 마지막으로 수정되었습니다.

버전은 8.2 입니다.

현재 시간당 수백만건이 시간 테이블에 저장 되고 있으며

update는 일어 나지 않고 있습니다.

현재 테스트 용도로 union을 한번 밖에 쓰지 않았으나

대략 수백만껀 테이블이 30번 이상 union될 수 있습니다.

유니온 한 결과를 인덱스를 사용 하려면 어떻게 해야 할까요?

몇일째 해결 못하고 있습니다.

letsgofast님이 2008-07-22 09:46에 작성한 댓글입니다. Edit

답 안나옵니다.


order by 대상이 되는 칼럼 기준으로 그 전용 테이블 - 통계용 테이블이 되겠죠. -을 트리거로 만들든지,

아니면, 전체 자료구조를 다시 설계해 보시든지,


현재 퀴리 방식로는 union 작업 뒤 인라인 뷰까지 쓰니,

PostgreSQL 에서는 인덱스를 어찌 써야할지 모를 것 같네요.

김상기(ioseph)님이 2008-07-22 10:02에 작성한 댓글입니다.

확언은 못하겠지만 UNION한 결과를 인덱스를 사용해 소트하는 DB는 세상에 없을 것 같다는 생각이 드네요.

각 row의 크기가 크다면 상기님 말씀처럼 ID와 audit_time 만 가지고 있는 테이블을 따로 만들어서 해결하는 수 밖에 없을 듯 합니다.

그런데 테이블을 꼭 날짜별로 나누어야 하는 이유라도 있나요? 나중에 날짜별로 한꺼번에 지우기는 좋겠지만....

박성철(gyumee)님이 2008-07-22 11:18에 작성한 댓글입니다.



subquery부분을 sp로  만들어서 sp상에서 order by 했을대는 속도가 어떻게 되는가여 ?

한번 결과를 올려주시기 바랍니다.

김병석(byung82)님이 2008-07-22 13:36에 작성한 댓글입니다.

현재 날짜별 시간대별 테이블로 구성 되어 있습니다.

예를 들면 20080717(년/월/시)으로 테이블 명이 구분 되어 있으며,

구분된 이유는 시간당 몇백 만건이 insert되기 때문에

최근 로그에 대해서 빠르게 UI 상에 보여줄려는 목적이 있습니다.

그러다 보니 사용자 입장에서는 최근 로그들 이외에 몇시간 전 로그까지

검색 하려다 보니 당연히 UNION을 쓸수 밖에 없습니다.

트리거는 한번도 사용해 본적이 없는데

insert시 속도가 느려지지 않나요?

이기원님이 2008-07-22 13:38에 작성한 댓글입니다.
이 댓글은 2008-07-22 13:39에 마지막으로 수정되었습니다. Edit

서브 쿼리 결과 입니다. 김병석 님....
-----------------------------------------------------------------------------------------
쿼리
explain analyze select *
     from audit_log_2008071718

     where audit_time >= '2008-07-01 00:00:00' and
           audit_time <= '2008-07-30 24:00:00'

 order by audit_time desc limit 1000;                                                                             
-----------------------------------------------------------------------------------------
QUERY PLAN                                                                       

 Limit  (cost=0.00..3848.74 rows=1000 width=648) (actual time=89.038..111.083 rows=1000 loops=1)
   ->  Index Scan Backward using time_20080718 on audit_log_2008071718  (cost=0.00..150278.03 rows=39046 width=648) (actual time=89.034..109.060 rows=1000 loops=1)
         Index Cond: ((audit_time >= '2008-07-01 00:00:00'::timestamp without time zone) AND (audit_time <= '2008-07-31 00:00:00'::timestamp without time zone))
 Total runtime: 112.202 ms

letsgofast님이 2008-07-22 13:54에 작성한 댓글입니다. Edit

110 ms 면 원하시는 속도가 아니신가여 더 빠르셔야 하는건가 ^^;



그러면 이제 이렇게 sp 상에서 order by 만 하고 limit는 쓰지 마시기 바랍니다.


그리고 select * from sp limit 100;


이렇게 했을때 결과물도 하나 올려주시기 바랍니다.


그리고 


조금더 튜닝을 하자면...


지금 테이블 설계시 oid를 사용하셨다면


제가 plsql을 잘 사용하지 못해서 다충 방법론만 적겠습니다.


select index1 = oid from audit_log_2008071718

where audit_time = '2008-07-01 00:00:00'


select index2 = oid from  audit_log_2008071718

where audit_time = '2008-07-30 24:00:00'


select index3 = oid from audit_log_2008071717

where audit_time = '2008-07-01 00:00:00'


select index4 = oid from audit_log_2008071717

where audit_time = '2008-07-30 24:00:00'



select 컬럼명을 다적어줌 from audit_log_2008071718

where oid >= index1 

and oid <= index2

union all 

select 컬럼명을 다적어줌 from audit_log_2008071717

where oid >= index3

and oid <= index4

limit 100;


이렇게 한번 해보시기 바랍니다.


로그 데이트가 일부분 공개가 가능하면 데이트를 한번 일부분만 올려주시기 바랍니다 ^^:


그럼


김병석(byung82)님이 2008-07-23 12:16에 작성한 댓글입니다.

김병석님

SP가 뭐죠? Stored Procedures 인가요?



letsgofast님

explain analyze (

select audit_time

from audit_log_2008071718

where audit_time >= '2008-07-01 00:00:00' and audit_time <= '2008-07-30 24:00:00' 

order by audit_time limit 200 )

union all

(select audit_time

from audit_log_2008071717

where audit_time >= '2008-07-01 00:00:00' and audit_time <= '2008-07-30 24:00:00' 

order by audit_time limit 200 )

order by audit_time limit 200;

는 결과가 어떤가요?

 

그냥님이 2008-07-23 12:50에 작성한 댓글입니다.
이 댓글은 2008-07-24 10:25에 마지막으로 수정되었습니다. Edit

쿼리를 조금 다른시각에서 생각해보면


select * from audit_log_2008071718

where audit_time >= '2008-07-01 00:00:00' and

audit_time <= '2008-07-30 24:00:00'


select * from audit_log_2008071717

where audit_time >= '2008-07-01 00:00:00' and

audit_time <= '2008-07-30 24:00:00'


이렇게 2개를 합친 상위ㅏ 200개만 필요하신거니깐


조금더 변경을 하면


from 

(

select * from audit_log_2008071718

where audit_time >= '2008-07-01 00:00:00'

order by audit_time limit 200;

union all

select * from audit_log_2008071717

where audit_time >= '2008-07-01 00:00:00'

order by audit_time limit 200;

) as a

order by a.audit_time limit 200;

이렇게 구현해도 문제가 되지 않을거라 생각이 드는군여


그럼

김병석(byung82)님이 2008-07-23 16:49에 작성한 댓글입니다.

댓글 달아주신 분들 모두 진심으로 감사드립니다.

김병석님께서 말씀하신대로

테스트를 하였습니다.

결과는 매우 빠릅니다만

UI에서 페이지 이동시

limit 200이후에 나오는

결과물에 대해서는 다음 테이블을

참조 함으로써 첫번재 테이블을 모두 보여주지 

못하는 문제점이 있어서 현재 적용을 못하고 있습니다.

letsgofast님이 2008-07-24 15:34에 작성한 댓글입니다. Edit

limit 수치는 페이징에 따라서 조정하셔야겠죠. ^^

축하드립니다.

박성철(gyumee)님이 2008-07-24 16:01에 작성한 댓글입니다.

괄호를 사용하여 뜻을 좀 더 명확히 하는건 어떨까요

tyro님이 2008-07-25 20:10에 작성한 댓글입니다. Edit
[Top]
No.
제목
작성자
작성일
조회
7472join 을 이용해서 id 걸러낼때. [2]
산하
2008-07-28
7041
7471PostgreSQL 데몬 시작 시 데몬이 시작이 안됩니다. [11]
김대청
2008-07-27
8501
7470PostgreSQL과 BIND 연동 해보신분 계신가요? [2]
지용남
2008-07-24
9123
7468union 에 order by 가 있을 경우 속도가 현저히 떨어 집니다. [14]
letsgofast
2008-07-21
10913
7467exec() 로 날리는 쿼리 사이즈 제한? [1]
help
2008-07-21
7444
7466PQputCopyData 함수에 관하여 (optimal한 버퍼 크기) [2]
help
2008-07-21
7072
7464설치시 다음단계가 안넘어갑니다 [2]
양진욱
2008-07-18
6439
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2024 DSN, All rights reserved.
작업시간: 0.017초, 이곳 서비스는
	PostgreSQL v16.2로 자료를 관리합니다