블로그 이미지
redkite

카테고리

분류 전체보기 (291)
00.SI프로젝트 산출물 (0)
00.센터 운영 문서 (0)
01.DBMS ============.. (0)
01.오라클 (117)
01.MS-SQL (15)
01.MySQL (30)
01.PostgreSql (0)
01.DB튜닝 (28)
====================.. (0)
02.SERVER ==========.. (0)
02.서버-공통 (11)
02.서버-Linux (58)
02.서버-Unix (12)
02.서버-Windows (2)
====================.. (0)
03.APPLICATION =====.. (11)
====================.. (0)
04.ETC =============.. (0)
04.보안 (5)
====================.. (0)
05.개인자료 (1)
06.캠핑관련 (0)
07.OA관련 (1)
Total
Today
Yesterday

달력

« » 2024.5
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

공지사항

최근에 올라온 글

 

 
   

LRU 알고리즘

 

먼저, 데이터베이스의 구조에는 데이터베이스 만을 위한 메모리 영역으로 SGA 영역(데이터버퍼 캐시, 로그버퍼, 공유 풀, 라지 풀 영역)이 있으며 또한 2개의 리스트(LRU-LIST, DIRTY-LIST)가 있습니다. LRU-LIST에는 데이터버퍼 캐시영역의 각 블록구조에 대한 사용 상태정보가 저장되어 있고 DIRTY-LIST에는 사용자에 의해 이미 트랜잭션이 완료된 블록에 대한 상태정보가 저장되어 있습니다.

먼저, LRU 알고리즘이 필요한 이유는 공유 풀 영역은 메모리 영역이기 때문에 그 크기가 항상 제한되어 있으며 항상 여유롭게 사용되지 못하는 물리적 한계를 가지고 있기 때문입니다.

자~ 그럼 LRU 알고리즘의 구체적 실행방법을 알아봅시다.

하나의 사용자가 데이터베이스에 접속하면 사용자 프로세스와 서버 프로세스가 할당됩니다. 사용자가 SQL문을 실행하면 서버 프로세스는 구문분석을 끝낸 후 실행(EXECUTE)작업을 하게되는데 먼저, 데이터버퍼 캐시영역을 검색해서 현재 실행된 SQL문에 있는 테이블이 다른 사용자에 의해 이미 읽혀져서 데이터버퍼 캐시영역에 로더되어 있는지 확인해 봅니다.
읽혀진 테이블 데이터가 없다면 데이터 파일로부터 테이블을 읽어서 데이터버퍼 캐시영역에 로더하게 될 것입니다

이때, 읽혀진 데이터를 데이터버퍼 캐시영역에 저장해야 하는데 데이터버퍼 캐시영역은 수많은 블록들로 구성되어 있기 때문에 매번 이 영역에서 빈 블록을 찾아 읽혀진 테이블 데이터를 저장하는 일은 너무 많은 시간이 필요하게 될 것이고 결국 성능을 저하시키게 될 것입니다.

예를 들어, 도서관의 선반에는 수많은 책들이 비치되어 있을 것입니다. 새로운 도서가 반입되어 책을 비치하려고 하는데 도서관 사서는 어떤 선반이 비어있는지를 책이 반입될 때마다 확인한 후 비치한다면 매번 반입작업을 처리하는데 많은 시간이 소요되어 일의 능률이 오르지 않을 것입니다. 이러한 문제를 극복하기 위해서 선반관리 리스트를 작성하며 책의 이동, 배치현황, 선반관리 등 정보를 미리 만들어 놓고 반입이 될 때마다 이 리스트를 통해 비어있는 선반을 쉽게 찾아낸다면 좋은 성능을 기대할 수 있을 것입니다.

데이터베이스에서도 마찬가지로 서버 프로세스가 매번 빈 블록을 데이터버퍼 캐시영역으로부터 직접 찾아낸다면 너무 많은 시간이 소요될 수 있기 때문에 미리 만들어져 있는 LRU-LIST를 통해 빈 블록을 쉽게 찾아냅니다.

