kimyu0218
  • [mysql] InnoDB 버퍼 풀
    2024년 02월 04일 14시 55분 09초에 업로드 된 글입니다.
    작성자: @kimyu0218

    프로젝트 진행 중에 DB 과부하를 완화하기 위해 자주 액세스되는 데이터를 Redis에 캐싱한 적이 있다. 하지만 `htop`* 명령을 사용하여 DB가 설치된 서버를 모니터링한 결과, 캐싱을 적용하기 전과 후의 CPU 사용량 차이가 거의 나타나지 않았다. 어째서 아무런 효과가 없었던 걸까?

    *htop : 리눅스 환경에서 사용되는 프로세스 모니터링 도구

     

    버퍼 풀

    버퍼 풀은 InnoDB에서 사용되는 캐싱 영역으로, 자주 사용되는 데이터를 메모리에 캐시하여 처리 속도를 향상시킨다. 이는 페이지로 나누어져 다수의 레코드를 저장할 수 있으며 연결 리스트 형태의 페이지들로 구현되어 있다.

    버퍼 풀은 LRU 알고리즘을 사용하여 드물게 사용되는 데이터를 캐시에서 제거한다. 자주 사용되는 데이터를 메모리에 유지하여 mysql을 튜닝하는 것이 중요하다.
     
    버퍼 풀은 변형된 LRU 알고리즘으로 관리된다. 새로운 페이지를 저장하는 공간이 필요한 경우, 연결 리스트에서 가장 오래 전에 사용된 페이지를 제거하고 새로운 페이지를 중간에 삽입한다.

    데이터베이스가 작동하는 동안 버퍼 풀의 사용되지 않는 페이지들은 에이징되어 리스트의 뒤로 이동한다. 결국 사용되지 않는 페이지는 old sublist의 꼬리에 도달하고 삭제 대상이 된다.
     
    `WHERE`절이 없는 `SELECT`문처럼 많은 양의 데이터를 버퍼 풀로 가져오면 어떻게 될까? 새로 가져온 데이터가 다시 사용되지 않더라도 자주 사용되는 페이지를 밀어낸다. 이러한 현상을 방지하는 방법은 없을까?
     
    InnoDB는 다시 사용되지 않는 데이터가 버퍼 풀의 공간을 차지하는 것을 최소화한다. read-ahead*나 풀스캔으로 새로운 블록을 버퍼 풀에 가져오더라도 자주 사용되는 페이지들이 버퍼 풀에 남도록 한다.

     *read-ahead : 필요할 것으로 예상되는 페이지 그룹을 미리 가져온다.

    앞서 말했듯이 InnoDB는 기존 LRU와 달리 새롭게 로드한 데이터를 연결 리스트의 중간에 삽입한다. 이로써 많은 양의 데이터를 읽어오더라도 자주 사용되는 데이터들을 목록의 앞 부분에 유지할 수 있다. 또한, 데이터가 실제로 사용되지 않는 경우 빠르게 에이징 아웃하여 메모리를 효율적으로 확보한다.
     
    mysql은 버퍼 풀 설정을 수정하여 최적화하는 데 도움을 준다. `my.cnf`나 `my.ini`에서 해당 정보를 확인할 수 있고 `SET GLOBAL`로 값을 변경할 수도 있다.

    • `innodb_old_blocks_pct` : 연결 리스트의 old 블록 백분율 제어 (기본값 37)
    • `innodb_old_blocks_time` : 리스트의 앞쪽으로 이동하지 않고 접근할 수 있는 시간 지정 (기본값 1000)
    이외에도 버퍼 풀 사이즈를 수정하고, 다수의 버퍼 풀을 사용하는 등 다양한 최적화 방법들이 존재한다.

    참고자료

     

    댓글