-
그림으로 공부하는 오라클 구조 : 캐시와 공유 메모리STUDY/DB 2022. 5. 8. 22:03
1. 캐시
일반적으로 캐시를 '작업장'이나 '작업대'에 비교
캐시의 목적은 빈번하게 사용하는 데이터를 매번 디스크에서 꺼내오지 않고 캐시라고 불리는 메모리에 둠으로써 빠르게 사용할 수 있도록 하는 것이다.
메인 메모리에는 프로세스가 사용한 메모리(변수 등)는 물론, 캐시가 놓여 있다. 디스크의 데이터를 처리해야 할 때, 메모리(캐시)에 같은 데이터가 존재하면 디스크에서 읽을 필요없이 CPU에게 데이터를 직접 건네줄 수 있다.
- 오라클에서의 데이터 캐시(버퍼 캐시) 동작
- 버퍼 캐시에 놓여 있는지 확인
- 데이터를 버퍼 캐시에서 빠르게 꺼내와서 처리
캐시를 히트하면 처리 속도가 느린 디스크를 사용할 필요가 없기 때문에 SQL문의 처리 속도가 빨라진다.
- 버퍼 캐시에 놓여 있는지 확인
- 데이터를 디스크에서 읽어 옴
- 읽어온 데이터를 캐시에 보관
- SQL문을 처리
캐시에 히트되지 않으면 처리 속도가 느린 디스크에서 읽어올 수밖에 없으므로 그만큼 SQL의 처리가 느려진다.
2. 데이터 관리
오라클은 '블록'이라고 하는 단위로 데이터를 관리한다. I/O의 단위도 블록을 기반으로 하고 있으며, 버퍼 캐시도 블록 단위로 관리하고 있다.
한 개의 블록에는 여러 건의 데이터가 보관돼 있으므로 한 건만을 디스크에서 읽어오려고 해도 필요한 데이터를 포함하고 있는 블록 전체가 캐시에 보관된다.
* 블록 크기는 2KB, 4KB, 8KB, 16KB, 32KB 중 선택 가능.
3. 캐시를 사용한 인덱스 검색
테이블뿐만 아니라 인덱스도 블록으로 구성되어 있다. 인덱스를 한 블록에 보관할 수 없을 때는 여러 개의 블록으로 구성하고, 인덱스의 크기가 매우 작지 않은 한 계층 구조이다.
I/O 한 번당 10밀리초라고 가정했을 때, 캐시에 적재되어 있지 않을 때는 I/O만 40밀리초가 걸리게 된다.
- 버터 캐시에 인덱스의 루트 블록이 놓여있는지 확인 -> 인덱스의 루트 블록을 디스트에서 읽어옴(10밀리초) -> 읽어온 데이터를 버퍼 캐시에 두고, 그 다음 블록을 확인
- 버퍼 캐시에 32번 블록이 놓여 있는지 확인 -> 32번 블록을 디스크에서 읽어옴(10밀리초) -> 32번 블록을 캐시에 보관하고, 그 다음 블록 확인
- 버퍼 캐시에 151번 블록이 놓여있는지 확인 -> 151번 블록을 디스크에서 읽어옴(10밀리초) -> 151번 블록을 캐시에 보관하고, 그 다음 블록 확인
- 버퍼 캐시에 501번 블록이 놓여 있는지 확인 -> 501번 블록을 디스크에서 읽어옴(10밀리초) -> 501번 블록을 캐시에 놓은 후 작업을 처리함. 처리하는데 걸리는 시간은 I/O에 들어간 시간과 다르게 순식간에 끝남.
반면에, 간단한 SQL을 처리하는데 CPU 시간은 1.2밀리초(가정한 값) 정도 사용한다. 즉, 캐시에 데이터가 없을 때 걸리는 처리 시간은 41.2밀리초이고, 캐시에 데이터가 있을 때 걸리는 처리 시간은 1.2밀리초이다.
4. 공유메모리
프로세스마다 캐시를 가지면 낭비가 많아지고, 다른 프로세스에서 변경된 데이터를 볼 수 없는 등 여러 문제가 발생한다. 그래서 어떠한 오라클 프로세스라도 볼 수 있는 메모리를 캐시로 활용한다.
OS가 메모리 안의 데이터를 손상되지 않도록 보호하고 있기 때문에, 다른 프로세스의 메모리를 보는 것은 기본적으로 불가능하다. 이러한 특징은 DBMS의 입장에서 불편함이 발생하게 된다. 이런 불편함을 해결해주는 것으로 OS의 '공유 메모리'라는 특수한 메모리 기능이 있다. 자신의 메모리 영역에 기록했던 데이터를 다른 프로세스에서도 즉시 볼 수 있도록 한다.
공유 메모리의 중요 포인트는 '실제 메모리는 한 개다'라는 개념이다. 각 프로세스에게는 마치 자신의 메모리인 것처럼 보이지만, 실제로는 모든 프로세스가 같은 메모리 영역에 접근하고 있는 것이다.
오라클에서는 이런 공유 메모리를 'SGA(System Global Area)', 공유하지 않는 메모리의 일부를 'PGA(Program Global Area)'라고 부른다.
DBMS에게 공유 메모리는 매우 편리한 도구이며, 다수의 프로세스로 구성된 DBMS에게 없어서는 안 될 필수 기능이다. 하지만 공유 메모리는 누구나 접근할 수 있으므로 Lock을 걸어서 베타적 제어(exclusive control)를 하지 않으면 데이터에 손상을 입힐 수 있다. 실제로 내부를 살펴보면 Lock을 통해 데이터를 보호하고 있다. 이 말은 DBMS가 Lock이 복잡하게 얽힌 내부 구조를 가지며, 내부에서 사용하는 Lock으로 인해 성능 문제가 발생하기 쉽다는 것을 의미한다.
- 공유메모리 설정
오라클의 설정 파일은 spfileXXXX.ora(XXXX에는 데이터베이스를 식별하는 ID가 들어간다)이다.
이 파일에는 버퍼 캐시의 크기를 결정하는 'DB_CACHE_SIZE'라느 파라미터가 존재하는데, 버퍼 캐시는 성능과 직결되기 때문에 크기를 정할 때 신중하게 결정해야 한다. 또한 공유 풀이나 로그 버퍼같은 설정 영역도 존재한다.
공유메모리 설정 예(리눅스)
# /sbin/sysctl -a | grep shm
kernel.shmall = 2097152
kernel.shmmax = 4294967295
kernel.shmni = 4096* 'shm'으로 시작하는 부분이 공유 메모리의 설정
* 버퍼 캐시의 크기 설정 : 데이터베이스를 새로 설치할 때와 같이 적절한 크기를 알 수 없는 경우에는 임시로 처음 크기를 크게 잡아놓고 업무를 실제로 돌려보면서 통계를 기반으로 조정하는 방법과 버퍼 캐시를 아예 설정하지 않고, 부하의 정도에 따라 오라클이 자동으로 조절하도록 설정하는 방법(자동 공유 메모리 관리, Automatic Shared Memory Management)이 있다.
5. 버퍼 캐시를 정리하는 LRU 알고리즘
버퍼 캐시는 자주 사용하는 데이터를 더 빠르게 가져오기 위해 존재한다. 버퍼 캐시의 크기는 한정되어 있으므로, 어떤 식으로든 관리하고 정리해야 한다. 오라클은 버퍼 캐시 관리에 LRU 알고리즘을 사용한다.
LRU(Least Recently Used)알고리즘은 최근에 사용하지 않은 데이터부터 캐시 아웃하는 알고리즘이다.
- 버퍼 캐시에 놓여 있는지 확인
- 데이터를 디스크에서 읽어옴
- 읽어온 데이터를 캐시에 놓음. 그때 LRU 리스트를 기반으로 어떤 블록을 캐시에서 버릴지 결정
데이터를 읽어오는 것 외에도 오라클의 서버 프로세스는 변경한 데이터(블록)도 캐시에 둔다. 서버 프로세스는 디스크에서 데이터를 읽어오지만 디스크에 기록하지 않는데, 이 때 변경된 데이터를 디스크에 기록하는 일은 백그라운드 프로세스인 DBWR이 수행한다. 단, 변경한 데이터는 디스크에 기록하기 전에 캐시에서 버려지면 데이터가 손실되기 때문에, 캐시에서 버려지기 전에 디스크에 기록해야 한다. 그래서 DBWR은 정기적으로 변경한 데이터를 디스크에 부하를 주지 않도록 디스크에 저장한다.
- 버퍼 캐시에 변경할 데이터가 놓여있는지 확인
- 데이터를 디스크에서 읽어옴
- 읽어온 데이터를 캐시에 놓고, 그 후에 데이터를 변경. 일반적으로 서버 프로세스는 변경한 데이터를 디스크에 저장하지 않음. SQL 처리와 관계없이 DBWR이 정기적으로 변경된 데이터 블록을 찾아 디스크에 저장
또한, 자주 사용하지 않는 데이터를 버퍼 캐시에 둘 필요가 없다. 예를 들어, 데이터가 많은 테이블을 풀 스캔한 데이터는 적재해두더라도 사용하는 일이 별로 없으며, 풀 스캔한 데이터를 적재함으로써 자주 사용하는 데이터를 캐시에서 없애는 일이 발생할 수 있다. 따라서 오라클은 큰 테이블이라고 판단하면 버퍼 캐시로 블록을 적재하지 않는다.
6. 스토리지 캐시
스토리지에서 데이터를 읽어오는 것은 당연히 빨라지지만, 캐시 덕분에 기록하는 것도 빨라진다. 그 이유는 원래 디스크에 기록해야 하는 데이터를 캐시에 기록하는 것만으로도 OS 관점에서의 I/O를 종료할 수 있기 때문이다.
7. OS의 버퍼 캐시와 가상 메모리
- 버퍼 캐시
메인 메모리의 일부를 OS의 버퍼 캐시로서 할당하고, 자주 사용하는 파일의 데이터를 둔다. 처음 데이터를 읽어올 때는 버퍼 캐시에 데이터가 존재하지 않기 때문에 디스크에서 읽어오고, 그 때 버퍼 캐시에 데이터를 적재한다. 그래서 두번째 읽어올 때는 버퍼 캐시에 존재하는 데이터를 읽어오게 된다. 따라서 첫번째 데이터 읽기 속도와 두번째 데이터 읽기 속도에 차이가 발생하게 된다.
- 가상 메모리
OS에서는 가상 메모리라는 기능을 이용해 실제로 가지고 있는 물리 메모리보다 더 많은 메모리를 사용할 수 있다. 이 기능은 메모리에서 자주 사용되지 않는 데이터를 디스크에 저장한다. 프로세스 관점에서는 데이터는 메모리에 있는 것처럼 보이지만, 실제는 디스크에 저장된 구성이다.
- 최근에 데이터를 사용하지 않아 디스크로 옮김(페이지 아웃, page out)
- 프로세스가 메모리에 접근(데이터 요청)
- 프로세스가 요청해서 디스크에서 데이터를 읽어 옴(페이지 인, page in)
1번과 3번 동작을 페이징(paging)이라고 말하며, 물이 메모리와 디스크 사이에서 블록(페이지)를 주고받는 것을 의미한다. 물리 메모리의 페이지를 디스크에 기록하는 것을 페이지 아웃, 디스크의 페이지를 물리 메모리로 읽어오는 것을 페이지 인이라고 한다.
가상 메모리는 버퍼 캐시와 반대로 동작한다. 버퍼 캐시는 디스크에 빠르게 접근하기 위해 메모리의 일정 부분을 할당하여 사용하고, 가상 메모리는 속도가 느려지더라도 사용할 수 있는 메모리의 크기를 늘리기 위해서 디스크를 사용한다.
이 세 가지 기술은 밀접한 관계가 있다. 예를 들어, 오라클의 버퍼 캐시를 물리 메모리보다 많게 설정한다고 가정했을 때, 물리 메모리가 충분하지 않으면 메모리에 있는 것처럼 보여도 실제로는 데이터가 스왑에 존재할 수 있다. 오라클의 버퍼 캐시를 무리해서 크게 잡아 버퍼 캐시가 스왑에 들어갈 경우가 발생하면 버퍼 캐시에서 데이터를 읽어오는 의미가 사라지게 된다. 따라서 물리 메모리를 충분하게 할당한 후, 오라클이 물리 메모리만 사용할 수 있도록 파라미터를 설정하는 것이 좋다. 즉, 가상 메모리를 사용하기 위한 디스크는 보험으로 준비해두고 실제로는 사용하지 않는 것이 좋다.
이전장 : 오라클의 여러 프로세스
다음장 : SQL문 분석과 공유 풀
출처 : 스키타 아츠시 외 4명, 『그림으로 공부하는 오라클 구조』, 이민재, 제이펍(2022), p38-p60
'STUDY > DB' 카테고리의 다른 글
그림으로 공부하는 오라클 구조 : 오라클의 기동과 정지 (0) 2022.05.15 그림으로 공부하는 오라클 구조 : SQL문 분석과 공유 풀 (0) 2022.05.09 그림으로 공부하는 오라클 구조 : 오라클의 여러 프로세스 (0) 2022.04.25 그림으로 공부하는 오라클 구조 : I/O와 디스크의 관계 (0) 2022.04.24 프로젝트 성패를 결정짓는 데이터 모델링 이야기2 (0) 2021.07.08