이번에는 LRU-LIST를 통해 빈 블록을 찾아내는 방법입니다.
먼저, 데이터버퍼 캐시영역에는 3가지 종류의 버퍼타입이 있습니다

첫 번째는 [FREE BUFFER]이며 비어있는 버퍼공간으로 새로운 데이터를 저장할 수 있는 공간입니다. 최초 사용자에 의해 읽혀진 테이블 데이터가 저장될 수 있는 블록입니다.

두 번째로는 [PINED BUFFER]인데 [FREE BUFFER]이었다가 사용자에 의해 데이터가 저장된 후 트랜잭션이 진행 중인 데이터를 저장하고 있는 버퍼공간입니다.

마지막으로, [DIRTY-BUFFER]는 사용자에 의해 진행 중인 트랜잭션이 완료된 버퍼공간입니다. 또한, LRU-LIST의 가장 오른쪽을 [LRU-END]라고 하며 가장 왼쪽은 [MRU(MOST RECENTLY USED)-END]라고 합니다. [LRU-END] 부분에는 데이터버퍼 캐시영역의 블록들 중에 가장 오래 전에 사용한 블록 정보들이 배치되고 [MUR-END] 부분에는 가장 최근에 사용된 블록 정보들이 배치됩니다.

 

   
 
 
  

자~ 서버 프로세스에 의해 읽혀진 테이블 데이터를 데이터버퍼 캐시영역의 빈 블록에 저장하기 위해 서버 프로세스는 LRU-LIST의 LRU-END 부분 블록들 부터 MRU-END 부분으로 검색하면서 [FREE BUFFER] 블록을 찾습니다. 먼저, [D5] 블록을 읽었는데 이 블록은 [PINED BUFFER]이군요. 다른 사용자에 의해 사용되고 있는 버퍼공간이므로 이공간을 사용해서는 안될 것입니다.

다음은 [G9] 블록인데 [FREE-BUFFER] 블록이군요. 서버 프로세스는 데이터버퍼 캐시영역으로 가서 이 블록에 테이블 데이터를 저장하게 됩니다. 이제 [G9] 블록은 [FREE-BUFER]이었다가 [PINED-BUFFER]로 속성이 바뀌게 되었고 가장 최근에 사용한 블록이 되었기 때문에 MRU-END 부분으로 블록정보가 이동하게 됩니다. 즉, [A1] 블록정보 왼쪽에 [G9] 블록정보가 배치됩니다. 하나의 블록으로 읽혀진 데이터가 모두 저장되었다면 작업은 끝나게 될 것입니다. 만약, 하나의 블록으로 데이터를 모두 저장하지 못했다면 [FREE BUFFER]를 찾기 위한 노력은 계속 진행됩니다.

다음은 [S3] 블록인데 [PINED BUFFER]이므로 스킵, 다음은 [F2] 블록인데 [DIRTY BUFFER] 블록이군요. 이 블록은 다른 사용자에 의해 저장된 데이터가 COMMIT 또는 ROLLBACK 문장을 만나 트랜잭션이 완전히 끝난 블록이므로 빨리 테이블에 변경된 정보를 저장해야할 블록입니다. [FREE-BUFFER]를 찾기 위해 검색을 하다가 [DIRTY-BUFFER]를 만나게 되면 이 블록의 정보들은 DIRTY-LIST로 이동되어 별도로 관리됩니다. 그리고, 다시 다음 블록을 검색합니다.

이번에는 [K7] 블록, [FREE-BUFFER]이군요. 서버 프로세스는 읽혀진 데이터를 저장합니다. 이런 방법으로 계속해서 읽혀진 데이터를 저장하기 위해 [FREE-BUFFER]를 찾게되는데 너무 많은 사용자들이 이러한 작업을 하다보면 LRU-LSIT를 아무리 검색해도 [FREE-BUFFER]를 찾지 못하는 경우가 발생할 수도 있습니다. 이런 경우에는 DIRTY LIST에 저장된 블록 정보들을 DBWR 백그라운드 프로세스에 의해 테이블에 저장하고 그 블록들을 [FREE-BUFFER]로 만들어 새로운 데이터를 저장하게 됩니다.

