1교시
★ 파티션은 레퍼런스를 꼭 만들어놓도록 하자.
★ 숙제 !!!!!!!!!!!!!!!!!!!!!!
commit 발생시 어떤 일이 일어나는지 / select 문의 처리과정 / DML 문의 처리과정 종이에 써서 제출하기
★ 병렬 처리 parallel query
- SQL 문이 수행해야 할 작업 범위를 여러 개의 작은 단위로 나누어
여러 개의 (서버) 프로세스가 동시에 처리하도록 하는 작업을 의미한다.
(혼자 할 일을 여럿이서 하기 때문에 처리속도가 빨라진다)
샘플 테이블 생성하기
HR@ora11g> drop table hr.emp purge;
HR@ora11g>
create table hr.emp nologging as
select rownum as employee_id, last_name, first_name, hire_date, job_id, salary, manager_id, department_id
from hr.employees e, (select level as id from dual connect by level <= 1000);
Table created.
HR@ora11g>
exec dbms_stats.gather_table_stats('HR', 'EMP', degree => 2)
PL/SQL procedure successfully completed.
degree = 병렬 프로세스 개수, CPU의 2배수까지 쓸 수 있다. (주로 짝수를 쓴다)
degree 2 = 프로세스 2개 = 서버프로세스 1개가 혼자 해야 하는 작업을 둘이 나눠서 한다 = 처리 속도가 2배로 빠르다.
(단 여기서 버추얼로 병렬처리 테스트를 하면 오히려 더 느려진다... 그래도 현장에서는 다르다고 하시니
너무 실망하지 말자.)
통계 정보 확인하기
HR@ora11g> select num_rows, blocks, avg_row_len, logging from user_tables where table_name = 'EMP';
※ 데일리 체크시에는 로깅으로 되어있어야 하는데 로깅으로 되어있는 것들 찾아서 바꿔주는 작업도 꼭 해주자.
(대량의 데이터 부어넣는 작업 시에는 꼭 노로깅으로 바꾸고 나서 해주기)
HR@ora11g> alter table hr.emp logging;
Table altered.
HR@ora11g> select num_rows, blocks, avg_row_len, logging from user_tables where table_name = 'EMP';
arraysize 확인하기
HR@ora11g> show arraysize
arraysize 15 : fetch I/O call 이 너무 많이 발생할 수 있다.
arraysize 1000 으로 조정하기
HR@ora11g> set arraysize 1000
확인
HR@ora11g> show arraysize
arraysize 1000
HR@ora11g> alter session set statistics_level = all;
Session altered.
# 직렬로 풀 테이블 스캔 serial full table scan
HR@ora11g> select /*+ full(e) */ count(*) from emp e;
HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last'));
I/O 754건 발생 + 0.01초 걸림
그럼 이제 병렬처리를 한번 유도해보자.
# 병렬로 풀 테이블 스캔 parallel full table scan (direct path read)
병렬처리 유도 : 힌트를 가지고 프로세스 2개로 e 테이블을 풀 테이블 스캔하도록 함
(DBC 를 건드리지 않고, 프로세스 2개가 디스크에서 바로 커서로 데이터를 읽어들인다.)
HR@ora11g> select /*+ full(e) parallel(e 2) */ count(*) from emp e;
병렬처리한 SQL 문의 실행계획을 볼 때는 뒤에 꼭 parallel 옵션을 표시해주어야 한다.
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel'));
I/O 5 건 발생 + 걸린 시간 0.14 초 (뭐야... 아까 0.01 초였는데 오히려 더 늘어났어...)
그럼 본격적인 병렬처리에 대해 2교시에서 배워보자.
2교시
HR@ora11g> select /*+ full(e) parallel(e 2) */ count(*) from emp e;
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel'));
1행의 SORT AGGREGATE 는 그룹함수를 썼다는 뜻
3행의 QC 는 Query Coordinator 의 약자
★ Query Coordinator (QC)
- 병렬 SQL 문이 발생한 세션
- 작업을 지시하고 일이 잘 진행되는지 관리, 감독하는 역할을 한다. (작업반장)
★ 병렬 서버 프로세스
- 실제 작업을 수행하는 개별 세션
- 병렬 서버 = 병렬 프로세스 = 병렬 슬레이브
★ 병렬 서버 풀 (parallel execution server pool)
- 병렬처리시 서버 풀에 있는 프로세스부터 사용 (미리 만들어놓은 프로세스부터 가져다 쓴다)
- 부족하면 추가로 생성한다.
- 생성할 수 있는 최대 병렬 서버 개수는?
parallel_max_servers 파라미터로 확인 가능
(show parameter parallel_max_servers : sys 창에서 확인해야 함, 최대 80개까지 쓸 수 있다고 나온다)
- 생성할 수 있는 최소 병렬 서버 개수는?
parallel_min_servers 로 확인 가능 (show parameter parallel_min_servers)
- 병렬 처리가 발생하지 않더라도 parallel_min_servers 파라미터에 지정된 개수만큼 병렬프로세스를 유지한다.
이유는? 병렬처리시 서버프로세스를 띄우는 데 드는 부하를 줄이기 위해서
★ 병렬서버집합 (server set) 할당
- 병렬도 (DOP = Degree Of Parallelism) 와 수행하는 오퍼레이션의 종류에 따라
한 개 또는 두 개의 병렬 서버 집합을 할당한다. (뒤에 다시 설명해주신다고 하심)
- 서버 풀 (parallel execution server pool) 로부터 필요한 만큼 (미리 만들어놓은) 서버 프로세스를 확보한다.
- 부족하면 새로 생성한다.
★ IN-OUT 오퍼레이션
아래 IN-OUT 컬럼을 보면: 공백이 있는 부분도 있고 뭔가 써있는 부분도 있는데
1. 공백이 있는 부분: 직렬 작업, 즉 serial 작업이 일어났다는 뜻
2. 뭔가 내용이 있는 부분:
- P → S : Parallel to Serial (3행)
(각 병렬 서버 프로세스가 처리한 데이터를 코디네이터 QC 에게 전송할 때 serial 하게 전송한다는 뜻)
3행을 보면 RANDOM, PQ Distrib 은 QC (RAND) 라고 나오는데 이건 Order by 되지 않고 무작위로 전송되었다는 뜻
즉, RANDOM 은 정렬이 일어나지 않았을 때 표시되며, 병렬 프로세스들이 무순위로 QC 에게 데이터를 전송한다.
- PCWP : Parallel Combined With Parent (4, 5, 6행)
한 서버 집합이 현재 스텝과 그 부모 스텝을 모두 처리하는 기능
(현재도 처리하고 바로 앞도 처리)
- PCWC : Parallel Combined With Child
한 서버 집합이 현재 스텝과 그 자식 스텝을 모두 처리하는 기능
- S → P : Parallel From Serial
코디네이터 (QC) 가 읽은 데이터를, 테이블 큐를 통해 병렬 서버프로세스에게 전달하는 것
- P → P : Parallel to Parallel
데이터 정렬, 또는 데이터를 그룹핑하거나 조인을 위해 동적으로 파티셔닝할 때 사용
첫번째 병렬 서버 집합이 읽거나 가공한 데이터를 두번째 병렬 서버 집합에 전송
order by 절을 쓴 SQL 문을 한번 수행해보자.
HR@ora11g>
select /*+ full(e) parallel(e 2) */ * from emp e
order by last_name;
107000 rows selected.
HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel'));
HR@ora11g>
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
(Producer : 생산자 = 작업을 생성해오는 쪽 = 데이터 추출팀 = 데이터를 추출해서 버퍼에 저장
Consumer : 소비자 = 작업을 처리 = 버퍼에 있는 데이터를 꺼내 소비 = 정렬, 그룹핑, 집계값을 구하는 등의 작업을 한다)
분명 우리는 서버프로세스를 2개 가지고 병렬처리하기로 했는데
QC 는 작업반장이라 제외하더라도 지금 프로세스를 P000, 001, 002, 003 이렇게 4개를 쓰고 있다.
# 병렬도 = 2
# 서버 프로세스 = 병렬도 * 2 = 2 X 2 = 4
그래서 서버프로세스를 4개 쓰고 있는 것
# 통신 채널 수 = 병렬도 ** 2 = 2 ** 2 = 4
(통신채널 : QC 와 서버프로세스들 사이의 연결고리)
칠판 그림
예를 들어, 테이블에서 1000건의 데이터를 찾아야 한다고 하자.
1000건이나 되는 데이터는 혼자 찾는 것보다 여럿이 나눠서 찾는 것이 더 빠르기 때문에
QC 는 4개의 프로세스 P000, P001, P002, P003 에 250건씩 일거리를 주면서 데이터를 찾아오라고 시키고
각 프로세스들은 찾아온 250건 데이터들을 QC 에 다시 전달한다.
그런데 프로세스들이 전달한 1000건 데이터를 QC 혼자서 정렬하려고 하니 너무 힘들다.
이럴 때는: 프로세스들 각각이 자기들이 모아온 데이터들을 먼저 정렬한 다음
QC 에 전달하도록 하면 된다. (이러면 QC 는 merge 만 하면 됨)
하지만 이건 데이터가 작으면 문제가 없는데
프로세스들이 모아온 데이터들이 너무 클 때는 부하가 많이 발생한다.
또 프로세스들 각각이 가져온 데이터들에 중복값이 많이 들어있으면
QC 가 다시 정렬해야 하는데 QC 입장에서는 이게 너무 힘들다.
해결책: 프로세스들을 역할분담시키면 된다.
테이블에서 1천 건의 데이터를 뽑아와야 한다고 할 때
1-500 |
501-1000 |
1행부터 500행까지의 데이터는 P002 가 가져오도록 하고
501행부터 1000행까지의 데이터는 P003이 가져오도록 한다.
(P002 & P003 = 데이터 추출 팀 = server set = 서버집합 1)
데이터 추출팀이 일을 마치면 > 이제 그 수집한 데이터들을 정렬팀에 넘겨주고
정렬팀이 작업할 수 있도록 한다.
(P000 & P001 = 데이터 정렬 팀 = server set = 서버집합 2)
P002, P003 은 데이터 수집 팀
P000, P001 은 데이터 정렬 팀
P002와 P003이 정렬팀에 데이터를 전달해줄 때
P000에는 A-M 사이의 데이터만 정렬하라고 주고, P001에는 N-Z 사이의 데이터만 정렬하라고 주면
P000은 A-M 사이의 데이터만 정렬하면 되고 P001은 N-Z 사이의 데이터만 정렬하면 된다.
이렇게 정렬한 결과를 QC 로 전달하면 → QC 는 merge 만 하면 된다.
3교시
아까의 뷰 조회문을 다시 살펴보자.
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
PROCESS 컬럼을 보면 프로세스들의 목록이 나온다.
맨 위에서부터
P002, P003: 생산자 = 데이터 추출 팀 = 병렬서버집합 1
num_rows 컬럼을 보면 각 프로세스들이 일을 얼마나 많이 했는지 볼 수 있는데
P002는 데이터 56692건 추출했고 P003은 데이터 50308건 추출함...
수행결과가 이렇게 차이나는 건 아마 블록을 기준으로 나누는데
블록마다 HWM 까지 사용한 건수가 달라서 그런 걸로 추측됨.
P000, P001: 소비자 = 데이터 정렬 팀 = 병렬서버집합 2
P000은 A-M 까지의 데이터를 53000 건 정렬
P001은 N-Z 까지의 데이터를 54000 건 정렬
그리고 방금 전까지 소비자였던 P000과 P001이 생산자가 되어서
정렬 결과 set 을 QC 에게 전달 (생산자와 소비자는 뒤바뀔 수도 있다.)
1 | 2 | 3 | 4 | 5 | |
P → P * | 소비자에서 → 생산자로 갑자기 돌변 | P → S * | |||
생산자 (Producer) * | 소비자 (Consumer) * | 생산자 (Producer) * | 소비자 (Consumer) * | ||
P002 P003 |
P000 (A-M) P001 (N-Z) |
P000(A-M) P001(N-Z) |
QC | ||
데이터 추출 병렬서버집합 1 |
테이블 큐 * : TQ10000 |
데이터 정렬 병렬서버집합 2 |
정렬 결과 set 을 QC 에게 전달 |
테이블 큐 * : TQ10001 |
생산자로부터 받은 각각의 정렬 결과를 merge 후 유저에게 전달 |
* 생산자 (Producer): 작업을 생성, 데이터 추출, 데이터를 추출해서 버퍼에 저장
* 소비자 (Consumer): 작업을 처리, 버퍼에 있는 데이터를 꺼내 소비 (정렬, 그룹 작업 수행)
* 테이블 큐: 프로세스 간 통신, 즉 메시지 또는 데이터를 전송하기 위한 통신, 파이프라인 (pipeline)
(실행계획의 Name 컬럼에서 볼 수 있다.)
* P → S : Parallel to Serial (3행)
(각 병렬 서버 프로세스가 처리한 데이터를 코디네이터 QC 에게 전송할 때 serial 하게 전송한다는 뜻)
3행을 보면 RANDOM, PQ Distrib 은 QC (RAND) 라고 나오는데 이건 Order by 되지 않고 무작위로 전송되었다는 뜻
즉, RANDOM 은 정렬이 일어나지 않았을 때 표시되며, 병렬 프로세스들이 무순위로 QC 에게 데이터를 전송한다.
* P → P : Parallel to Parallel
데이터 정렬, 또는 데이터를 그룹핑하거나 조인을 위해 동적으로 파티셔닝할 때 사용
첫번째 병렬 서버 집합이 읽거나 가공한 데이터를 두번째 병렬 서버 집합에 전송
4교시
HR@ora11g>
select /*+ full(e) parallel(e 2) */ department_id, count(*) from emp e
group by department_id;
HR@ora11g>
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel'));
HR@ora11g>
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
이상한 점: P000과 P001이 처리한 데이터의 수가 갑자기 줄어들었다가 > 다시 12건으로 늘어났다.
왜 이러는지 실행계획에서 힌트를 찾아보자.
hash group by 가 2번 발생:
이건 데이터 추출팀에서 데이터를 수집하면서 + 추출한 데이터를 group by 까지 하고 있었음을 의미한다.
그리고 나서 수집한 데이터를 정렬팀에 전달할 때
이미 그룹핑한 데이터를 (n 개의 프로세스들에, 서로 겹치지 않도록) 주고
정렬팀에서 다시 그룹핑한다.
이래서 맨 처음에 12건이 나왔다가 > 나중에 다시 6건으로 줄어들었다가 다시 12건이 되었던 것
# 추출팀은 추출만 하고, 정렬팀에서 group by 하도록 힌트로 유도하기: no_gby_pushdown
HR@ora11g> select /*+ full(e) parallel(e 2) no_gby_pushdown */ department_id, count(*) from emp e
group by department_id; 2
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel'));
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
group by pushdown 을 하지 않았을 때:
(※ select /*+ full(e) parallel(e 2) */ * from emp e
order by last_name; --- 이렇게 하면 group by pushdown 이 돌아간다.)
# group by pushdown 적용
원래 수행되어야 하는 group by 는 ID 컬럼 기준으로 3번이지만
ID 6번에서 먼저 group by 가 수행되었다.
그 이유는 : ID 5번의 TQ10000에게 데이터를 전달하기 전에 데이터를 줄여서 성능을 향상시키기 위함이다.
HR@ora11g>
select /*+ full(e) parallel(e 2) */ * from emp e
order by last_name;
107000 rows selected.
HR@ora11g> select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel'));
5교시
리스트 파티션 테이블을 만들어보자.
HR@ora11g>
drop table hr.emp_part purge;
HR@ora11g>
drop table hr.dept_part purge;
HR@ora11g>
create table hr.emp_part
partition by list(department_id)
(partition p_dept_1 values(10, 20, 30, 40),
partition p_dept_2 values(50),
partition p_dept_3 values(60, 70, 80, 90, 100, 110),
partition p_dept_4 values(default))
as select * from hr.employees;
Table created.
HR@ora11g>
create table hr.dept_part
partition by list(department_id)
(partition p_dept_1 values(10, 20, 30, 40),
partition p_dept_2 values(50),
partition p_dept_3 values(60, 70, 80, 90, 100, 110),
partition p_dept_4 values(default))
as select * from hr.departments;
Table created.
HR@ora11g>
exec dbms_stats.gather_table_stats('HR', 'EMP_PART', granularity => 'auto')
PL/SQL procedure successfully completed.
HR@ora11g>
exec dbms_stats.gather_table_stats('HR', 'DEPT_PART', granularity => 'auto')
PL/SQL procedure successfully completed.
HR@ora11g>
select partition_name, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'EMP_PART';
HR@ora11g>
select * from user_part_key_columns where name = 'EMP_PART';
HR@ora11g>
select partition_name, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'DEPT_PART';
HR@ora11g>
select * from user_part_key_columns where name = 'DEPT_PART';
HR@ora11g>
select /*+ leading(d, e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) */
e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
from dept_part d, emp_part e
where e.department_id = d.department_id;
106 rows selected.
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel partition'));
선생님 결과와 다른 화면이 나옴... 선생님은 6행까지밖에 없는데...
★ full partition wise join
- 조인하려는 두 테이블에 조인 키 컬럼을 기준으로 파티션된 경우
- 데이터를 재분배할 필요가 없다.
- pq_distribute(inner 테이블, outer table distribution 방식, inner table distribution 방식)
- pq_distribute(e, none, none)
: full partition wise join 으로 유도할 때 사용
+ 양쪽 테이블 모두 조인 컬럼에 대해 같은 기준으로 파티셔닝되어 있을 경우 사용한다.
다시 힌트 써서 제대로 된 결과가 나오도록 유도해보기: pq_distribute(e, none, none) 는 재분배 힌트
HR@ora11g>
select /*+ leading(d, e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) pq_distribute(e, none, none) */
e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
from dept_part d, emp_part e
where e.department_id = d.department_id;
106 rows selected.
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel partition'));
힌트를 다시 썼더니 제대로 된 결과가 나왔다. (아까는 3행의 list all 이 없었는데 지금은 생김)
HR@ora11g>
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
HR@ora11g>
select partition_name, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'EMP_PART';
HR@ora11g>
select partition_name, high_value, tablespace_name, num_rows, blocks, avg_row_len
from user_tab_partitions
where table_name = 'DEPT_PART';
6교시
★ partial partition wise join
- 조인 테이블 중 한 쪽에만 파티셔닝이 되어 있을 때
- 파티셔닝이 되어있지 않은 다른 쪽 테이블을, 파티셔닝이 되어있는 테이블과 같은 기준으로 파티셔닝한 다음
풀 파티션 와이즈 조인 full partition wise join 을 수행한다.
- 동적 파티셔닝을 위한 데이터 재분배가 필요하다.
- pq_distribute(inner 테이블(별칭), outer table distribution 방식, inner table distribution 방식)
- pq_distribute(e, none, partition): inner 테이블을 outer 테이블 파티션 기준에 따라 파티셔닝하라는 뜻
당연히 outer 테이블 조인 키 컬럼에 대해 파티셔닝되어 있을 때 작동된다.
none 과 partition 이 서로 뒤바뀌지 않도록 주의!!!!!!!!!!!!!!!!!!!!!!!!!!
테이블 만들기
HR@ora11g> drop table hr.emp_non purge;
Table dropped.
HR@ora11g> create table hr.emp_non as select * from hr.employees;
Table created.
HR@ora11g>
select /*+ leading(d, e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) pq_distribute(e, none, partition) */
e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
from dept_part d, emp_non e
where e.department_id = d.department_id;
106 rows selected.
HR@ora11g>
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel partition'));
HR@ora11g>
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
# pq_distribute(e, partition, none)
# partial partition wise join 일 때 full partition wise join 으로 수행하기 위해서 사용된다.
outer 테이블을 inner 테이블 파티션 기준에 따라 파티셔닝하라는 뜻
부서 테이블 만들기
HR@ora11g>
create table hr.dept_non as select * from hr.departments;
Table created.
HR@ora11g>
select /*+ leading(d, e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) pq_distribute(e, partition, none) */
e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
from dept_non d, emp_part e
where e.department_id = d.department_id;
106 rows selected.
HR@ora11g>
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel partition'));
# 양쪽 테이블이 둘다 빅테이블인데 둘다 파티셔닝되어 있지 않을 경우: 동적 파티셔닝
★ 동적 파티셔닝
- 어느 한쪽도 조인 컬럼에 대해 파티셔닝이 되어있지 않은 상황
- 양쪽 테이블 모두 대용량일 때
# pq_distribute(e, hash, hash) : 조인 키 컬럼을 해시함수에 적용하고, 거기서 반환된 값을 기준으로
양쪽 테이블을 동적으로 파티셔닝하라는 뜻
HR@ora11g>
select /*+ leading(d, e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) pq_distribute(e, hash, hash) */
e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
from dept_non d, emp_non e
where e.department_id = d.department_id;
106 rows selected.
HR@ora11g>
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel partition'));
★ broadcast
- 두 테이블이 모두 파티셔닝되지 않은 경우
- 둘 중 한 데이터 집합은 매우 작을 때 : 그 한 쪽은 굳이 파티셔닝할 필요가 없다. (그냥 스캔해버리는 것이 낫다)
- 브로드캐스트되는 데이터 집합이 매우 작을 때 유용하다.
예를 들어, d 테이블은 대용량인데 e 테이블은 작다고 할 때
outer(build) = dept_non, inner = emp_non
# pq_distribute(e, broadcast, none): 작은 테이블을 큰 테이블 쪽으로 브로드캐스트한다.
HR@ora11g>
select /*+ leading(d, e) use_hash(e) full(d) full(e) parallel(d 2) parallel(e 2) pq_distribute(e, broadcast, none) */
e.employee_id, e.last_name, e.salary, d.department_id, d.department_name
from dept_non d, emp_non e
where e.department_id = d.department_id;
106 rows selected.
HR@ora11g>
select * from table(dbms_xplan.display_cursor(null, null, 'allstats last parallel partition'));
select tq_id, server_type, process, num_rows, bytes, waits
from v$pq_tqstat
order by tq_id, decode(substr(server_type, 1, 4), 'RANG', 1, 'Prod', 2, 'Cons', 3), process;
P002만 일하고 P003은 전혀 일하고 있지 않다.
2024년 2월 27일 (1) | 2024.02.27 |
---|---|
2024년 2월 22일 (1) | 2024.02.22 |
2024년 2월 15일 (1) | 2024.02.15 |
2024년 2월 14일 5교시 + 6교시 (0) | 2024.02.14 |
2024년 2월 14일 4교시 (0) | 2024.02.14 |