FLM

EXEM Knowledge Base

Jump to: navigation, 찾기

목차

[편집] Basic Info

FLM은 오라클의 전통적인 세그먼트 공간 관리 기법으로 최신 버전인 오라클 10g R2에서도 여전히 사용 가능한 기법이다. 단, 오라클 10g R1까지는 FLM이 디폴트 속성이었지만, 오라클 10g R2부터는 ASSM이 디폴트 속성이 되었다. FLM은 Free List Management의 약자로, 말 그대로 프리리스트의 관리를 통해 세그먼트 공간을 관리한다는 것을 의미한다. 프리리스트(Freelist)는 프리 블록의 목록을 의미한다. 프리리스트를 이해하려면 우선 프리 블록의 의미를 정확하게 이해해야 한다.

[편집] 프리 블록

세그먼트 레벨에서의 프리 블록(Free Block)이란 INSERT 작업을 위해 사용 가능한 여유 블록을 의미한다. 다음과 같은 블록들이 프리 블록으로 분류된다.

  • 익스텐트가 추가로 할당되는 과정에서 생긴 HWM 아래에 존재하는 한번도 사용되지 않은 블록
  • INSERT 문에 의해 로우(Row)가 추가되었지만, 아직 PCTFREE 속성에 의해 지정된 영역을 다 사용하지 않은 블록
  • PCTFREE 속성에 의해 지정된 영역을 다 소모한 후, 다시 DELETE나 UPDATE에 의해 PCTUSED 속성에 의해 지정된 영역만큼 사용량이 낮아진 블록

프리 블록 여부를 결정하는 요소는 세그먼트 생성시 부여하는 PCTFREE 속성과 PCTUSED 속성이다. 아래 그림을 보면 FLM에서 PCTFREE 속성과 PCTUSED 속성이 어떻게 프리 블록 여부를 결정하는지 직관적으로 이해할 수 있다. 그림:Pctfree와 pctused2.jpg

[편집] 프리리스트 개념

프리리스트란 프리 블록을 리스트 형태로 관리한다는 것을 의미한다. 세그먼트 헤더 블록(Segment Header Block)에 프리리스트의 머리(Header)와 꼬리(Tail) 값을 가지고 있으며, 각각은 프리 블록의 DBA(Data Block Address) 값을 가지고 있다. 각 프리 블록들은 다음 프리 블록(Next Free Block)에 대한 DBA 값을 가지고 있다. 즉 프리리스트란 세그먼트 헤더의 {머리/꼬리}{프리 블록}{프리 블록} … 으로 이루어진 리스트를 의미한다.

오라클은 총 세가지 종류의 프리리스트를 사용한다.

  • 마스터 프리리스트(Master Freelist. MFL): 세그먼트당 하나씩 존재하는 메인 프리리스트를 의미한다. 모든 프리 블록들은 반드시 마스터 프리리스트를 통해 관리된다. 가령 익스텐트 할당이나 HWM 이동에 의해 새롭게 할당된 프리 블록들은 우선 마스터 프리리스트에 속하게 된다.
  • 프로세스 프리리스트(Process Freelist. PFL): 세그먼트 생성시 FREELISTS 속성에 의해 부여되는 프리리스트를 의미한다. 가령 FREELISTS 속성값을 10으로 주면 총 10개의 프로세스 프리리스트가 생성된다. 세그먼트를 사용하는 서버 프로세스들은 10개의 프로세스 프리리스트 중 하나를 사용하게 되며, 각 프로세스 프리리스트들은 마스터 프리리스트로부터 필요한 수만큼의 프리 블록을 할당 받아 프로세스에게 분배한다. 즉, 프로세스 프리리스트는 여러 프로세스가 동시에 같은 프리 블록을 차지하기 위해 경쟁하는 것을 방지하는 역할을 수행한다.
  • 트랜잭션 프리리스트(Transaction Freelist. TFL): 트랜잭션을 수행하는 도중 프리 블록으로 변한 블록들의 목록을 관리하다. 가령 트랜잭션 내에서 특정 블록에서 로우를 DELETE한 후 이 블록의 상태가 프리 블록으로 변했다고 하자. 오라클은 이 블록을 즉시 마스터 프리리스트로 반환하는 대신에 트랜잭션 기간 동안 트랜잭션 프리리스트 내에 자신만의 프리 블록으로 사용하게 된다.

