1P by neo 2달전 | favorite | 댓글 1개
  • 지난 1년 동안 SQLite를 사용하여 Rails 애플리케이션을 성능 좋고 안정적으로 실행하는 방법을 깊이 이해하려고 노력해왔음
  • 이 과정에서 여러 가지 교훈을 배웠으며, 이를 공유하고자 함
  • 문제의 원인과 해결 방법을 설명할 것임

SQLite와 Rails의 문제점

  • 기본적으로 SQLite를 사용한 Rails 애플리케이션은 바로 사용할 수 있는 상태가 아님
  • 약간의 조정과 미세 조정을 통해 성능 좋고 안정적인 애플리케이션을 만들 수 있음
  • Rails 8에서는 기본 설정만으로도 프로덕션 준비가 완료된 상태가 되도록 목표를 설정함

데모 애플리케이션 "Lorem News"

  • "Lorem News"라는 데모 애플리케이션을 사용하여 문제와 해결책을 설명할 것임
  • 이 애플리케이션은 Hacker News의 클론으로, 사용자들이 게시물과 댓글을 작성할 수 있음

성능 테스트

  • oha 로드 테스트 CLI와 애플리케이션 내 벤치마킹 경로를 사용하여 성능을 테스트함
  • 단일 요청과 동시 요청을 통해 성능을 측정함

주요 문제: SQLITE_BUSY 예외

  • SQLite는 한 번에 하나의 쓰기 작업만 허용하기 위해 쓰기 잠금을 사용함
  • 여러 연결이 동시에 쓰기 잠금을 시도하면 SQLITE_BUSY 예외가 발생함
  • 이 문제를 해결하기 위해 즉시 트랜잭션을 사용해야 함

즉시 트랜잭션

  • 기본적으로 SQLite는 지연된 트랜잭션 모드를 사용함
  • 즉시 트랜잭션을 사용하면 쓰기 잠금을 즉시 시도하고 실패 시 재시도할 수 있음
  • sqlite3-ruby gem을 사용하여 기본 트랜잭션 모드를 즉시 모드로 설정할 수 있음

타임아웃 설정

  • database.yml 파일에서 타임아웃 설정을 통해 SQLITE_BUSY 예외를 줄일 수 있음
  • SQLite의 busy_timeout 설정을 사용하여 쓰기 잠금을 재시도할 수 있음

GVL(글로벌 VM 잠금) 문제

  • sqlite3-ruby gem은 SQLite의 C 코드를 호출할 때 GVL을 해제하지 않음
  • 이는 동시성 성능을 저하시킴
  • busy_handler를 사용하여 GVL을 해제하고 성능을 개선할 수 있음

busy_timeout 재구현

  • busy_timeout을 재구현하여 모든 쿼리가 동일한 빈도로 재시도하도록 설정함
  • 이는 오래된 쿼리가 타임아웃되지 않도록 함

성능 개선

  • 성능을 개선하기 위해 다음과 같은 설정을 적용해야 함
    • 즉시 트랜잭션 사용
    • 타임아웃 설정
    • busy_handler 사용
    • WAL(Write-Ahead Logging) 모드 사용
    • 읽기/쓰기 연결 풀 분리

GN⁺의 정리

  • SQLite를 사용한 Rails 애플리케이션의 성능 문제와 해결책을 다룸
  • 즉시 트랜잭션, 타임아웃 설정, GVL 해제, WAL 모드 사용, 읽기/쓰기 연결 풀 분리 등의 방법을 통해 성능을 개선할 수 있음
  • 이 기사는 SQLite와 Rails를 사용하는 개발자들에게 매우 유용할 것임
  • 유사한 기능을 가진 다른 프로젝트로는 PostgreSQL과 MySQL을 추천함
