l9791185890302

원문 : 『그림으로 공부하는 오라클 구조 개정판』 , 스기타 아츠시 외 4명 지음, 제이펍

캐시가 왜 필요한가

오라클 DBMS는 기본적으로 디스크에서 데이터를 읽고 쓴다. 그러나 디스크에서 I/O를 하는것은 컴퓨터에겐 매우 느린 동작이다. 이때 캐시라는 것을 쓰면 읽고 쓰는 속도를 더 빠르게 할 수 있다.

캐시는 간단한 튜닝 항목이고 잘 알려진 기능 중 하나지만, 아키텍처를 제대로 이해하지 않고 있다면 ‘가져오고 싶었던 데이터가 캐시에 존재하지 않아 성능이 좋지 않다’와 같은 결과를 초래한다.

캐시란 무엇인가

빈번하게 데이터를 디스크에서 꺼내 오지 않고 메모리에 데이터를 두어 더 빠르게 사용할 수 있도록 하는 것이다.

오라클에서의 데이터 캐시(버퍼 캐시)는 서버 프로세스가 원하는 데이터가 버퍼 캐시에 존재할 때와 존재하지 않을 때로 나뉜다. 캐시에 히트(hit)하지 않을 경우 처리속도가 느린 디스크에서 읽어오므로 그만큼 sql의 처리가 늦어진다.

캐시HIT

캐시히트하지못함

데이터는 블록 단위로 관리

오라클은 블록(Block) 단위로 데이터를 관리한다. I/O 단위도 블록 기반이고 버퍼 캐시도 블록 단위이다. 여기서 블록이란 것은 “정리용 상자”라고 생각하면 된다. 오라클에서는 데이터가 수바이트에서 수천 바이트 이상의 로우가 존재하므로 블록이라는 정리용 상자를 준비해서 보관하는 것이다.

데이터를 한 건만 디스크에서 읽어오려고 해도 필요한 데이터를 포함한 블록 전체가 캐시에 보관된다. 블록 크기는 2KB, 4KB, 8KB, 16KB, 32KB 중에서 선택할 수 있다.

오라클 블록

캐시를 사용한 인덱스

테이블 뿐만 아니라 인덱스도 블록으로 구성되어 있다. 인덱스를 한 블록에 보관할 수 없을 때 여러개의 블록으로 구성한다.

Indexes and Index-Organized Tables

인덱스가 3계층이라고 가정하고 테이블에서 데이터 한건을 꺼내온다고 가정했을 때 시간을 예측해보자. 테이블의 한 블록을 포함해서 총 네 블록을 처리해야 하고 I/O 하나당 10밀리초라고 가정하면 캐시에 적재되어 있지 않았을때는 40밀리초가 걸린다.

인덱스 캐싱

만일 캐시에 데이터가 있을 경우 캐시를 한번만 거치면 되기 때문에 10밀리초면 충분하다.

##프로세스 캐시 공유

프로세스마다 캐시를 가지면 낭비가 많이되고 다른 프로세스에서 변경된 데이터를 볼 수 없는 문제가 발생한다. 그 때문에 어떠한 오라클 프로세스라도 볼 수 있는 메모리를 캐시로서 이용한다.

OS에서는 공유 메모리라는 특수 메모리를 제공한다. 공유 메모리를 사용하면 자신의 메모리 영역에 기록했던 데이터를 다른 프로세스에서 즉시 볼 수 있다. 공유 메모리는 모든 프로세스에서 접근이 가능하며 이러한 공유 메모리를 SGA(System Global Area)라고 한다. 공유하지 않는 메모리의 일부를 PGA(Program Global Area)라고 부른다.

다수의 프로세스로 구성된 오라클 DBMS에서는 반드시 필요한 기능이기도 하지만 누구든지 접근할 수 있으므로 베타적 제어(exclusive control)를 하지 않으면 데이터에 손상을 입을 수 있다. 내부에서 Lock을 사용하며 이 Lock으로 인해 성능 문제가 발생할 수 있다.

공유 메모리에 필요한 설정

