SQLite: 충분한 경우
VPS의 SQLite가 별도의 데이터베이스 서버보다 나은 경우, WAL을 활성화하고 “database is locked”를 피하는 방법, 그리고 PostgreSQL로 옮겨야 할 시점을 알아봐요.
업데이트 2026년 8월 23일
SQLite는 단일 파일로 동작하는 데이터베이스예요. 서비스도, 포트도, 비밀번호도 필요 없죠. 봇이나 작은 제어판, 쓰기 작업이 드문 사이트에는 딱 맞는 선택이에요. 메모리를 많이 먹지 않는데 VDS-1은 메모리가 2 GB뿐이고, 설정할 것도 없어요.
언제 사용할까요
적합한 경우:
- Telegram 및 Discord 봇, 카운터, 설정 저장;
- 분당 수천 건의 읽기와 수십 건의 쓰기가 발생하는 사이트;
- 두 번째 서버를 둘 계획 없이 한 대의 머신에서 운영하는 모든 프로젝트.
적합하지 않은 경우:
- 여러 프로세스가 동시에 활발하게 쓰기를 수행하는 경우;
- 데이터베이스 크기가 수십 GB를 넘는 경우;
- 다른 머신에서 네트워크로 접근하는 경우 — SQLite는 이 기능을 아예 지원하지 않아요.
두 번째와 세 번째 경우에는 PostgreSQL 또는 MySQL을 사용하세요.
파일 다루기
apt install -y sqlite3
sqlite3 /srv/app/data/app.db
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL);
INSERT INTO users (name) VALUES ('test');
.tables
.schema users
.quit
데이터베이스 파일은 머신의 로컬 디스크에 있어야 하고, 애플리케이션 사용자가 소유해야 해요:
chown appuser:appuser /srv/app/data/app.db
chmod 640 /srv/app/data/app.db
데이터베이스가 있는 디렉터리에도 쓰기 권한이 필요해요. SQLite가 그 옆에 임시 -wal 및 -shm 파일을 만들기 때문이에요.
필수 설정
pragma 설정 두 가지면 대부분의 문제를 없앨 수 있어요:
PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
WAL을 사용하면 쓰기가 진행되는 동안에도 읽기가 가능해요. WAL이 없으면 쓰기 작업이 모든 읽기를 막아버려요. busy_timeout은 클라이언트가 즉시 실패하는 대신 5초 동안 기다리게 해요. 콘솔에서 한 번만 설정하지 말고, 코드에서 연결을 열 때마다 설정하세요.
백업
애플리케이션이 실행 중일 때는 cp로 파일을 복사하면 안 돼요. 데이터베이스가 손상돼요. 안전한 내장 명령이 있어요:
sqlite3 /srv/app/data/app.db ".backup '/srv/backups/app-$(date +%F).db'"
이 명령을 cron에 넣고 복사본을 머신 밖으로 옮기세요. VPS에는 자동 백업도, 스냅샷도 없어요. 일반적인 방법은 데이터베이스 백업에서 다뤄요.
자주 발생하는 오류
| 증상 | 원인 |
|---|---|
database is locked | WAL 또는 busy_timeout이 없거나, 다른 프로세스가 긴 트랜잭션을 붙잡고 있음 |
attempt to write a readonly database | 파일 또는 디렉터리에 권한이 없음 |
disk I/O error | 디스크 공간 부족 — “개요”의 디스크 사용량 링에서 확인하세요 |
| 행을 삭제해도 데이터베이스가 커짐 | 공간이 자동으로 회수되지 않음: VACUUM |
| 복사 후 데이터베이스 손상 | 실행 중에 cp로 복사함 — .backup만 사용하세요 |
언제 이전할까요
신호는 이래요. WAL을 켰는데도 database is locked 오류가 규칙적으로 발생하고, 애플리케이션 워커가 여러 개이며, 보고서 계산에 몇 분씩 걸리는 경우예요. 이전 방법은 데이터베이스 마이그레이션을 참고하세요.
데이터베이스 파일은 웹 서버가 서비스하는 디렉터리에 두면 안 돼요.
/var/www/app.db는 모든 비밀번호와 사용자 데이터와 함께 직접 링크로 다운로드될 수 있어요.