티스토리 뷰

반응형

대부분의 데이터베이스는 상대적으로 속도가 느린 디스크와 빠른 Ram으로 구성된 메모리 계층 구조로 돼 있다. 따라서 영구 저장소 접근 횟수를 줄이기 위해 페이지를 메모리에 캐시하고 스토리지 계층에서 캐시된 페이지를 재요청하면 캐시에서 반환한다.

 

만약에 디스크에 있는 해당 페이지에 있는 값이 바뀌지 않았다면 캐시 페이지를 재사용할 수 있다. 이를 가상 디스크라고 부른다.

가상 디스크 읽기 작업은 요청된 페이지가 메모리에 없을 경우에만 물리적 저장소에 접근하게 된다. (페이지 캐시 또는 버퍼 풀)

페이지 캐시는 디스크에서 읽은 페이지를 메모리에 캐시한다.

 

아직 캐시되지 않은 페이지를 디스크에서 메모리로 복사하는 작업을 페이징이라고 한다. 아직 디스크로 플러시되지 않은 변경된 페이지는 더티(dirty) page라고 한다.

 

지속된 요청이 오게 되면 메모리는 다 차게 될 것이고 이렇게 되었을 때 캐시되지 않은 값이 요청이 왔을 때 기존에 있는 값 중에 하나를 만료시키는 작업을 해야 한다.

 

페이지 캐시 주요 기능

  • 페이지 내용을 메모리에 캐시한다.
  • 디스크에 저장된 페이지에 대한 변경 상항을 함께 버퍼링하고 캐시된 페이지에 반영한다.
  • 캐시되지 않은 데이터가 요청된 경우 메모리에 공간이 충분하다면 페이징하고 캐시된 버전을 반환한다.
  • 캐시된 페이지가 요청된 경우 메모리에서 반환한다.
  • 메모리에 새로운 페이지를 추가할 공간이 없을 경우 일부 페이지를 만료시키고 페이지로 플러시한다.

 

커널 페이지 우회 기법
많은 데이터베이스 시스템이 O_DIRECT 플래그를 사용해 파일을 읽는다. 이 플래그를 사용하면 I/O 시스템 호출 시 커널 페이지 캐시를 우회하고 디스크에 바로접근하며 데이터베이스에 특화된 버퍼 관리 기법을 사용할 수 있다.

 

캐싱

데이터베이스는 페이지 캐시를 사용해 메모리를 관리하고 디스크 접근을 제어한다. 페이지 캐시를 애플리케이션에 특화된 커널 페이지 캐시라고 생각할 수 있다. 디스크 접근을 추상화하고 논리적 쓰기와 물리적 쓰기를 분리

페이지 캐시를 하면 알고리즘을 수정하거나 객체를 메모리에 실체화하지않고 트리의 일부를 메모리에 저장할 수 있다.

 

캐시된 데이터가 많을수록 더 많은 읽기 요청을 영구 저장소에 접근하지 않고 처리할 수 있다. 나아가 같은 페이지에 대한 변경 사항을 더 많이 같이 버퍼할 수 있다. 

 

페이지를 제거할 때마다 디스크로 플러시한다면 성능을 저하시킬 수 있다. 따라서 일부 데이터베이스는 별도의 백그라운드 프로세스가 제거될 가능성이 높은 더티 페이지를 주기적으로 디스크로 플러시한다. PostgreSQL의 백그라운드 플러시 라이터(background flush writer)가 이 역할을 한다.

 

지속성은 데이터베이스에서 매우 중요한 속성!!

데이터베이스에 장애가 발생하면 플러시되지 않은 데이터는 손실된다. 데이터 손실을 방지하기 위해 checkpoint process가 플러시 시점을 제어한다. 체크 포인트 프로세스는 선행 기록 로그와 페이지 캐시의 싱크가 맞도록 조정한다. 오직 플러시가 완료된 캐시된 페이지와 관련된 로그만 WAL에서 삭제될 수 있다. 이 과정이 완료될 때까지 더티 페이지는 제거될 수 없다.

 

캐싱에 여러 트레이드-오프가 존재

  • 디스크 접근 횟수를 줄이기 위해 플러시 시점을 늦춘다
  • 페이지를 우선적으로 플러시해 빠르게 캐시를 제거한다.
  • 제거할 페이지를 선택하고 최적의 순서로 플러시한다.
  • 캐시 크기를 메모리 범위 내로 유지
  • 기본 저장소에 저장되지 않은 데이터는 손실되지 않아야 한다.

 

복구

선행 기록 로그(WAL)는 장애 및 트랜잭션 복구를 위해 디스크에 저장하는 추가 전용 보조 자료 구조다. 페이지 캐시는 페이지에 대한 변경 사항을 메모리에 버퍼링한다. 캐시된 내용이 디스크로 플러시될 때까지 관련 작업 이력의 유일한 디스크 복사본은 WAL이다. Postgresql에서도 사용한다.

 

WAL의 역할

  • 디스크에 저장된 페이지에 대한 변경 사항을 페이지 캐시에 버퍼링하는 동시에 데이터베이스 시스템 맥락에서의 지속성을 보장한다.
  • 캐시된 페이지가 디스크와 동기화될 때까지 작업 이력을 디스크에 저장한다. 데이터베이스의 상태를 변경하는 모든 작업을 실제 페이지에 적용하기 전에 먼저 디스크에 로깅한다.
  • 장애 발생 시 로그를 기반으로 마지막 메모리 상태를 재구성한다.