오라클 설정파일인 spfileXXXX.ora(XXXX는 DB 식별 ID)에 버퍼 캐시의 크기를 결정하는 DB_CACHE_SIZE라는 파라미터가 있다. 버퍼 캐시는 성능과 직결되기 때문에 크기를 정할 때 신중히 결정해야 한다. 공유 메모리에는 버퍼 캐시 뿐 아니라 공유 풀이나 로그 버퍼 같은 영역도 존재한다.

# 리눅스의 공유 메모리 설정
# /sbin/sysctl -a | grep shm
kernel.shmall = 2097152
kernel.shmmax = 4294967295
kernel.shmmni = 4096

버퍼 캐시를 아예 설정하지 않는 방법이 있는데 부하의 정도에 따라서 오라클이 자동으로 조절하는 방법이다. 공유 메모리에는 버퍼 캐시 외에도 sql 파싱 결과를 보관하는 공유 풀과 같은 다른 영역이 존재하고, 각각 설정하게 될 경우 크기를 하나하나 조절해야한다. SGA만 크기를 설정해 놓으면 나머지는 오라클이 알아서 관리하는 것을 ‘자동 공유 메모리 관리’라고 부른다. SGA와 PGA간 비율도 자동으로 조정하는 ‘자동 메모리 관리’라는 기능도 있다

버퍼 캐시를 정리하는 LRU 알고리즘

버퍼 캐시의 크기는 한정되어 있으므로 관리하고 정리해야한다. 그런 알고리즘 중 하나가 LRU(Least Recently Used)알고리즘이다. 최근에 사용하지 않은 데이터부터 캐시 아웃하는 알고리즘이다. 오라클이 이 LRU알고리즘을 사용한다. 데이터를 읽어오는 것만이라면 이 동작으로도 충분하지만 오라클의 서버 프로세스는 변경한 데이터도 캐시에 둔다.

이는 DBWR이 하는데 단, 변경한 데이터는 디스크에 기록하기 전에 캐시에서 버리면 데이터가 손실되므로 캐시에서 버려지기 전에 디스크에 기록해둘 필요가 있다. 그래서 DBWR은 정기적으로 변경한 데이터를 디스크에 저장한다.

dbwr

또한 자주 사용하지 않는 데이터는 버퍼 캐시에 둘 필요가 없다. 풀 스캔한 데이터를 캐시에 적재할 때는 자주사용하는 데이터를 캐시에서 쫓아내는 일이 발생한다. 따라서 오라클은 큰 테이블이라고 판단하면 버퍼 캐시로 블록을 적재하지 않는다.

OS나 스토리지에 대해서도 생각해야 하는 이유

스토리지 캐시라는 것이 있다. 스토리지에서 데이터를 읽어오는 것은 캐시 덕분에 더 빨라 질 수가 있다. 디스크에 기록해야 하는 데이터를 캐시에 기록하는 것만으로도 OS 관점에서는 I/O를 종료할 수 있기 때문이다.

Swapping

물리 메모리와 디스크 사이에서 블록을 주고 받는 것을 페이징이라고 한다.

OS의 버퍼캐시와 가상 메모리 차이

OS에는 버퍼 캐시와 가상 메모리라고 불리는 기능이 있는데 이 두가지 기능과 오라클의 버퍼 캐시가 같은 것으로 생각해야 한다. OS는 가상 메모리라는 기능을 이용해 실제로 가지고 있는 물리 메모리보다 더 많은 메모리를 사용할 수 있다. 메모리에서 자주 사용되지 않는 데이터를 디스크에 저장한다. 프로세스 관점에서 데이터는 메모리에 있는 것처럼 보이지만 실제는 디스크에 저장된 구성이다.

가상 메모리는 버퍼 캐시와는 반대로 동작하도록 구현되어 있다. 버퍼 캐시는 디스크에 빠르게 접근하기 위해 메모리의 일정 부분을 할당하여 사용한다. 이것에 비해 속도가 느려지더라도 사용할 수 있는 메모리의 크기를 늘리기 위해서 디스크를 사용하는 것이 가상 메모리이다.

만일 버퍼 캐시를 물리 메모리보다 크게 설정한다면 버퍼 캐시를 사용하여 메모리를 고속화한다는 목적이 페이징으로 인해 상쇄되어 버릴 수도 있다.

이는 요즘 같이 메모리가 크고 싼 요즘에는 잘 설정되지는 않는다.