본문 바로가기
Oracle/운영

lob의 용량관리 (deduplicate 와 compress)

by 취미툰 2025. 12. 17.

oracle lob의 기능 중. deduplicate(중복제거)와 compress(압축)기능이 있습니다.

securefile형태에서 사용가능한 기능입니다.

 

deduplicate(중복제거)

동일한 lob데이터를 한번만 저장하고 포인터로 저장하는 매커니즘입니다.

Secure hash index를 사용하여 모든 lob 데이터의 해시값을 계산하고 동일한 내용이면 포인터만 저장합니다.

 

실제로 1000개 행에 동일한 100kb이미지를 저장한다고 했을때 동작과정은 아래와같습니다.

 

1. 1 row insert

 - 한개 row의 image hash값 계산하여 secure hash index에 등록

 - 100kb 데이터 저장

2.동일 이미지 insert 

 - index조회하여 동일 hash값인거 확인 후 포인터(8bytes)만 저장

3. 999번 반복

 

특징

읽기 성능

 - 결과는 동일하며 포인터 -> 원본위치로 이동시 1회 추가 i/o 필요

쓰기 성능

 - 포인터만 저장하므로 극정으로 향상

 - hash 계산 오버헤드는 최소화

복사 성능

 - 원본데이터 공유하여 포인터만 복사하기때문에 성능이 크게 향상(10배 이상)

 

compress(압축)

개별 lob object를 압축 알고리즘으로 축소하는 매커니즘입니다.

단위는 (low/medium/high)세가지가 있습니다.

low - CPU를 최대 3배 적게사용하며 압축률은 medium 대비 80%의 압축률 낮은 특징이 있음.

medium(default) - 일반적인 용도로 사용.CPU 오버헤드 3~5% 

high - CPU 오버헤드 최고이며 압축률도 최고

 

Oracle 23ai부터는 deduplicate 활성화 전 효과를 측정할 수 있습니다.

DBMS_LOB.GET_LOB_DEDUPLICATION_RATIO

 

테스트를 통해 절감효과를 확인해보겠습니다.

 

테스트테이블 생성 스크립트

B(BLOB)과 C(CLOB)으 포함된 테이블이고 파티션테이블로 생성하였습니다.

DROP TABLE LOB_TEST PURGE;
CREATE TABLE YSBAE.LOB_TEST
(
    A   VARCHAR2(10),
    B   BLOB,
    C   CLOB,
    D   VARCHAR2(4000)
)
TABLESPACE USERS
NOLOGGING
LOB (B) STORE AS SECUREFILE 
(
    STORAGE
    (
        INITIAL 8M
        NEXT 1M
    )
    CHUNK 8192
    PCTVERSION 0
)
LOB (C) STORE AS SECUREFILE 
(
    STORAGE
    (
        INITIAL 8M
        NEXT 1M
    )
    CHUNK 8192
    PCTVERSION 0
)
PARTITION BY RANGE (A)
(
    PARTITION PT_LOB_TEST_01 VALUES LESS THAN ('20250101')
    TABLESPACE USERS
    STORAGE
    (
        INITIAL 1M
        NEXT 1M
    )
    NOLOGGING
    NOCOMPRESS
    LOB (B) STORE AS 
    (
        TABLESPACE USERS
        CHUNK 8192
        PCTVERSION 0
    )
    LOB (C) STORE AS 
    (
        TABLESPACE USERS
        CHUNK 8192
        PCTVERSION 0
    )    
    ,
    PARTITION PT_LOB_TEST_02 VALUES LESS THAN ('20250201')
    TABLESPACE USERS
    STORAGE
    (
        INITIAL 1M
        NEXT 1M
    )
    NOLOGGING
    NOCOMPRESS
    LOB (B) STORE AS 
    (
        TABLESPACE USERS
        CHUNK 8192
        PCTVERSION 0
    )
        LOB (C) STORE AS 
    (
        TABLESPACE USERS
        CHUNK 8192
        PCTVERSION 0
    )

);

 