아래 그림을 보면 프리리스트의 관리 방법을 개략적으로 알 수 있다. 그림:프리리스트관리기법2.jpg

[편집] 프리리스트와 프리 블록 경합

동시에 여러 프로세스가 같은 세그먼트에 대해 INSERT 작업을 수행하는 경우 프리 블록을 둘러싼 경합이 발생할 수 있다. INSERT 과정에서 프리 블록에 대한 경합이 발생하는 경우 buffer busy waits 이벤트 대기로 관찰된다. 만일 RAC 환경이라면 gc buffer busy 이벤트나 gc current request 류의 이벤트에 대한 대기로 관찰된다.

오라클은 프리 블록에 대한 경합을 감소시키기 위한 방안으로 프로세스 프리리스트 기능을 제공한다. 가령 10개의 프로세스가 동시에 같은 세그먼트에 대해 INSERT 작업을 수행하는 경우를 가정해보자. 모든 프로세스가 마스터 프로세스로부터 프리 블록을 할당 받는다면, 동일 프리 블록을 사용하게 되고 프리 블록에 대한 경합이 생기게 된다. 대부분의 프로세스들이 동일 프리 블록에 대한 buffer busy waits 이벤트를 대기하는 것으로 관찰될 것이다. 반면에 FREELISTS 속성을 통해 10개의 프로세스 프리리스트를 생성한 경우에는 대부분의 프로세스들이 자신만의 프리리스트를 사용하게 된다. 따라서 각 프로세스들이 서로 다른 프리 블록을 사용하게 되며 블록 경합도 현저하게 감소한다.

불행하게도 FLM을 사용하는 경우 테이블 세그먼트의 FREELISTS 속성의 디폴트 값은 “1”이다. 이는 오직 하나의 프리리스트(마스터 프리리스트)만이 사용된다는 의미이며, 모든 프로세스들이 같은 프리 블록을 사용하게 된다는 것을 의미한다. FLM을 사용하는 경우에 발생하는 성능 문제 중 많은 부분이 이 잘못된 FREELISTS 속성 값에 기인한다. 따라서 동시성이 높은 시스템, 즉 동시에 많은 수의 프로세스가 동일 세그먼트에 대한 INSERT 작업을 수행하는 시스템에서는 반드시 FREELISTS 속성값을 적절하게 부여해야 한다. FREELISTS 속성값을 부여하는 방법은 아래와 같다.

CREATE TABLE … STORAGE ( FREELISTS 10 )

[편집] 프리리스트 그룹과 글로벌 프리 블록 경합

RAC와 같은 멀티 인스턴스 환경에서는 글로벌 프리 블록 경합이 발생할 수 있다. 프로세스 프리리스트를 부여해서 로컬 노드에서의 프리 블록 경합을 해소했다고 하더라도 다른 노드가 서로 같은 프리 블록을 사용하게 된다면 글로벌 프리 블록 경합에 의해 성능이 저하될 수 있다. 글로벌 프리 블록 경합이 발생하는 경우에는 gc buffer busy 이벤트나 gc current request 류의 이벤트에 대한 대기가 발생한다.

오라클은 글로벌 프리 블록 경합 해소를 위해서 프리리스트 그룹(Freelist Group) 기능을 제공한다. 프리리스트 그룹이란 각 인스턴스 별로 별도의 프리리스트를 사용한다는 것을 의미한다. 가령 프리리스트 그룹 수가 2개라고 하면, 1번 인스턴스는 프리리스트 그룹 1번을, 2번 인스턴스는 프리리스트 그룹 2번을 사용한다. 각 프리리스트 그룹은 독자적인 마스터 프리리스트와 프로세스 프리리스트들을 사용한다. 따라서 각 인스턴스 간에 같은 프리 블록을 사용하지 않게 되고, 자연스럽게 글로벌 프리 블록 경합이 줄어든다.