PostgresSQL 대 fsync()
PostgresSQL은 체크포인트를 사용해 인덱스와 데이터 파일이 로그 파일의 특정 레코드까지의 내용과 동기화됐는지 확인한다. 체크포인트 프로세스는 주기적으로 더티 페이지를 디스크로 플러시한다. 더티 페이지를 디스크로 플러시하고 커널 페이지의 더티 플래그를 초기화하는 fsync() 커널 함수를 호출해 더티 페이지와 디스크를 동기화한다. 디스크로 페이지를 플러시할 수 없는 경우 fsync는 에러를 반환한다.

체크포인트 프로세스는 항상 모든 파일을 열어 두지 않기 때문에 일부 에러가 발생한 사실을 모를 수가 있다. 결과와 상관없이 더티 플래그는 초기화되기 때문에 체크포인트 포르세서는 실제로 플러시되지 않은 데이터가 성공적으로 디스크로 플러시됐다고 착각할 수 있다. 이러한 문제로 인해 데이터베이스 손상으로 이어질 수 있다.

 

대부분의 데이터베이스 시스템은 퍼지 체크포인트를 사용한다. 마지막으로 성공한 체크포인트는 begin_checkpoint라는 특별한 로그 레코드로 시작해 더티 페이지에 대한 정보와 트랜잭션 테이블의 내용을 저장한 end_checkpoint라는 로그 레코드로 끝난다. 이 로그 레코드에 명시된 모든 페이지가 플러시될 때까지 해당 체크포인트는 미완료 상태다.

 


 

여기서부터는 해당 내용에서 PostgresSQL하고 관련하여 적어보고자 한다.

 

Postgresql.conf 파일을 우선 찾아서 들어가보자.

linux file location : /etc/postgresql/12/main/postgresql.conf
aws rds location: 파라미터 그룹을 통해 조작할 수 있다.

shared_buffers

여기서 ram에 관련되 버퍼 설정은  shared_buffers 이다.

설명은 다음과 같이 되어 있다.

 

여기서 추천하는 값은 시스템 메모리의 25%정도이다. 클수록 효과적이지만 일정 크기가 넘어갔을 때 오히려 역효과가 나올 수 있으므로 적절한 값을 설정해주는 것이 좋다.

 

 

bgwriter_flush_after

해당 설정은 트랜잭션의 레이턴시를 줄어줄 수는 있으나 간혹 shared_buffers보다 큰 경우에 대해서는 성능을 떨어트릴 수가 있다.

 

여기서 더 봐야할 것은 check point이다.

 

CheckPoints

checkpoint는 힙 및 인덱스 데이터 파일이 해당 checkpoint 전에 기록된 모든 정보로 기록된 모든 정보로 업데이트 되도록 보장하는 시퀀스의 지점이다. checkpoint 시에, 모든 dirty 데이터 페이지는 디스크에 쓰기 작업이 되고, 특수한 checkpoint레코드는 로그로 기록에 남는다.

여기서 주의할 점은 모든 dirty 페이지를 디스크에 쓰는 checkpoint는 상당한 부하를 가져올 수 있다.

그러므로 checkpoint가 시작될 때 I/O가 시작되고, 다음 checkpoint가 시작되기 전에 완료되도록 조절한다. 이렇게 함으로써 checkpoint 시 성능저하를 최소화 한다.

 

checkpoint는 checkpoint_timeout초가 지나면, 또는 트랜잭션 로그 전체 크기가 max_wal_size 값을  초과할 때마다 진행한다.  만약에 이전 checkpoint이후로 wal 로그 기록이 없다면 그냥 건너뛴다. (SQL명령 CHECKPOINT를 사용하여 checkpoint를 강제 적용하는 것도 가능하다.)

 

checkpoint는, 현재 dirty buffer 기록해야하고, 추후 wal traffic이 존재하기 때문에 비용이 매우 비싸다.  그러므로 체크포인트가 자주 일어나지 않도록 checkpointing의 크기를 크게 가져가는 것이 좋다.

 

그럼 폭발적인 쓰기 작업을 어떻게 처리할까?

우선 일정기간에 걸쳐 분산시켜서 처리한다. 해당 기간은 checkpoint_completion_target에 의해 제어되며, 체크 포인트 작업은 지정된 checkpoint_timeout 초 안에, max_wal_size 크기를 초과하지 않는 범위 내에서 처리하도록 조정한다. 

정상실행 중 최대 I/O 처리량을 처리하고자 checkpoint_completion_target의 값을 올려서 checkpoint로부터 줄일 수가 있다. 이것의 단점은 wal 세그먼트 값을 더 확보해야 하므로 checkpoint 복구시간에 영향을 주게 된다.

 

 

fsync

이 매개변수가 on 인 경우 PostgresSQL 서버는 업데이트가 물리적으로 디스크에 기록되었는지를 fsync() 호출 또는 상응하는 다양한 메서드를 사용하여 확인하려고 한다. 이로써 운영체제 또는 하드웨어 충돌 후에 데이터베이스 클러스터를 일정한 상태로 복구할 수 있다.

fsync를 해제하는 것은 잠재적 성능상의 장점이 있지만, 문제가 생겼을 때 복구를 못할 수도 있다. 

 

checkpoint_timeout

자동 WAL checkpoints 간의 최대 시간. 값을 늘리면 충돌시간에 필요한 시간을 늘릴 수 있다. 

 

checkpoint_completion_target

checkpoints간 총 시간 분할로써, checkpoints 완료 목표를 지정한다. 기본 값은 0.5이다.

 

 

참고: 데이터베이스 인터널스, postgresql 공식문서

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함