deduplicate 테스트

테이블 생성 직후 메타데이터 확인

select 
table_name,column_name,lob_name,partition_name,lob_partition_name,IN_ROW,compression,deduplication,securefile 
--*
From dba_lob_partitions where table_name='LOB_TEST'
TABLE_NAME   COLUMN_NAME    LOB_NAME                     PARTITION_NAME   LOB_PARTITION_NAME  IN_ROW COMPRESSION DEDUPLICATION   SECUREFILE 
------------ -------------- ---------------------------- ---------------- ------------------- ------- ----------- --------------- ---------- 
LOB_TEST	B	SYS_LOB0000480436C00002$$	PT_LOB_TEST_01	SYS_LOB_P81972	YES	NO	NO	YES
LOB_TEST	B	SYS_LOB0000480436C00002$$	PT_LOB_TEST_02	SYS_LOB_P81973	YES	NO	NO	YES
LOB_TEST	C	SYS_LOB0000480436C00003$$	PT_LOB_TEST_01	SYS_LOB_P81976	YES	NO	NO	YES
LOB_TEST	C	SYS_LOB0000480436C00003$$	PT_LOB_TEST_02	SYS_LOB_P81977	YES	NO	NO	YES 

select owner,segment_name,partition_name,segment_type,blocks,bytes/1024/1024 as MB 
from dba_segments where segment_name in ('LOB_TEST','SYS_LOB0000480436C00002$$','SYS_LOB0000480436C00003$$');

OWNER      SEGMENT_NAME                   PARTITION_NAME     SEGMENT_TYPE       BLOCKS    MB        
---------- ------------------------------ ------------------ ------------------ --------- --------- 
YSBAE	LOB_TEST					PT_LOB_TEST_02	TABLE PARTITION	128	1
YSBAE	LOB_TEST					PT_LOB_TEST_01	TABLE PARTITION	128	1
YSBAE	SYS_LOB0000480436C00003$$	SYS_LOB_P81977	LOB PARTITION	1024	8
YSBAE	SYS_LOB0000480436C00003$$	SYS_LOB_P81976	LOB PARTITION	1024	8
YSBAE	SYS_LOB0000480436C00002$$	SYS_LOB_P81973	LOB PARTITION	1024	8
YSBAE	SYS_LOB0000480436C00002$$	SYS_LOB_P81972	LOB PARTITION	1024	8

 

1.2000건의 데이터를 삽입 후 용량변화 체크

BEGIN
FOR i in 1..2000
LOOP
INSERT INTO LOB_TEST VALUES('20250101',xmlserialize(content xmltype(to_clob('<x>')||lpad('X',4000,'X')||lpad('X',4000,'X')||'</x>') as blob),to_clob(lpad('a',4000,'a')),DBMS_RANDOM.STRING('B',4000));
END LOOP;
COMMIT;
END;
/

 

2.용량 확인

B,C 컬럼 둘다 blocks(1024 -> 3072) , MB(8 -> 24)로 증가된것을 확인할 수 있음.

select owner,segment_name,partition_name,segment_type,blocks,bytes/1024/1024 as MB 
from dba_segments where segment_name in ('LOB_TEST','SYS_LOB0000480436C00002$$','SYS_LOB0000480436C00003$$');

OWNER       SEGMENT_NAME                   PARTITION_NAME   SEGMENT_TYPE       BLOCKS    MB        
----------- ------------------------------ ---------------- ------------------ --------- --------- 
YSBAE	LOB_TEST					PT_LOB_TEST_02	TABLE PARTITION	2048	16 <--
YSBAE	LOB_TEST					PT_LOB_TEST_01	TABLE PARTITION	128	1
YSBAE	SYS_LOB0000480436C00003$$	SYS_LOB_P81977	LOB PARTITION	3072	24 <-- PT_LOB_TEST_02(C) 컬럼의 lob 값
YSBAE	SYS_LOB0000480436C00003$$	SYS_LOB_P81976	LOB PARTITION	1024	8 
YSBAE	SYS_LOB0000480436C00002$$	SYS_LOB_P81973	LOB PARTITION	3072	24  <-- PT_LOB_TEST_02(B) 컬럼의 lob 값
YSBAE	SYS_LOB0000480436C00002$$	SYS_LOB_P81972	LOB PARTITION	1024	8

 