각 세그먼트의 프리리스트 그룹 수는 FREELIST GROUPS 속성에 의해 지정되며, 클러스터에 속한 인스턴스 수만큼 지정하면 된다. 가령 2개의 노드로 이루어진 RAC에서 글로벌하게 액세스되는 세그먼트에 대해서는 다음과 같이 속성을 지정하면 된다.

CREATE TABLE … STORAGE ( FREELISTS 10 FREELIST GROUPS 2 )

프리리스트 그룹의 한가지 단점은 데이터 비대칭(Data Skew) 현상이 발생할 수 있다는 것이다. 가령 인스턴스 1번에서는 테이블에 대해 INSERT 작업만을 수행하고, 인스턴스 2번에서는 DELETE 작업만을 수행하는 경우를 가정해보자. 만일 프리리스트 그룹 속성이 지정되지 않았다면 2번 인스턴스의 DELETE 작업에 의해 확보된 프리 블록들이 1번 인스턴스의 INSERT 작업에 재활용된다. 하지만 프리리스트 그룹 속성이 지정되어 두 인스턴스가 각각 다른 프리리스트 그룹을 사용하는 경우에는, 2번 인스턴스의 DELETE 작업에 의해 확보된 프리 블록들이 1번 인스턴스의 INSERT 작업에 재활용될 수 없다. 따라서 세그먼트의 여유 공간이 충분한데도 불구하고 계속해서 새로운 익스텐트를 할당 받는 현상이 발생하게 된다. 데이터 비대칭 현상이 발생하는 경우는 다음과 같다.

  • 특정 인스턴스는 INSERT 작업을, 다른 인스턴스는 DELETE 작업을 집중적으로 수행하는 경우
  • 특정 인스턴스가 비정상적으로 종료된 경우

데이터 비대칭 현상은 프리리스트 그룹의 구현 방식에 의해 필연적으로 발생하는 것으로 프리리스트 그룹을 사용하지 않거나 ASSM을 사용하는 것 외에는 별다른 해결책이 없다. 아래 예제는 2개의 노드로 이루어진 RAC 시스템에서 FLM을 사용하는 경우, 프리리스트 그룹 설정에 따라 글로벌 프리 블록 경합이 얼마나 영향을 받는지를 테스트한 결과이다. 프리리스트 그룹을 노드 수와 동일하게 2로 설정한 경우 글로벌 프리 블록 경합이 현저하게 줄어드는 것을 확인할 수 있다.

-- FLM을 사용하는 테이블스페이스를 생성한다.
CREATE TABLESPACE tbs_gc_buffer_busy
DATAFILE '&1' SIZE 20M
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 10M
SEGMENT SPACE MANAGEMENT MANUAL;

-- Case 1: 프리리스트 그룹을 부여하지 않은 경우
-- FREELIST GROUPS=1, FREELISTS=20의 스토리지 속성을 지니는 테이블을 생성한다.
CREATE TABLE t_gc_buffer_busy
(
	id		NUMBER,
	name		VARCHAR2(700)
) 
STORAGE ( FREELISTS 20 )
TABLESPACE tbs_gc_buffer_busy;

