손상된 SQLite 데이터베이스에서 데이터 복구하기
손상된 SQLite 데이터베이스에서 데이터 복구하기
이 문서는 손상된(corrupt) SQLite 데이터베이스 파일에서 가능한 한 많은 데이터를 복구하는 방법을 설명해요. SQLite에 내장된 복구 기능과, 명령줄 인터페이스의 .recover 명령, 그리고 응용 프로그램에 복구 API를 내장하는 방법을 다뤄요.
출처: 문서
본문
1. 손상된 SQLite 데이터베이스에서 (일부) 데이터 복구하기
SQLite 데이터베이스는 놀랄 만큼 견고해요. 응용 프로그램 결함과 정전은 보통 데이터베이스의 내용을 그대로 두어요. 하지만 SQLite 데이터베이스를 손상시키는 것은 가능해요. 예를 들어 하드웨어 오작동이 데이터베이스 파일을 손상시키거나, 잘못된 프로세스가 데이터베이스를 열어 일부를 덮어쓸 수 있어요.
손상된 데이터베이스 파일이 주어지면, 때로는 그 파일에서 가능한 한 많은 데이터를 구해내는 것이 바람직해요. 복구 API는 파일에서 읽을 수 있는 것 중 최대한 많은 데이터를 구해내도록 설계됐어요. 하지만 모든 데이터를 복구할 수 있다고 보장하지는 않아요. 복구 프로세스는 원본 데이터베이스에서 가능한 한 많은 테이블과 인덱스와 행을 재구성해서 새 데이터베이스에 쓰는 방식으로 동작해요.
1.1. 제한 사항 (Limitations)
복구가 항상 모든 데이터를 되찾을 수 있는 것은 아니에요. 손상이 심하면 일부 인덱스나 행을 복구할 수 없을 수 있어요. 복구는 최선의 노력을 다해 가능한 한 많은 것을 구해내는 것이 목적이며, 손상되지 않은(완전한) 데이터베이스에 대해서는 백업과 같은 다른 전략이 더 적절해요.
2. CLI에서 ".recover" 명령을 사용한 복구
SQLite 3.29.0부터 명령줄 인터페이스(CLI)에 .recover 점(dot) 명령이 추가됐어요. 이 명령은 손상된 데이터베이스에서 가능한 한 많은 내용을 복구해요.
기본 형식은 다음과 같아요.
sqlite3> .recover corrupted.db
또는 복구된 데이터를 새 데이터베이스로 직접 보낼 수도 있어요.
$ sqlite3 new.db ".recover corrupt.db"
.recover 명령은 몇 가지 옵션을 지원해요.
--ignore-freelist— freelist 페이지(미사용 페이지)의 처리를 건너뛰어 복구를 더 빠르고 안전하게 만들 수 있어요.--lost-and-found— 원본 테이블에 속한 것으로 식별할 수 없는 행을lost_and_found테이블에 넣어요.-no-rowids— ROWID를 복구하지 않고 복구된 테이블을WITHOUT ROWID테이블로 만들어요(중복 ROWID 문제 방지).-no-schema— 원본 스키마를 복구하는 대신, 복구된 각 테이블에 대해 열 개수만 맞는 새 스키마를 생성해요.
예를 들어:
$ sqlite3 new.db ".recover --ignore-freelist corrupt.db"
$ sqlite3 new.db ".recover --lost-and-found corrupt.db"
.recover 명령의 자세한 옵션과 동작은 CLI 문서의 .recover 절을 참고하세요.
3. 응용 프로그램에 복구 API 내장하기
CLI의 .recover 명령 외에도, SQLite는 응용 프로그램에 직접 복구 기능을 구현할 수 있는 C API를 제공해요.
3.1. 소스 코드 파일 (Source Code Files)
복구 기능의 소스 코드는 두 개의 C 파일로 구성돼요.
ext/recover/dbdata.c— "dbdata" 가상 테이블 구현. 이 모듈은 손상된 데이터베이스 파일의 페이지를 직접 읽어 테이블/인덱스 내용을 노출해요.ext/recover/sqlite3recover.c— 복구 API(sqlite3recover_*함수)를 구현해요.
이 파일들은 SQLite 소스 트리의 ext/recover/ 디렉터리에 있어요. CLI에 복구 지원을 넣으려면 -DSQLITE_ENABLE_RECOVER 같은 컴파일 옵션으로 활성화해야 해요.
3.2. 복구 구현 방법 (How To Implement Recovery)
응용 프로그램에서 복구를 구현하는 일반적인 절차는 다음과 같아요.
첫째, sqlite3recover 객체를 만들어요. 복구할 손상된 원본 데이터베이스, 그리고 복구된 내용을 쓸 새 대상 데이터베이스를 지정해요.
#include "sqlite3recover.h"
/* 원본(손상) 및 대상(새) 데이터베이스에서 복구 객체 생성 */
sqlite3recover *pRec = sqlite3recover_init(
src_db, /* 손상된 데이터베이스 연결 */
dst_db, /* 복구된 데이터를 쓸 새 연결 */
errmsg /* 오류 메시지를 받을 버퍼 */
);
그 다음 sqlite3recover_run()으로 복구를 실행하고, sqlite3recover_finish()로 마무리해요.
int rc = sqlite3recover_run(pRec);
if( rc!=SQLITE_OK ){
/* 복구 중 오류 - sqlite3recover_errcode()/errmsg()로 확인 */
}
rc = sqlite3recover_finish(pRec);
sqlite3recover_free(pRec);
복구가 완료되면 대상 데이터베이스에서 PRAGMA integrity_check를 실행해 복구된 데이터의 무결성을 확인할 수 있어요.
3.3. 구현 예시 (Example Implementations)
복구 API를 응용 프로그램에 내장하는 실제 예시는 SQLite 소스 트리에서 찾을 수 있어요.
- CLI의
.recover명령 구현이 가장 대표적인 예시예요.shell.c에서 점 명령이 복구 API를 어떻게 호출하는지 볼 수 있어요. - 복구 기능을 활용하는 테스트와 예제 코드가 소스 트리에 포함돼 있어요.
복구 결과 확인 및 주의사항
복구된 데이터는 새 데이터베이스에 저장되므로, 복구 후에는 항상 원본을 건드리지 말고 별도의 새 파일로 보관된 결과를 검증하세요. 어떤 손상 유형은 복구할 수 없으며, 복구가 완벽한 데이터 손실 방지 수단은 아니에요. 데이터 손실 위험을 최소화하려면 정기적인 백업이 가장 좋은 방법이에요.