3.deduplicate명령어

ALTER TABLE LOB_TEST MODIFY PARTITION PT_LOB_TEST_02 LOB(B)  (deduplicate);
ALTER TABLE LOB_TEST MODIFY PARTITION PT_LOB_TEST_02 LOB(C)  (deduplicate);

 

기존에 데이터가 있는 상태에서 확인시 오히려 blocks와 MB가 더 늘어난 것을 확인할 수 있습니다.

SQL> select owner,segment_name,partition_name,segment_type,blocks,bytes/1024/1024 as MB 
from dba_segments where segment_name in ('LOB_TEST','SYS_LOB0000480436C00002$$','SYS_LOB0000480436C00003$$');

OWNER  SEGMENT_NAME                PARTITION_NAME   SEGMENT_TYPE       BLOCKS    MB        
------ --------------------------- ---------------- ------------------ --------- --------- 
YSBAE  LOB_TEST                    PT_LOB_TEST_02   TABLE PARTITION         2048        16
YSBAE  LOB_TEST                    PT_LOB_TEST_01   TABLE PARTITION          128         1
YSBAE  SYS_LOB0000480436C00003$$   SYS_LOB_P81977   LOB PARTITION           4096        32 <-- PT_LOB_TEST_02(C) 컬럼의 lob 값 dedupulicate 후에 늘어남.
YSBAE  SYS_LOB0000480436C00003$$   SYS_LOB_P81976   LOB PARTITION           1024         8
YSBAE  SYS_LOB0000480436C00002$$   SYS_LOB_P81973   LOB PARTITION           4096         32 <-- PT_LOB_TEST_02(B) 컬럼의 lob 값 dedupulicate 후에 늘어남.
YSBAE  SYS_LOB0000480436C00002$$   SYS_LOB_P81972   LOB PARTITION           1024         8

SQL> select 
table_name,column_name,lob_name,partition_name,lob_partition_name,IN_ROW,compression,deduplication,securefile 
--*
From dba_lob_partitions where table_name='LOB_TEST';

TABLE_NAME   COLUMN_NAME    LOB_NAME                    PARTITION_NAME     LOB_PARTITION_NAME    IN_ROW COMPRESSION DEDUPLICATION   SECUREFILE 
------------ -------------- --------------------------- ------------------ --------------------- ------- ----------- --------------- ---------- 
LOB_TEST     B              SYS_LOB0000480436C00002$$   PT_LOB_TEST_01     SYS_LOB_P81972        YES      NO          NO              YES       
LOB_TEST     B              SYS_LOB0000480436C00002$$   PT_LOB_TEST_02     SYS_LOB_P81973        YES      NO          LOB             YES        <--deduplication 컬럼이 NO 에서 LOB으로 변경
LOB_TEST     C              SYS_LOB0000480436C00003$$   PT_LOB_TEST_01     SYS_LOB_P81976        YES      NO          NO              YES        
LOB_TEST     C              SYS_LOB0000480436C00003$$   PT_LOB_TEST_02     SYS_LOB_P81977        YES      NO          LOB             YES       <--deduplication 컬럼이 NO 에서 LOB으로 변경

4 rows selected.

 

해당 상태에서 다시 2000건 삽입후 용량 비교