-- 각 노드에서 10개의 세션이 동시에 INSERT를 수행한 경우 gc buffer busy 이벤트나 gc current request 류의 이벤트가 보편적으로 발생하는 것을 확인할 수 있다.
Type=EVENT, Name=enq: HW - contention, Value=19586(cs)
Type=EVENT, Name=log buffer space, Value=16306(cs)
Type=EVENT, Name=gc buffer busy, Value=10151(cs)
Type=EVENT, Name=gc current block 2-way, Value=3862(cs)
Type=EVENT, Name=gc current block busy, Value=3414(cs)
Type=EVENT, Name=enq: TX - row lock contention, Value=3156(cs)
Type=EVENT, Name=log file switch completion, Value=3095(cs)
Type=EVENT, Name=log file switch (checkpoint incomplete), Value=848
Type=EVENT, Name=buffer busy waits, Value=685(cs)
Type=EVENT, Name=gc cr block busy, Value=413(cs)
Type=EVENT, Name=read by other session, Value=313(cs)
Type=EVENT, Name=latch: redo copy, Value=276(cs)
Type=EVENT, Name=latch: library cache, Value=267(cs)
Type=EVENT, Name=library cache load lock, Value=233(cs)
Type=EVENT, Name=library cache pin, Value=230(cs)
Type=EVENT, Name=gc current block congested, Value=116(cs)
Type=EVENT, Name=latch: shared pool, Value=84(cs)
Type=EVENT, Name=gc cr block 2-way, Value=59(cs)
Type=EVENT, Name=gc current grant 2-way, Value=53(cs)
Type=EVENT, Name=db file sequential read, Value=49(cs)
Type=EVENT, Name=gc cr failure, Value=46(cs)
Type=EVENT, Name=gc current multi block request, Value=42(cs)
Type=EVENT, Name=latch: library cache pin, Value=40(cs)
Type=EVENT, Name=gc cr multi block request, Value=38(cs)
Type=EVENT, Name=latch: cache buffers chains, Value=27(cs)
Type=EVENT, Name=enq: TM - contention, Value=20(cs)
Type=EVENT, Name=latch: redo allocation, Value=17(cs)
Type=EVENT, Name=latch free, Value=14(cs)
Type=EVENT, Name=enq: JQ - contention, Value=13(cs)
Type=EVENT, Name=library cache lock, Value=12(cs)
Type=EVENT, Name=gc current grant busy, Value=8(cs)
Type=EVENT, Name=rdbms ipc reply, Value=6(cs)
Type=EVENT, Name=row cache lock, Value=2(cs)
Type=EVENT, Name=gc current grant congested, Value=2(cs)
Type=EVENT, Name=gc current retry, Value=1(cs)
Type=EVENT, Name=cr request retry, Value=0(cs)
Type=EVENT, Name=lock escalate retry, Value=0(cs)
Type=EVENT, Name=db file parallel read, Value=0(cs)
Type=EVENT, Name=latch: cache buffers lru chain, Value=0(cs)
Type=EVENT, Name=latch: enqueue hash chains, Value=0(cs)
Type=EVENT, Name=db file scattered read, Value=0(cs)

-- Case 2: 프리리스트 그룹을 두 개(FREELIST GROUPS = 2)로 설정한 경우
-- FREELIST GROUPS=2, FREELISTS=20의 스토리지 속성을 지니는 테이블을 생성한다.
CREATE TABLE t_gc_buffer_busy
(
	id		NUMBER,
	name	VARCHAR2(700)
) 
STORAGE ( FREELIST GROUPS 2 FREELISTS 20 )
TABLESPACE tbs_gc_buffer_busy;

-- 각 노드에서 10개의 세션이 동시에 INSERT를 수행한 경우 gc buffer busy 이벤트나 gc current request 류의 이벤트가 여전히 발생한다. 하지만 프리리스트 그룹을 지정하지 않은 경우에 비해 현저하게 감소한 것을 확인할 수 있다.
Type=EVENT, Name=log buffer space, Value=37493(cs)
Type=EVENT, Name=log file switch (checkpoint incomplete), Value=12070(cs)
Type=EVENT, Name=enq: HW - contention, Value=4098(cs)
Type=EVENT, Name=log file switch completion, Value=3397(cs)
Type=EVENT, Name=enq: TX - row lock contention, Value=2538(cs)
Type=EVENT, Name=gc buffer busy, Value=1319(cs)
Type=EVENT, Name=buffer busy waits, Value=1078(cs)
Type=EVENT, Name=latch: redo copy, Value=564(cs)
Type=EVENT, Name=gc current block busy, Value=528(cs)
Type=EVENT, Name=latch: library cache, Value=398(cs)
Type=EVENT, Name=gc cr block busy, Value=286(cs)
Type=EVENT, Name=library cache pin, Value=230(cs)
Type=EVENT, Name=latch: library cache pin, Value=114(cs)
Type=EVENT, Name=gc current multi block request, Value=86(cs)
Type=EVENT, Name=library cache load lock, Value=84(cs)
Type=EVENT, Name=gc current grant 2-way, Value=83(cs)
Type=EVENT, Name=read by other session, Value=48(cs)
Type=EVENT, Name=db file sequential read, Value=40(cs)
Type=EVENT, Name=gc cr multi block request, Value=33(cs)
Type=EVENT, Name=latch: redo allocation, Value=21(cs)
Type=EVENT, Name=gc cr block 2-way, Value=19(cs)
Type=EVENT, Name=gc current grant busy, Value=18(cs)
Type=EVENT, Name=enq: JQ - contention, Value=16(cs)
Type=EVENT, Name=gc cr failure, Value=12(cs)
Type=EVENT, Name=latch: shared pool, Value=12(cs)
Type=EVENT, Name=enq: TM - contention, Value=12(cs)
Type=EVENT, Name=latch free, Value=4(cs)
Type=EVENT, Name=gc current block 2-way, Value=4(cs)
Type=EVENT, Name=library cache lock, Value=4(cs)
Type=EVENT, Name=latch: ges resource hash list, Value=3(cs)
Type=EVENT, Name=latch: cache buffers chains, Value=2(cs)
Type=EVENT, Name=row cache lock, Value=1(cs)
Type=EVENT, Name=gc cr block congested, Value=1(cs)
Type=EVENT, Name=gc current retry, Value=1(cs)
Type=EVENT, Name=rdbms ipc reply, Value=0(cs)
Type=EVENT, Name=gc cr grant 2-way, Value=0(cs)
Type=EVENT, Name=cr request retry, Value=0(cs)
Type=EVENT, Name=latch: redo writing, Value=0(cs)
Type=EVENT, Name=lock escalate retry, Value=0(cs)