서버 프로세스는 이러한 방법으로 데이터 파일로부터 읽은 데이터를 데이터버퍼 캐시영역에 저장하기 위해 [FREE-BUFFER]를 찾게되는데 이런 메커니즘을 LRU 알고리즘이라고 합니다.

  

  

LRU 래치

  

"LRU 알고리즘"에서 사용자가 실행한 SQL문에 의해 테이블 데이터가 데이터버퍼 캐시 영역에 저장되기 위해서는 LRU LIST로부터 FREE BUFFER를 찾아야하는데, 만약 서버 프로세스가 하나의 FREE BUFFER를 찾았다면 이것을 "래치를 얻었다"라고 합니다. 하나의 서버 프로세스가 얻은 하나의 래치는 약 50개의 FREE BUFFER를 동시에 처리할 수 있습니다.

하나의 서버 프로세스는 지금 이 순간에도 하나의 래치를 얻기 위해 다른 서버 프로세스와 경합을 벌여 FREE BUFFER를 찾게 됩니다. 오라클 데이터베이스를 설치하면 LRU 래치의 기본 값은 1입니다. 사용자 수가 많은 데이터베이스 환경이라면 래치를 얻기 위한 경합(CONTENTION)으로 인해 성능이 저하될 수도 있습니다.

래치의 경합현상은 V$LATCH 자료사전을 통해 알 수 있는데 GETS 컬럼은 LATCH를 얻은 수이며 SLEEPS 컬럼은 LATCH를 얻지 못하고 대기(WAITING)했던 수를 의미합니다.
GETS 컬럼에 대해 SLEEPS 컬럼이 99% 이상의 백분율을 보일 때 경합이 발생하지 않기 때문에 좋은 성능을 기대할 수 있습니다. 만약, 백분율이 좋지 못하다면 다음 파라메터를 통해 래치 수를 더 높게 설정해 주어야 합니다.

  
 

DB_BLOCK_LRU_LATCHES = [n]

   

오라클 데이터베이스를 설치하면 LRU 래치의 기본 값은 1입니다. 이 값은 시스템의 CPU 수와 균형을 맞추어서 설정해야 하는데 최대 값은 (CPU 수 X 2 X 3 또는 데이터버퍼 수 / 50)계산에 의해 산출할 수 있으며 CPU수가 하나인 시스템 환경에서 래치수의 증가는 성능에 별로 도움이 안됩니다.

참 !!! 이 기능은 오라클 9i 버전에서 추가된 동적인 SGA 관리 기법을 사용할 때는 설정해 줄 필요가 없습니다. 래치 수를 오라클 서버가 자동으로 관리해 주기 때문입니다.

 
   

 
   

리두로그 버퍼 래치

 
   

오라클 데이터베이스 구조에서 발생하는 두 번째 래치는 리두로그 버퍼영역에서 발생합니다.
많은 사용자들이 동시에 UPDATE, INSERT, DELETE문을 실행하면 변경 행의 이전 데이터와 이후 데이터가 모두 리두로그 버퍼영역에 저장되어야 합니다. 하지만 하나의 구조로 활성화되어 있는 메모리 공간에 많은 사용자들의 변경 데이터가 저장되려면 서로 리두로그 버퍼 영역을 사용하기 위해 경합을 벌여야 합니다. 결국 경합현상으로 인해 대기상태가 발생하게 되고 성능이 저하되게 됩니다.

서버 프로세스가 변경 데이터를 저장하기 위해 먼저 리두로그 버퍼의 공간을 확보하게 되는데 이것을 리두할당 래치(Redo Allocation Latch)라고 합니다. 리두할당 래치를 입수한 서버 프로세스는 변경 데이터를 리두로그 버퍼에 저장해야 하는데 이것을 리두카피 래치(Redo Copy Latch)라고 합니다.

 
   
 

래치의 유형

 

오라클 데이터베이스에서 제공하는 래치는 다음과 같이 크게 2가지 유형이 있습니다.

 
   
 

WILLING TO WAIT 타입

   
  