BEGIN
FOR i in 1..2000
LOOP
INSERT INTO LOB_TEST VALUES('20250101',xmlserialize(content xmltype(to_clob('<x>')||lpad('X',4000,'X')||lpad('X',4000,'X')||'</x>') as blob),to_clob(lpad('a',4000,'a')),DBMS_RANDOM.STRING('B',4000));
END LOOP;
COMMIT;
END;
/

 

2000 -> 4000건으로 늘었지만, block수는 B와 C 모두 4096에서 변화없음.

(포인터만 입력되서 실제 데이터는 늘어나지 않는것으로 판단)

select count(*) from lob_test;
4000



SQL> select owner,segment_name,partition_name,segment_type,blocks,bytes/1024/1024 as MB 
from dba_segments where segment_name in ('LOB_TEST','SYS_LOB0000480436C00002$$','SYS_LOB0000480436C00003$$');

OWNER  SEGMENT_NAME                PARTITION_NAME   SEGMENT_TYPE       BLOCKS    MB        
------ --------------------------- ---------------- ------------------ --------- --------- 
YSBAE  LOB_TEST                    PT_LOB_TEST_02   TABLE PARTITION         4096        32
YSBAE  LOB_TEST                    PT_LOB_TEST_01   TABLE PARTITION          128         1
YSBAE  SYS_LOB0000480436C00003$$   SYS_LOB_P81977   LOB PARTITION           4096        32 <-- PT_LOB_TEST_02(C) 컬럼의 lob 값 dedupulicate 후에 늘어남.
YSBAE  SYS_LOB0000480436C00003$$   SYS_LOB_P81976   LOB PARTITION           1024         8
YSBAE  SYS_LOB0000480436C00002$$   SYS_LOB_P81973   LOB PARTITION           4096         32 <-- PT_LOB_TEST_02(B) 컬럼의 lob 값 dedupulicate 후에 늘어남.
YSBAE  SYS_LOB0000480436C00002$$   SYS_LOB_P81972   LOB PARTITION           1024         8

 

+2000건 추가 후 확인

BEGIN
FOR i in 1..2000
LOOP
INSERT INTO LOB_TEST VALUES('20250101',xmlserialize(content xmltype(to_clob('<x>')||lpad('X',4000,'X')||lpad('X',4000,'X')||'</x>') as blob),to_clob(lpad('a',4000,'a')),DBMS_RANDOM.STRING('B',4000));
END LOOP;
COMMIT;
END;
/

 

2000건 추가하여도 마찬가지로 블록의 변화는 없음

select count(*) from lob_test;
6000



SQL> select owner,segment_name,partition_name,segment_type,blocks,bytes/1024/1024 as MB 
from dba_segments where segment_name in ('LOB_TEST','SYS_LOB0000480436C00002$$','SYS_LOB0000480436C00003$$');

OWNER  SEGMENT_NAME                PARTITION_NAME   SEGMENT_TYPE       BLOCKS    MB        
------ --------------------------- ---------------- ------------------ --------- --------- 
YSBAE  LOB_TEST                    PT_LOB_TEST_02   TABLE PARTITION         6144        48
YSBAE  LOB_TEST                    PT_LOB_TEST_01   TABLE PARTITION          128         1
YSBAE  SYS_LOB0000480436C00003$$   SYS_LOB_P81977   LOB PARTITION           4096        32 <-- PT_LOB_TEST_02(C) 컬럼의 lob 값 dedupulicate 후에 늘어남.
YSBAE  SYS_LOB0000480436C00003$$   SYS_LOB_P81976   LOB PARTITION           1024         8
YSBAE  SYS_LOB0000480436C00002$$   SYS_LOB_P81973   LOB PARTITION           4096         32 <-- PT_LOB_TEST_02(B) 컬럼의 lob 값 dedupulicate 후에 늘어남.
YSBAE  SYS_LOB0000480436C00002$$   SYS_LOB_P81972   LOB PARTITION           1024         8

 