[편집] Analysis Case

[편집] RAC 환경에서 동일 테이블 DML에 의한 성능 저하 현상 분석

일반적으로 RAC환경에서 업무 파티셔닝은 성능관리에 있어 중요한 항목이다. RAC 각 Node에서 동일한 테이블에 대한 DML을 수행할 경우에는 Single Node환경보다 더 심각한 성능 저하가 발생할 가능성이 있게 된다. 각 Node에서 동일한 테이블에 대해 Delete/Insert 작업이 동시에 발생되었을 경우의 성능 저하 현상을 Oracle DBMS의 성능진단/분석 툴인 MaxGauge(맥스게이지)를 활용하여 분석해보기로 한다.

[편집] 성능저하구간의 확인

성능문제가 발생한 「RAC02 」 인스턴스에서 수집된 가동이력로그로부터 일간 추이그래프를 확인해 보면, 「CPU사용률」에는 뚜렷한 변화가 없어 보이지만, 23시24분~23시28분 사이에 「Active Session」의 수가 급증 하는 것을 쉽게 확인할 수 있다.

■ CPU 사용률의 추이 그래프 그림:Case2_1.jpg

■ Active Session 수의 추이 그래프 그림:Case2_2.jpg

■ Wait Events의 추이그래프(Wait Time) 그림:Case2_3.jpg

[편집] 성능저하구간 Zoom Up

문제 구간을 편리하게 확인하기 위해 23시20분~23시40분의 데이터를 확인해본 결과 23시 25분부터 성능문제가 발생하였고, 23시27분이 Peak 시점임을 알 수 있다.

그림:Case2_4.jpg

[편집] Wait Event 검출 및 분석

Active Session의 급증으로 인한 성능저하(Performance Slow-Down)의 원인을 규명하기 위해, 문제 Peak 시점(23시27분)의 Wait Events 발생내용을 확인해 본다.

그림:Case2_5.jpg

「Value」탭에서 동 시점의 Top Wait Event를 확인한 결과, Idle Events (SQL*Net message from client, rdbms ipc message)를 제외한 Top Wait Events는 buffer busy waits, latch free, buffer busy global CR 순임을 알 수 있다.

「세션 Grid」 화면에서 동 시점의 세션들의 대기이벤트와 SQL을 확인해 본다.

그림:Case2_6.jpg

확인 결과, 모든 세션들이 동일한 SQL (Insert 문장)을 수행중임을 알 수 있으며, buffer busy global CR 이벤트를 대기하는 세션은 모두 동일한 file#(34) block#(321)을 대기하고 있다. buffer busy wait 이벤트를 대기하는 세션도 대부분은 동일한 file#, block#를 대기하며 P3(reason code)=220임을 알 수 있다. buffer busy global CR 이벤트는 RAC와 관련 있는 대기이벤트이므로 RAC관련 성능 정보를 확인해 보기로 하자.