이 타입은 서버 프로세스가 래치를 이용할 수 없을 때 잠시 기다렸다가 다시 래치를 요청하고 또 이용할 수 없으면 기다렸다가 다시 래치를 요청하게 됩니다. 일정한 회수를 반복적으로 요청하다 계속 래치를 얻지 못하면 수면상태(SLEEP)에 빠졌다가 일정시간이 지나면 다시 래치를 요청하는 방법으로 운영됩니다. 리두할당 래치가 대표적인 WILLING-TO-WAIT 타입입니다.

   
  

SQL>

SELECT b.name, a.misses / a.gets "MISSES/GETS"

 

FROM V$LATCH A, V$LATCHNAME B

 

WHERE b.name = ('redo allocation')

 

And b.latch# = a.latch#;

  

NAME

MISSES/GETS

 

redo allocation

.000023226

redo copy

0

 
   
  

<- WILLING-TO-WAIT 타입의 래치 경합이 발생하면 MISSES/GETS의 결과 값이 1% 이상의 값을 보이게 됩니다. MISSES의 경우는 래치를 얻지 못한 경우이며, GETS는 래치를 얻은 경우를 나타냅니다. 이런 경우에는 리두로그 버퍼가 너무 작아 경합이 발생하는 경우이므로 리두로그 버퍼의 크기를 보다 크게 늘려줘야 합니다.

   
 

IMMEDIATE 타입

   
  

서버 프로세스가 WILLING-TO-WAIT 래치와 같이 요청된 래치를 이용할 수 없을 경우 기다리지 않고 바로 래치를 얻는 타입입니다. 리두카피 래치가 대표적인 IMMEDIATE 타입입니다.

   
  

SQL>

SELECT

b.name,

  

a.immediate_misses / decode((a.immediate_gets + immediate_misses), 0, 1,

  

(a.immediate_gets + a.immediate_misses)) "MISSES/GETS"

 

FROM

V$LATCH A, V$LATCHNAME B

 

WHERE

b.name = ('redo copy')

  

And b.latch# = a.latch#;

   

NAME

 

MISSES/GETS

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

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

redo allocation

 

0

redo copy

 

.000118391

 
   
  

<- IMMEDIATE 타입의 래치 경합이 발생하면 MISSES/GETS의 결과 값이 1% 이상의 값을 보이게 됩니다. MISSES의 경우는 래치를 얻지 못한 경우이며, GETS는 래치를 얻은 경우를 나타냅니다. 이런 경우에는 리두로그 버퍼가 너무 작아 경합이 발생하는 경우이므로 리두로그 버퍼의 크기를 보다 크게 늘려줘야 합니다.

   
 

리두로그 래치의 튜닝

   
 

리두할당 래치에서 MISSES/GET의 비율이 1% 이상의 경합이 발생한다면

 
   
  

LOG_SMALL_ENTRY_MAX_SIZE 파라메터의 값을 감소시켜야 합니다. 만약, 변경 데이터의 양이 이 파라메터의 값보다 작으면 리두할당 래치를 얻은 상태에서 변경 데이터를 복사하는 작업까지 완료할 수 있고, 만약, 크다면 리두할당 래치를 할당 받은 후 래치를 해제하게 됩니다.

   
 

리두카피 래치에서 MISSES/GET의 비율이 1% 이상의 경합이 발생한다면

 
   
  

LOG_SMALL_ENTRY_MAX_SIZE 파라메터의 값이 작아서 리두할당 래치가 너무 빨리 해제되어 리두카피 래치가 시작되는 문제 때문에 발생한 것입니다.
이런 경우에는 리두카피 래치 수를 증가시켜 경합이 발생하지 않도록 해야 합니다.

리두카피 래치의 기본 수는 해당 시스템의 CPU 수이며 최대 CPU수의 2배까지 정의할 수 있습니다. LOG_SIMULTANEOUS_COPIES 파라메터에 의해 리두카피 래치수가 결정됩니다.

Posted by redkite
, |

최근에 달린 댓글

최근에 받은 트랙백

글 보관함