결론. deduplicate옵션은 새로 들어올 데이터에 대해서 적용되는것으로 판단되며, 기존에 있는데이터에서 적용시 오히려 용량이 늘어나는 모습을 보임

 

compress 테스트

위의 상황에서 두 컬럼에 대해서 압축을 시도하고 용량이 얼마나 주는지 체크

 

high 옵션사용하여 테스트를 진행하였습니다.

ALTER TABLE LOB_TEST MOVE PARTITION PT_LOB_TEST_02 LOB(C) STORE AS SECUREFILE (TABLESPACE USERS
COMPRESS HIGH
)  UPDATE INDEXES;


ALTER TABLE LOB_TEST MOVE PARTITION PT_LOB_TEST_02 LOB(B) STORE AS SECUREFILE (TABLESPACE USERS
COMPRESS HIGH
)  UPDATE INDEXES;

 

확인

SQL> select 
table_name,column_name,lob_name,partition_name,lob_partition_name,IN_ROW,compression,deduplication,securefile 
--*
From dba_lob_partitions where table_name='LOB_TEST';

TABLE_NAME   COLUMN_NAME    LOB_NAME                    PARTITION_NAME     LOB_PARTITION_NAME    IN_ROW COMPRESSION DEDUPLICATION   SECUREFILE 
------------ -------------- --------------------------- ------------------ --------------------- ------- ----------- --------------- ---------- 
LOB_TEST     B              SYS_LOB0000480436C00002$$   PT_LOB_TEST_01     SYS_LOB_P81972        YES      NO          NO              YES       
LOB_TEST     B              SYS_LOB0000480436C00002$$   PT_LOB_TEST_02     SYS_LOB_P81973        YES      HIGH          LOB             YES        <--compression 컬럼이 NO 에서 high으로 변경
LOB_TEST     C              SYS_LOB0000480436C00003$$   PT_LOB_TEST_01     SYS_LOB_P81976        YES      NO          NO              YES        
LOB_TEST     C              SYS_LOB0000480436C00003$$   PT_LOB_TEST_02     SYS_LOB_P81977        YES      HIGH          LOB             YES       <--compression 컬럼이 NO 에서 high으로 변경

4 rows selected.

select owner,segment_name,partition_name,segment_type,blocks,bytes/1024/1024 as MB 
from dba_segments where segment_name in ('LOB_TEST','SYS_LOB0000480499C00002$$','SYS_LOB0000480499C00003$$');


YSBAE	LOB_TEST	                PT_LOB_TEST_01	TABLE PARTITION	128	    1
YSBAE	SYS_LOB0000480499C00002$$	SYS_LOB_P82062	LOB PARTITION	1024	8 <--PT_LOB_TEST_02(B) 용량이 줌
YSBAE	SYS_LOB0000480499C00003$$	SYS_LOB_P82056	LOB PARTITION	1024	8
YSBAE	SYS_LOB0000480499C00002$$	SYS_LOB_P82052	LOB PARTITION	1024	8
YSBAE	LOB_TEST	                PT_LOB_TEST_02	TABLE PARTITION	6144	48
YSBAE	SYS_LOB0000480499C00003$$	SYS_LOB_P82060	LOB PARTITION	1024	8 <--PT_LOB_TEST_02(C) 용량이 줌
;

 

결론 : 압축 시 용량이 압축률로 인해 신규생성 직후의 블록수만큼의 크기로 줄어듬

 

매커니즘은 다르지만 각 옵션을 사용하면 lob의 용량을 적절하게 관리를 할 수 있을 것 같습니다.

 

출처 : https://docs.oracle.com/en/database/oracle/oracle-database/18/adlob/using-oracle-LOBs-storage.html

 

Database SecureFiles and Large Objects Developer's Guide

Oracle LOB storage has two types, SecureFiles LOB storage and BasicFiles LOB storage, which are used with different types of tablespaces.

docs.oracle.com

 

securefiles-whitepaper-2009-160970.pdf
0.27MB

반응형

댓글