Hacker News 의견
  • Oldmoe의 Litestack 프로젝트 소개

    • SQLIte와 Rails를 사용하는 사람들은 Oldmoe의 Litestack 프로젝트를 확인할 필요가 있음
    • Litestack은 SQLite의 강력함을 활용하여 웹 애플리케이션 데이터 인프라를 제공하는 Ruby gem임
    • SQL 데이터베이스, 빠른 캐시, 강력한 작업 큐, 신뢰할 수 있는 메시지 브로커, 전체 텍스트 검색 엔진, 메트릭스 플랫폼을 하나의 패키지로 제공함
    • 현재 프로젝트에서 사용 중이며 매우 만족스러움
  • 상세한 기사 작성에 대한 감사

    • SQLite 웹 애플리케이션을 확장하려는 사람들에게 유용한 정보임
    • Rails를 넘어 다른 프레임워크에서도 적용 가능함
    • 작가에게 감사함
  • SQLite 관련 작업을 하는 사람들에게 추천

    • 사용하는 언어나 프레임워크와 상관없이 SQLite 관련 작업을 하는 사람들은 이 기사를 읽어야 함
    • 몇 년 전에는 직접 해결해야 했던 문제들을 다루고 있음
    • 작가에게 감사함
  • FOSS 분석 시스템에 대한 질문

    • 설치가 쉬운 FOSS 분석 시스템을 만들고 있음
    • 이벤트 데이터를 별도의 SQLite 데이터베이스에 보내어 메인 앱의 데이터와 분리하려고 함
    • 초당 1000개 이상의 이벤트를 처리할 수 있는 확장성에 대한 우려가 있음
    • 서버 메모리에 이벤트를 저장하고 매초 한 번씩 일괄적으로 쓰는 방법을 고려 중임
    • SQLite의 많은 DB 쓰기 문제를 해결할 수 있는 합리적인 방법인지 의견을 구함
  • sqlite3-ruby gem의 GVL 문제

    • sqlite3-ruby gem은 SQLite 호출 시 GVL을 해제하지 않음
    • 이는 대부분 합리적인 결정으로 보임
    • Python 확장에서는 다른 방식으로 설계되었을 가능성이 있음
    • extralite gem은 블로킹 중 GVL을 해제하며, 일반적으로 더 빠르고 동시성 문제도 없음
  • 개인 웹서비스 설정

    • 개인 웹서비스에서 사용하는 몇 가지 설정:
      • PRAGMA journal_mode = WAL
      • PRAGMA busy_timeout = 5000
      • PRAGMA synchronous = NORMAL
      • PRAGMA cache_size = 1000000000
      • PRAGMA foreign_keys = true
      • PRAGMA temp_store = memory
      • BEGIN IMMEDIATE 트랜잭션 사용
  • Django에 대한 질문

    • 이 기사는 훌륭함
    • Django에 대한 유사한 솔루션이 있는지 궁금함
    • ArchiveBox는 Django를 통해 SQLite를 사용하며, Rails에서 언급된 문제를 자주 겪음
    • 앱의 다른 채널을 통해 모든 쓰기를 직렬화하지 않는 SQLite 레이어 솔루션이 있으면 좋겠음
  • busy_timeout 기본 설정에 대한 의문

    • 매우 유익하고 잘 작성된 기사임
    • 기본 busy_timeout 메서드가 오래된 쿼리를 벌주는 지연을 가지는 이유가 궁금함
    • 왜 이것이 기본 설정으로 의미가 있는지 궁금함
  • SQLite와 Rails 사용에 대한 의견

    • SQLite와 Rails를 좋아하지만, 이는 MS Access를 프로덕션 환경에서 사용하는 것과 유사함
  • Rails 통합 문제 해결에 대한 감사

    • 통합 문제를 해결하고 다른 사람들을 도와주는 것을 항상 기쁘게 생각함
    • 이러한 수정 사항이 기본 Rails 설정에 포함되기를 바람
    • Rails 앱을 운영하며, 몇 년 전 Postgres로 전환하여 매우 만족하고 있음
    • 여전히 대안이 있는 것은 좋으며, 다른 작업에 SQLite를 사용함