[편집] RAC 관련 성능 정보 분석

buffer busy global CR 이벤트는 상대 Node로부터 블록을 전송 받는 것과 관련된 대기이벤트이므로, global cache current blocks received, global cache cr block received 성능 지표의 추이를 확인해보자.

그림:Case2_7.jpg

확인 결과, global cache current blocks received 수치가 가장 높은 시점부터 성능 저하가 발생하였고, 성능 저하 구간 동안 global cache cr blocks received 수치도 지속적으로 높게 나타나고 있음을 알 수 있다. 즉, 이 구간에 상대 노드에서 「RAC02 」 인스턴스에서 Insert를 위해 사용하려는 블록에 대한 DML을 수행하고 있다는 것을 알 수 있다.

[편집] RAC 상대 노드 수행 분석

「RAC02 」 인스턴스에서 발생한 global cache current blocks received 와 global cache cr blocks received 가 「RAC01 」 인스턴스와 관련 있는지 재 확인을 위해 「RAC01 」 인스턴스에서 global cache current blocks served와 global cache cr blocks served 지표의 추이를 확인해보자.

그림:Case2_8.jpg

확인 결과, 2개의 지표간의 수치가 정확히 일치하는 것을 알 수 있다. 즉, 「RAC01 」 인스턴스에서 CURRENT 블록을 초당 60 블록 보내고, 「RAC02 」 인스턴스에서 CURRENT 블록을 초당 60 블록 받았음을 알 수 있으며, 「RAC01 」 인스턴스에서 CR 블록을 초당 400 블록 보내고 「RAC02」 인스턴스에서 CR 블록을 초당 400 블록 받았음을 알 수 있다. 이제 「RAC01」 인스턴스에서 어떠한 작업이 수행되었는지 알아보자.

「세션 Grid」에서 23시 25분 시점의 세션들이 수행한 SQL을 확인해 본다.

그림:Case2_9.jpg

확인 결과, 1개의 세션에서 「RAC02 」 인스턴스에서 Insert 하는 테이블과 동일한 테이블에 대한 Delete 작업을 수행하고 있다.

[편집] 세션 상세 분석을 통한 문제 원인 규명

「RAC02」 인스턴스에서 성능 저하를 겪고 있는 세션을 분석해보면, Insert 문장 1번을 수행(Execute Count=0임)하기 위해, buffer busy global CR, latch free, buffer busy waits 이벤트들을 반복적으로 대기하고 있다. 또한 buffer busy global CR 이벤트의 P1(File#=34) P2(Block#=321)를 이용하여 DBA_SEGMENTS 뷰를 조회해본 결과 해당 블록은 테이블 헤더 블록임을 알 수 있었다.

그림:Case2_10.jpg

[편집] 결론

지금까지의 MaxGauge 분석 데이터를 이용하여 다음과 같은 결론을 낼 수 있다.

1. 「RAC01 」 인스턴스에서 수행된 동일 테이블 Delete에 의해 Free 블록이 발생하였다.

2. 발생된 Free 블록은 테이블 헤더 블록의 프리 리스트(Free Lists)에 등록된다.

3. 「RAC02 」 인스턴스는 Insert를 위한 Free 블록을 검색하기 위해 헤더 블록의 프리 리스트 검색이 필요하다.(FLM 환경에서 FREELIST GROUPS 가 1인 경우). 이때 다른 세션이 해당 블록을 전송 받는 중이라면 buffer busy global CR 이벤트를 대기할 수 있다.

4. Free 블록을 할당 받으면, 해당 블록에 Insert를 수행한다.

5. 이때 다른 세션에 동일 블록에 Insert를 수행 중이라면 buffer busy waits (P3=220)를 대기하게 된다.

즉, 이러한 현상은 FLM(FreeList Management)환경에서 FREELIST GROUPS를 1로 설정하였기 때문에 발생한 것으로 볼 수 있다.

[편집] 해결방안

해결방안 1. FREELIST GROUPS 파라미터를 2로 하여 테이블 재 생성.

해결방안 2. ASSM (Automatic Segment Space Management) 방식으로 변경