운영체제 캐시
앞 포스팅에서 말한 것처럼 MongoDB 의 MMAPv1 스토리지는 운영체제 캐시를 사용한다.
따라서 OS 캐시정책이 매우 중요하다.
페이지 캐시의 데이터 읽기
읽는 방법
MMAPv1 스토리지 엔진은 리눅스의 캐시를 거쳐서 데이터를 MongoDB 서버로 읽어들인다.
이를 순서로 표현하면 아래와 같다.
1) MongoDB 서버가 필요로 하는 데이터는 먼저 리눅스의 페이지 캐시에 적재
2) 그 데이터를 MongoDB 서버가 가져감
3) 그 데이터를 다시 읽어야 하는 경우 디스크에서 읽지 않고 리눅스 페이지 캐시에 적재된 페이지를 조회함
문제점
문제는 리눅스의 디스크 읽기는 주변의 일부 페이지들을 같이 읽어 들이는 경우가 많다.
이를 Read-Ahead 라고 하는데, 데이터 파일에서 필요로 하는 페이지 주변의 페이지들 일부가 캐시에 이미 적재되어 있으면 리눅스의 Read-Ahead 알고리즘이 작동하게 된다.
이 알고리즘이 작동하게 되면 MongoDB 서버가 필요로 하지 않더라도 주변의 데이터를 읽어 캐시페이지로 올린다.
리눅스의 Read-Ahead 알고리즘은 일반적으로 대량의 데이터를 읽을 때 성능을 획기적으로 높여주지만, DB 와 같이 랜덤 읽기 위주(연속된 페이지를 읽지 않는)의 처리를 필요로 한다.
따라서 꼭 필요한 페이지만 메모리로 적재하면 된다.
일반적으로 리눅스의 Read-Ahead 설정은 128~256 으로 설정되어 있다.
이는 512 바이트 섹터를 한 번에 몇 개까지 디스크에서 읽을 것인지 결정하는 옵션인데, 이 알고리즘이 작동하면 64 ~ 128KB 의 데이터를 읽게된다.
MMAPv1 스토리지 엔진은 4KB 나 8KB 의 데이터를 읽으면 되는데 무의미하게 많이 읽게 된다.
무의미하게 많이 읽는다는건 DB의 메모리 사용 효율이 떨어진다는 뜻이다.
무분별하게 많은 데이터를 페이지캐시에 올림으로써 필요한 데이터들이 메모리에서 사라지게 되기 때문이다.
따라서 Read-Ahead 값을 MMAPv1 에서 읽는 크기에 맞춰 (데이터 4KB, 인덱스 8KB) 16이나 32로 조정해준다.
페이지 케시의 데이터 쓰기
쓰는 방법
사용자가 데이터를 변경하게 되면 MMAPv1 스토리지 엔진은 리눅스의 페이지 캐시 내용을 변경한다.
이렇게 메모리상의 데이터는 변경되었지만 디스크로 동기화되지 않은 페이지는 더티 페이지라고 부른다.
디스크로 동기화되기 전 컴퓨터가 비정상적으로 종료되면 메모리의 데이터는 유실된다.
이 유실을 방지하기 위해 MongoDB 는 WAL(Write Ahead Log) 로그를 별도로 기록하여 손실된 데이터를 복구할 수 있도록 준비한다.
MMAPv1 엔진은 변경된 내용을 즉시 디스크로 기록하지 않는데 이유는 조금 더 많은 쓰기작업을 모아 한번에 기록함으로써 서버 시스템의 자원을 효율적으로 사용할 수 있게 해주기 때문이다.
또한 리눅스의 페이지 캐시를 거쳐서 쓰기를 실행하면 비동기 모드로 디스크 쓰기를 실행할 수 있어 사용자 쿼리가 데이터 변경이 디스크에 동기화될 때까지 기다리지 않아도 된다.
문제점
더티 페이지를 디스크에 기록하여 동기화 해야 하는데,
이러한 페이지가 너무 자주 디스크에 동기화되면 불필요하게 시스템 자원을 많이 사용하게 된다.
반대로 너무 동기화를 안하면 더티 페이지가 많아져 그로인해 의도치 않은 데이터 손실 혹은 복구시간을 길어지게 만들 수 있다.
이는 커널 파라미터에서 dirty 와 관련된 커널 파라미터를 조정하여 설정할 수 있다.
데이터 프레그멘테이션
MMAPv1 스토리지 엔진은 도큐먼트가 저장되는 순서대로 데이터 파일에 기록한다.
만약 중간에 도큐먼트가 삭제되면 빈 공간을 기록해두고 새로운 도큐먼트가 저장되면 이 공간을 이용한다.
그러나 새로운 도큐먼트가 삭제된 도큐먼트보다 작은 경우에만 해당되고 그렇지 않다면 신규 저장공간을 계속 사용한다.
이렇게 되면 데이터 파일에서 사용하지 못하는 공간이 늘어나게 되고 실제 데이터 크기보다 파일 크기가 훨씬 커지게 된다.
이를 프레그맨테이션이라고 한다.
프레그멘테이션이 좋지않은 이유는 똑같은 페이지를 읽더라도 가져올 수 있는 데이터의 수가 줄어든다는 의미이므로
더 많은 데이터페이지를 캐시로 올려야 하기 때문이다.
이 프레그멘테이션을 알기위해서는 stats 로 확인해 보면 된다.
프레그멘테이션 확인
mongo> db.mozi.stats()
{
"ns" :"mozi.tistory",
"count" : 11,
"size" : 531144,
"avgObjSize: 48200,
"storageSize" : 989711,
"paddingFactor" : 1
}
필드 | 값 |
count | 컬렉션이 가진 도큐먼트 수 |
size | 컬렉션의 전체 도큐먼트 크기, 도큐먼트에 패딩된 바이트까지 합한 값, 인덱스 크기는 포함하지 않음 |
storageSize | 컬렉션을 위해 할당된 전체 디스크 데이터 파일의 크기 |
avgObjSize | 도큐먼트 하나의 평균 크기 |
paddingFactor | MMAPv1 스토리지 엔진을 사용하는 컬렉션만 패딩이 사용되는데 도큐먼트를 저장할 때 처음부터 일정 크기의 여유공간을 만들어서 저장하는데 이 때 얼마나 덧붙일지 결정하는 값 |
주의점
컬렉션을 컴팩션하는 동안에는 대상 컬렉션이 저장된 데이터베이스의 모든 운영이 잠긴다.
따라서 반드시 점검 시간을 이용하여 작업하도록 한다.
컴팩션을 한다고 해서 디스크 파일의 크기 자체가 줄어드는 건 아니다.
다만 빈 공간이 없도록 데이터를 파일 앞으로 뭉치기 때문에 다음에 들어오는 데이터는 순차적으로 데이터 파일에 적재된다.
'Database > MongoDB' 카테고리의 다른 글
[MongoDB] WiredTiger 의 내부 작동방식 (0) | 2024.05.13 |
---|---|
[MongoDB] WiredTiger 스토리지 엔진, 데이터파일 구조 (0) | 2024.05.13 |
[MongoDB] MMAPv1 스토리지 엔진 (0) | 2024.05.13 |
[MongoDB] MongoDB 아키텍처 MMAPv1, WiredTiger (0) | 2024.05.10 |
[MongoDB] MongoDB 의 CRUD 사용 ( SELECT, INSERT, UPDATE, DELETE ) (0) | 2024.04.18 |