OS 환경 : Oracle Linux 8.4 (64bit)
DB 환경 : Oracle Database 19.27.0.0
방법 : 오라클 19c 통계정보 대량 이관 분석 및 속도 개선
지난 게시글에서 대용량 통계정보를 세션을 여러개로 나누어 병렬로 이관하는 방법을 설명했음
참고 : 오라클 19c 통계정보 대량 이관시 병렬 처리 ( https://positivemh.tistory.com/1267 )
그리고 지난 테스트에서 db 크기가 큰 경우 dbms_stats.import_database_stats 을 이용하면 속도가 너무 느려 프로시저가 끝나질 않는다고 얘기했었음
본문에서는 dbms_stats.export_database_stats, import_database_stats를 분석해보고
문제점을 개선해서 dbms_stats.import_database_stats도 빠르게 수행해봄
결론부터 말하자면 dbms_stats.export_database_stats 시 statid를 넣어줘야 빠르게 동작함
이유는 본문에서 설명함
테스트
샘플 유저 및 테이블, 인덱스 등 생성
참고 : 오라클 19c 유저 100개, 테이블 100개, 인덱스 100개, 데이터 1000건 생성 ( https://positivemh.tistory.com/1266 )
현재 db에는 유저 100개와 해당 유저에 테이블 100개, 인덱스 100개 생성 후 데이터도 1000건씩 생성되어있음
이 상태에서 통계정보를 export 한뒤 다시 import 하는 테스트를 진행함
통계정보 수집(db 레벨 수집)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
SQL>
set timing on
BEGIN
DBMS_STATS.GATHER_DATABASE_STATS(
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE,
method_opt => 'FOR ALL COLUMNS SIZE SKEWONLY',
degree => DBMS_STATS.AUTO_DEGREE,
cascade => TRUE
);
END;
/
PL/SQL procedure successfully completed.
|
각 파라미터 의미
DBMS_STATS.GATHER_DATABASE_STATS : db 레벨에서 통계정보 수집
estimate_percent => DBMS_STATS.AUTO_SAMPLE_SIZE : 자동 샘플링, Oracle이 데이터 양과 통계 품질에 따라 적절한 샘플링 비율을 자동 결정
method_opt => 'FOR ALL COLUMNS SIZE SKEWONLY' : Oracle이 컬럼의 데이터 분포를 기반으로 히스토그램을 수집할 컬럼을 결정
degree => DBMS_STATS.AUTO_DEGREE : 병렬 자동 시스템 상황(코어 수, 부하 등)에 따라 Oracle이 병렬도를 자동 설정
cascade => TRUE 인덱스 포함 테이블뿐 아니라 해당 테이블에 속한 인덱스 통계도 함께 수집
참고 : 오라클 19c 통계정보 이관 방법 ( https://positivemh.tistory.com/1265 )
통계 저장용 테이블 생성
|
1
2
3
|
SQL> exec dbms_stats.create_stat_table(ownname => 'SYSTEM', stattab => 'ALL_TABLE_STATS2');
PL/SQL procedure successfully completed.
|
통계정보 export(export 시 statid도 ALL_TABLE_STATS_ID로 지정함)
(ALL_TABLE_STATS 테이블로 통계정보를 넣는작업)
|
1
2
3
|
SQL> exec dbms_stats.export_database_stats(statown => 'SYSTEM', stattab => 'ALL_TABLE_STATS2', statid => 'ALL_TABLE_STATS_ID');
PL/SQL procedure successfully completed.
|
all_table_stats 테이블 count 확인
|
1
2
3
4
5
|
SQL> select count(*) from system.all_table_stats2;
COUNT(*)
----------
1529704
|
약 150만건이 저장됨
참고1. 통계정보 export 시 statid도 넣었기 때문에 statid는 모두 ALL_TABLE_STATS_ID로 되어있음
|
1
2
3
4
5
6
7
8
|
SQL>
select nvl(statid, 'NULL') statid, count(*)
from system.all_table_stats2
group by statid;
STATID COUNT(*)
------------------- ----------
ALL_TABLE_STATS_ID 1529704
|
참고2. stat 테이블에는 index도 존재함
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
SQL>
set lines 200 pages 1000
col owner for a15
col index_type for a10
col index_name for a20
col table_name for a30
col columns for a50
SELECT a.*, (SELECT status from dba_indexes where index_name = a.index_name and rownum<=1) status from (
SELECT
ind.owner AS "OWNER",
ind.table_name AS "TABLE_NAME",
ind.index_name AS "INDEX_NAME",
ind.index_type AS "INDEX_TYPE",
LISTAGG(ind_col.column_name, ', ') WITHIN GROUP (ORDER BY ind_col.column_position) AS "COLUMNS"
FROM
dba_indexes ind
JOIN
dba_ind_columns ind_col
ON ind.owner = ind_col.index_owner
AND ind.index_name = ind_col.index_name
WHERE
ind.table_name = 'ALL_TABLE_STATS2'
AND ind.owner = 'SYSTEM'
GROUP BY
ind.owner,
ind.index_name,
ind.index_type,
ind.table_name
ORDER BY
ind.owner,
ind.index_name) a;
OWNER TABLE_NAME INDEX_NAME INDEX_TYPE COLUMNS STATUS
--------------- ------------------------------ -------------------- ---------- -------------------------------------------------- --------
SYSTEM ALL_TABLE_STATS2 ALL_TABLE_STATS2 NORMAL STATID, TYPE, C5, C1, C2, C3, C4, VERSION VALID
|
ALL_TABLE_STATS2에 인덱스 이름도 ALL_TABLE_STATS임 STATID, TYPE, C5, C1, C2, C3, C4, VERSION 컬럼으로 이루어져 있음
다시 이 통계정보를 db에 import(ALL_TABLE_STATS2 테이블에서 db 테이블로 통계정보를 넣는작업)
|
1
2
3
4
|
SQL>
set timing on
exec dbms_stats.import_database_stats(statown => 'SYSTEM', stattab => 'ALL_TABLE_STATS2', statid => 'ALL_TABLE_STATS_ID');
(실행중)
|
v$session_longops 확인
|
1
2
3
4
5
6
7
8
9
10
|
SQL>
set lines 200 pages 1000
col opname for a30
select sid, serial#, opname, sofar, totalwork
from v$session_longops
where sofar != totalwork;
SID SERIAL# OPNAME SOFAR TOTALWORK
---------- ---------- ------------------------------ ---------- ----------
296 52026 Import Database Statistics 4303 11852
|
이전 테스트와는 다르게 5분도 안지났는데 sofat이 4300이 넘음
top 확인
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
$ top
top - 20:09:42 up 1:59, 3 users, load average: 1.24, 0.91, 0.50
Tasks: 396 total, 2 running, 394 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 96.5 us, 2.6 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.9 hi, 0.0 si, 0.0 st <<<<<<<<<
%Cpu2 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu4 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu5 : 0.9 us, 0.9 sy, 0.0 ni, 98.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu6 : 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu7 : 0.0 us, 0.9 sy, 0.0 ni, 97.4 id, 0.0 wa, 1.7 hi, 0.0 si, 0.0 st
%Cpu8 : 3.5 us, 9.7 sy, 0.0 ni, 66.4 id, 17.7 wa, 0.9 hi, 1.8 si, 0.0 st
%Cpu9 : 0.0 us, 0.0 sy, 0.0 ni, 97.4 id, 0.0 wa, 2.6 hi, 0.0 si, 0.0 st
MiB Mem : 7932.5 total, 53.8 free, 1587.6 used, 6291.1 buff/cache
MiB Swap: 10240.0 total, 10219.9 free, 20.1 used. 4042.5 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4112 oracle 20 0 2984876 1.6g 1.6g R 99.1 21.1 4:48.18 oracle_4112_ora
1756 oracle 20 0 2966412 80556 76704 S 9.6 1.0 0:16.19 ora_lgwr_ora19d
281 root 0 -20 0 0 0 I 4.3 0.0 0:02.31 kworker/8:1H-kblockd
4165 oracle 20 0 264356 4628 3736 R 1.7 0.1 0:00.03 top
1 root 20 0 238412 11016 8248 S 0.0 0.1 0:01.09 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.01 kthreadd
|
여전히 cpu 10개중에 하나만 사용중임
oratop 확인
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
$ /app/oracle/product/19c/suptools/oratop/oratop / as sysdba -rf
Oracle 19c - 20:04:55 Primary r/w ORA1 up: 1.9h, 2 sn, 1 ins, 0 er, 18G sz, 2.3G sga, 0%fra, archivelog 3.9%db
ID CPU %CPU LOAD AAS ASC ASI ASW ISW REDO TEMP IORT MBPS IOPS R/S W/S LIO GCPS %FRE PGA NETV UTPS UCPS SQRT %DBC %DBW
1 10 4.1 0.5 0.4 1 0 0 1 374k 1M 2.8m 1.4 123 15.3 108 62k 0 16.0 635M 589 0 8.4 77u 3.4 0.5
EVENT (RT) SESSIONS WAIT TIME AVG %DBT WAIT_CLASS
db file sequential read 770 2.8s 3.6m 55.6 User I/O
log file parallel write 5.9k 1.5s 255u 30.5 System I/O
DB CPU 340m 6.9
db file async I/O submit 497 290m 585u 5.8 System I/O
control file parallel write 38 20m 547u 0.4 System I/O
ID SID SPID USERNAME PROGRAM SRV SERVICE OPN SQLID/BLOCKER E/T %CPU %LIO PGA STS STE WAIT_CLASS EVENT/OBJECT NAME W/T
1 296 4112 SYS sqlplus@o DED SYS$USE INS 22x35t27br8tn 8.1t 75.9 100 48M ACT RUN On CPU 7u
|
빠르게 나타났다 사라지는 insert 쿼리들에 %CPU가 75~90정도로 찍힘
oratop에 걸리는 쿼리는 대부분 select 가 많았고 insert는 가끔 관측됨(빠르게 수행되서 그런것으로 판단됨)
oratop 플랜확인
oratop에서 x 입력 후 sql_id 입력
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
Enter sql_id: 22x35t27br8tn
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL_ID 22x35t27br8tn, child number 0
--------------------------------------
insert /*+ OPT_PARAM('_parallel_syspls_obey_force' 'false')
*/ into sys.dbms_stats_id_map_tab (c5, c1, c2, c3, cn)
select /*+ leading(s) index(s) */ distinct s.c5, s.c1,
s.c2, s.c3, d.subpartition_name cn from
"SYSTEM"."ALL_TABLE_STATS2" s, (select u.name
table_owner, op.name table_name, op.subname
partition_name, os.subname subpartition_name,
tsp.subpart# subpartition_position from user$
u, obj$ op, tabsubpartv$ tsp, obj$ os where
u.user# = op.owner# and op.type# = 19 and
op.obj# = tsp.pobj# and tsp.obj# = os.obj#
and os.type# = 34) d where s.c5 = :1 and s.c1 = :2
and s.type in ('T','C','E','P','H','B','t','c','M','U','G','L')
and s.n13 is not null and s.c3 like 'SYS_%' and
s.c5 = d.table_owner and s.c1 = d.table_name and s.c2 =
d.partition_name and s.n13 = d.s
Plan hash value: 4170016247
-------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------------------------
| 0 | INSERT STATEMENT | | | | 19 (100)| |
| 1 | LOAD TABLE CONVENTIONAL | DBMS_STATS_ID_MAP_TAB | | | | |
| 2 | HASH UNIQUE | | 1 | 436 | 19 (6)| 00:00:01 |
| 3 | NESTED LOOPS | | 1 | 436 | 18 (0)| 00:00:01 |
| 4 | NESTED LOOPS | | 1 | 436 | 18 (0)| 00:00:01 |
| 5 | NESTED LOOPS | | 1 | 424 | 16 (0)| 00:00:01 |
| 6 | NESTED LOOPS | | 1 | 396 | 14 (0)| 00:00:01 |
| 7 | MERGE JOIN CARTESIAN | | 1 | 361 | 13 (0)| 00:00:01 |
| 8 | INLIST ITERATOR | | | | | |
|* 9 | TABLE ACCESS BY INDEX ROWID BATCHED| ALL_TABLE_STATS2 | 1 | 346 | 12 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN | ALL_TABLE_STATS2 | 1 | | 12 (0)| 00:00:01 |
| 11 | BUFFER SORT | | 1 | 15 | 1 (0)| 00:00:01 |
| 12 | TABLE ACCESS BY INDEX ROWID | USER$ | 1 | 15 | 1 (0)| 00:00:01 |
|* 13 | INDEX UNIQUE SCAN | I_USER1 | 1 | | 0 (0)| |
|* 14 | INDEX RANGE SCAN | I_OBJ2 | 1 | 35 | 1 (0)| 00:00:01 |
|* 15 | VIEW PUSHED PREDICATE | TABSUBPARTV$ | 1 | 28 | 2 (0)| 00:00:01 |
| 16 | WINDOW BUFFER | | 32 | 480 | 2 (0)| 00:00:01 |
|* 17 | TABLE ACCESS BY INDEX ROWID | TABSUBPART$ | 32 | 480 | 2 (0)| 00:00:01 |
|* 18 | INDEX RANGE SCAN | I_TABSUBPART_POBJSUBPART$ | 32 | | 1 (0)| 00:00:01 |
|* 19 | INDEX RANGE SCAN | I_OBJ1 | 1 | | 1 (0)| 00:00:01 |
|* 20 | TABLE ACCESS BY INDEX ROWID | OBJ$ | 1 | 12 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
9 - filter("S"."N13" IS NOT NULL)
10 - access("S"."STATID"='ALL_TABLE_STATS_ID' AND (("S"."TYPE"='B' OR "S"."TYPE"='C' OR "S"."TYPE"='E' OR
"S"."TYPE"='G' OR "S"."TYPE"='H' OR "S"."TYPE"='L' OR "S"."TYPE"='M' OR "S"."TYPE"='P' OR "S"."TYPE"='T' OR
"S"."TYPE"='U' OR "S"."TYPE"='c' OR "S"."TYPE"='t')) AND "S"."C5"=:1 AND "S"."C1"=:2 AND "S"."C3" LIKE 'SYS_%')
filter(("S"."C2" IS NOT NULL AND "S"."C3" LIKE 'SYS_%'))
13 - access("U"."NAME"=:1)
14 - access("U"."USER#"="OP"."OWNER#" AND "OP"."NAME"=:2 AND "S"."C2"="OP"."SUBNAME" AND "OP"."TYPE#"=19)
filter(("OP"."SUBNAME" IS NOT NULL AND "S"."C2"="OP"."SUBNAME" AND "OP"."TYPE#"=19))
15 - filter("S"."N13"="TSP"."SUBPART#")
17 - filter(BITAND("FLAGS",8388608)=0)
18 - access("POBJ#"="OP"."OBJ#")
19 - access("TSP"."OBJ#"="OS"."OBJ#" AND "OS"."TYPE#"=34)
filter("OS"."TYPE#"=34)
20 - filter("S"."C3"<>"OS"."SUBNAME")
Note
-----
- dynamic statistics used: dynamic sampling (level=2)
press Enter to return
|
DBMS_STATS_ID_MAP_TAB 테이블에 insert를 하는 쿼리임, ALL_TABLE_STATS2 테이블은 이전 테스트와 동일하게 인덱스 range 스캔함
하지만 자세히보면 8번에서 inlist 방식으로 동작하고, id 10에 보면 인덱스 첫번째 컬럼이 null이 아니라 엑세스에 모든 컬럼을 다 잘 이용함
그부분에서 예상 cost도 낮게잡힘
쿼리 비교(v$sql에서 sqlid로 sql_fulltext 확인)
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
#statid 미입력시 쿼리
SQL>
insert /*+ OPT_PARAM('_parallel_syspls_obey_force' 'false') */
into sys.dbms_stats_id_map_tab (c5, c1, c2, c3, cn)
select /*+ leading(s) index(s) */
distinct s.c5, s.c1,s.c2, s.c3, d.subpartition_name cn
from "SYSTEM"."ALL_TABLE_STATS" s
, (select u.name table_owner, op.name table_name, op.subname partition_name, os.subname subpartition_name, tsp.subpart# subpartition_position
from user$ u, obj$ op, tabsubpartv$ tsp, obj$ os
where u.user# = op.owner#
and op.type# = 19
and op.obj# = tsp.pobj#
and tsp.obj# = os.obj#
and os.type# = 34) d
where s.c5 = :1
and s.c1 = :2
and s.type in ('T','C','E','P','H','B','t','c','M','U','G','L')
and s.n13 is not null
and s.c3 like 'SYS_%'
and s.c5 = d.table_owner
and s.c1 = d.table_name
and s.c2 = d.partition_name
and s.n13 = d.subpartition_position
and s.c3 != d.subpartition_name and s.statid is null
#statid 입력시 쿼리
SQL>
insert /*+ OPT_PARAM('_parallel_syspls_obey_force' 'false') */
into sys.dbms_stats_id_map_tab (c5, c1, c2, c3, cn)
select /*+ leading(s) index(s) */
distinct s.c5, s.c1,s.c2, s.c3, d.subpartition_name cn
from "SYSTEM"."ALL_TABLE_STATS2" s
, (select u.name table_owner, op.name table_name, op.subname partition_name, os.subname subpartition_name, tsp.subpart# subpartition_position
from user$ u, obj$ op, tabsubpartv$ tsp, obj$ os
where u.user# = op.owner#
and op.type# = 19
and op.obj# = tsp.pobj#
and tsp.obj# = os.obj#
and os.type# = 34) d
where s.c5 = :1
and s.c1 = :2
and s.type in ('T','C','E','P','H','B','t','c','M','U','G','L')
and s.n13 is not null
and s.c3 like 'SYS_%'
and s.c5 = d.table_owner
and s.c1 = d.table_name
and s.c2 = d.partition_name
and s.n13 = d.subpartition_position
and s.c3 != d.subpartition_name and s.statid = 'ALL_TABLE_STATS_ID'
|
statid 미입력시 쿼리에는 statid is null 조건이 들어가고, statid 입력시 쿼리에는 상수값 조건이 들어감
플랜 비교
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#statid 미입력시 플랜
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------------
..
| 7 | MERGE JOIN CARTESIAN | | 1 | 361 | 942 (0)| 00:00:01 |
|* 8 | TABLE ACCESS BY INDEX ROWID BATCHED| ALL_TABLE_STATS | 1 | 346 | 941 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | ALL_TABLE_STATS | 1 | | 941 (0)| 00:00:01 |
#statid 입력시 플랜
------------------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------------------
..
| 7 | MERGE JOIN CARTESIAN | | 1 | 361 | 13 (0)| 00:00:01 |
| 8 | INLIST ITERATOR | | | | | |
|* 9 | TABLE ACCESS BY INDEX ROWID BATCHED| ALL_TABLE_STATS2 | 1 | 346 | 12 (0)| 00:00:01 |
|* 10 | INDEX RANGE SCAN | ALL_TABLE_STATS2 | 1 | | 12 (0)| 00:00:01 |
|
statid 입력시 플랜이 inlist 방식으로 동작하고 예상값이지만 cost도 낮음
Predicate Information 비교
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#statid 미입력시 Predicate Information
Predicate Information (identified by operation id):
---------------------------------------------------
8 - filter("S"."N13" IS NOT NULL)
9 - access("S"."STATID" IS NULL AND "S"."C5"=:1 AND "S"."C1"=:2 AND "S"."C3" LIKE 'SYS_%')
filter(("S"."C2" IS NOT NULL AND "S"."C1"=:2 AND "S"."C5"=:1 AND "S"."C3" LIKE 'SYS_%' AND
INTERNAL_FUNCTION("S"."TYPE")))
#statid 입력시 Predicate Information
Predicate Information (identified by operation id):
---------------------------------------------------
9 - filter("S"."N13" IS NOT NULL)
10 - access("S"."STATID"='ALL_TABLE_STATS_ID' AND (("S"."TYPE"='B' OR "S"."TYPE"='C' OR "S"."TYPE"='E' OR
"S"."TYPE"='G' OR "S"."TYPE"='H' OR "S"."TYPE"='L' OR "S"."TYPE"='M' OR "S"."TYPE"='P' OR "S"."TYPE"='T' OR
"S"."TYPE"='U' OR "S"."TYPE"='c' OR "S"."TYPE"='t')) AND "S"."C5"=:1 AND "S"."C1"=:2 AND "S"."C3" LIKE 'SYS_%')
filter(("S"."C2" IS NOT NULL AND "S"."C3" LIKE 'SYS_%'))
|
통계 테이블의 인덱스 컬럼이 STATID, TYPE, C5, C1, C2, C3, C4, VERSION 인데
statid 미입력시에는 두번째 컬럼인 type이 INTERNAL_FUNCTION으로 감싸져 있어 access에도 없고 filter에만 존재함
statid 입력시에는 STATID를 먼저 읽고 or로 여러개 작성된 type 컬럼을 inlist로 읽어 모두 비효율을 줄임
통계정보 import가 9분만에 끝남
|
1
2
3
4
5
|
SQL>
set timing on
exec dbms_stats.import_database_stats(statown => 'SYSTEM', stattab => 'ALL_TABLE_STATS2', statid => 'ALL_TABLE_STATS_ID');
(실행완료)
Elapsed: 00:09:04.08
|
9분만에 완료됨
결론 :
이전글에서 대용량 db에서 통계정보 전체를 이관할(import) 경우 일반적인 방법으로 하면 느릴수 있다고 얘기했었음
하지만 이건 인덱스 선두 컬럼인 statid가 null이고, 오라클에서 내부적으로 통계 테이블을 읽을때 inlist가 동작하지 않아 느린것이었음
본문처럼 dbms_stats.export_database_stats에 statid를 넣어주고 import시에도 이 statid를 적어주면 내부적으로 inlist 방식으로 동작해 statid를 사용하지 않은 경우보다 훨씬 빠르게 수행됨
dbms_stats시 statid를 작성하지 않을 경우 기본값이 null이 들어가게됨, 이 부분만 추후 버전에서 datapump를 수행할때 자동으로 할당되는 job 이름처럼 리터럴값이 들어가게끔 패치가 된다면 매우 좋을듯함
만약 export 시 이미 statid를 넣지 않았을 경우 오라클 19c 통계정보 대량 이관시 병렬 처리 ( https://positivemh.tistory.com/1267 ) 게시글처럼 세션을 여러개 나눠서 수행하면 빠르게 import 가능함
참고로 statid 값이 null일때 수동으로 다른값을(IMSI) 넣었을때도 inlist 방식으로 빠르게 import 되었음(이 방식은 오라클이 만든 테이블을 변경하는 방식이기 때문에 비추천함)
|
1
2
3
4
5
6
7
8
9
10
11
12
|
SQL> update system.ALL_TABLE_STATS set statid='IMSI';
1529703 rows updated.
SQL> commit;
Commit complete.
SQL>
set timing on
exec dbms_stats.import_database_stats(statown => 'SYSTEM', stattab => 'ALL_TABLE_STATS', statid => 'IMSI');
(빠름)
|
참조 :
오라클 19c 통계정보 대량 이관시 병렬 처리 ( https://positivemh.tistory.com/1267 )
오라클 19c 유저 100개, 테이블 100개, 인덱스 100개, 데이터 1000건 생성 ( https://positivemh.tistory.com/1266 )
오라클 19c 통계정보 이관 방법 ( https://positivemh.tistory.com/1265 )
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_STATS.html#GUID-0244C71F-40E4-4CC5-A1D3-08C96B8B9E16
https://docs.oracle.com/en/database/oracle/oracle-database/19/arpls/DBMS_STATS.html#GUID-D3FF95A6-ECAE-41B9-90CB-95A68A86AE2D
https://dohdatabase.com/2023/12/18/how-to-export-and-import-statistics-faster-using-dbms_stats-in-parallel/
https://dohdatabase.com/2022/05/21/if-importing-statistics-using-dbms_stats-is-slow/
'ORACLE > Migration' 카테고리의 다른 글
| 오라클 19c to 19c datapump expdp network_link 옵션 사용 (1) | 2025.10.31 |
|---|---|
| 오라클 19c 통계정보 export 관련 궁금증 테스트 (0) | 2025.08.11 |
| 오라클 19c 통계정보 대량 이관시 병렬 처리 (0) | 2025.08.10 |
| 오라클 19c 통계정보 이관 방법 (0) | 2025.08.10 |
| 오라클 11gR2 Raw Device RAC to 19c ASM RAC Rman 백업셋 이용 업그레이드 방법 (0) | 2025.06.22 |
