SQLite가 사용하는 임시 파일
SQLite가 사용하는 임시 파일 (Temporary Files Used By SQLite)
SQLite의 특징 중 하나는 데이터베이스가 단일 디스크 파일로 구성된다는 거예요. 하지만 데이터베이스를 처리하는 과정에서 SQLite는 많은 임시 파일을 사용해요. 이 문서는 SQLite가 만들고 사용하는 다양한 임시 파일(롤백 저널, WAL 파일, 공유 메모리 파일, 슈퍼 저널, 문 저널, TEMP 데이터베이스 등)과 그것들이 언제 생성·삭제되는지, 그리고 임시 파일 생성이 비싼 시스템에서 어떻게 피하는지 설명해요.
출처: 문서
본문
1. 도입
SQLite의 독특한 특징 중 하나는 데이터베이스가 단일 디스크 파일로 구성된다는 거예요. 이는 데이터베이스를 이동하거나 백업하는 것이 단일 파일을 복사하는 것만큼 간단하므로 SQLite 사용을 단순화해요. 또한 SQLite가 애플리케이션 파일 형식으로 사용하기에 적합하게 만들어요. 하지만 완전한 데이터베이스가 단일 디스크 파일에 담겨 있음에도, SQLite는 데이터베이스를 처리하는 동안 많은 임시 파일을 사용해요.
이 문서는 SQLite가 만들고 사용하는 다양한 임시 파일을 설명해요. 파일이 언제 생성되는지, 언제 삭제되는지, 무엇에 사용되는지, 왜 중요한지, 그리고 임시 파일 생성이 비싼 시스템에서 어떻게 피하는지 설명해요.
SQLite가 임시 파일을 사용하는 방식은 SQLite가 애플리케이션에 하는 계약의 일부로 간주되지 않아요. 이 문서의 정보는 이 문서가 작성되거나 마지막으로 갱신된 시점에 SQLite가 작동하는 방식을 정확히 설명한 것이에요. 하지만 미래 버전의 SQLite가 같은 방식으로 임시 파일을 사용할 것이라는 보장은 없어요. 새 종류의 임시 파일이 도입될 수도 있고, 현재의 일부 임시 파일 사용이 미래 릴리스에서 중단될 수도 있어요.
2. 아홉 가지 종류의 임시 파일
SQLite는 현재 아홉 가지 서로 다른 유형의 임시 파일을 사용해요:
- 롤백 저널 (Rollback journals)
- 슈퍼 저널 (Super-journals)
- Write-ahead Log (WAL) 파일
- 공유 메모리 파일 (Shared-memory files)
- 문 저널 (Statement journals)
- TEMP 데이터베이스
- 뷰와 서브쿼리의 자료화 (Materializations of views and subqueries)
- 일시적 인덱스 (Transient indices)
- VACUUM이 사용하는 일시적 데이터베이스
이 각각의 임시 파일 유형에 대한 추가 정보는 뒤에서 다룰 거예요.
2.1. 롤백 저널
롤백 저널은 SQLite에서 원자적 커밋과 롤백 기능을 구현하는 데 사용되는 임시 파일이에요. (이것이 어떻게 작동하는지에 대한 자세한 논의는 Atomic Commit In SQLite라는 별도 문서를 참고하세요.) 롤백 저널은 항상 데이터베이스 파일과 같은 디렉터리에 위치하며, "-journal"이라는 8개 문자를 붙인 것 외에는 데이터베이스 파일과 같은 이름을 가져요. 롤백 저널은 보통 트랜잭션이 처음 시작될 때 생성되고, 트랜잭션이 커밋되거나 롤백될 때 삭제돼요. 롤백 저널 파일은 SQLite의 원자적 커밋과 롤백 기능을 구현하는 데 필수적이에요. 롤백 저널이 없으면 SQLite는 불완전한 트랜잭션을 롤백할 수 없고, 트랜잭션 도중 크래시나 정전이 발생하면 롤백 저널 없이는 전체 데이터베이스가 손상될 가능성이 높아요.
롤백 저널은 보통 트랜잭션의 시작과 끝에 각각 생성되고 파괴돼요. 하지만 이 규칙에는 예외가 있어요.
트랜잭션 도중 크래시나 정전이 발생하면 롤백 저널 파일이 디스크에 남아 있어요. 다음에 다른 애플리케이션이 데이터베이스 파일을 열려 하면, 버려진 롤백 저널의 존재를 알아차리고(이 상황에서 우리는 "hot journal"이라고 불러요) 저널의 정보를 사용해 데이터베이스를 불완전한 트랜잭션 시작 전 상태로 복원해요. 이것이 SQLite가 원자적 커밋을 구현하는 방식이에요.
애플리케이션이 pragma를 사용해 SQLite를 배타적 잠금 모드로 설정하면:
PRAGMA locking_mode=EXCLUSIVE;
SQLite는 배타적 잠금 모드 세션의 첫 번째 트랜잭션 시작 시 새 롤백 저널을 만들어요. 하지만 트랜잭션이 끝날 때 롤백 저널을 삭제하지 않아요. 롤백 저널이 잘리거나 헤더가 0으로 채워질 수는 있지만(사용하는 SQLite 버전에 따라), 롤백 저널은 삭제되지 않아요. 롤백 저널은 배타적 접근 모드를 벗어날 때까지 삭제되지 않아요.
롤백 저널 생성과 삭제는 journal_mode pragma에 의해서도 변경돼요. 기본 저널링 모드는 각 트랜잭션 끝에 롤백 저널 파일을 삭제하는 기본 동작인 DELETE예요. PERSIST 저널 모드는 저널 파일의 삭제를 생략하고 대신 롤백 저널 헤더를 0으로 덮어써, 다른 프로세스가 저널을 롤백하는 것을 방지하므로 실제로 디스크에서 파일을 제거하는 비용 없이 저널 파일을 삭제한 것과 같은 효과를 내요. 즉, 저널 모드 PERSIST는 EXCLUSIVE 잠금 모드에서 보이는 것과 같은 동작을 보여줘요. OFF 저널 모드는 SQLite가 롤백 저널을 완전히 생략하게 해요. 다시 말해, 저널 모드가 OFF로 설정되면 롤백 저널은 결코 작성되지 않아요. OFF 저널 모드는 SQLite의 원자적 커밋과 롤백 기능을 비활성화해요. OFF 저널 모드가 설정되면 ROLLBACK 명령을 사용할 수 없어요. 그리고 OFF 저널 모드를 사용하는 트랜잭션 도중 크래시나 정전이 발생하면 복구가 불가능하고 데이터베이스 파일이 손상될 가능성이 커요. MEMORY 저널 모드는 롤백 저널을 디스크가 아닌 메모리에 저장하게 해요. 저널 모드가 MEMORY일 때도 ROLLBACK 명령은 여전히 작동하지만, 복구를 위한 파일이 디스크에 존재하지 않으므로 MEMORY 저널 모드를 사용하는 트랜잭션 도중 크래시나 정전이 발생하면 데이터베이스가 손상될 가능성이 커요.
2.2. Write-Ahead Log (WAL) 파일
write-ahead log(WAL) 파일은 SQLite가 WAL 모드에서 작동할 때 롤백 저널 대신 사용돼요. 롤백 저널과 마찬가지로 WAL 파일의 목적은 원자적 커밋과 롤백을 구현하는 거예요. WAL 파일은 항상 데이터베이스 파일과 같은 디렉터리에 위치하며, "-wal"이라는 4개 문자를 붙인 것 외에는 데이터베이스 파일과 같은 이름을 가져요. WAL 파일은 데이터베이스에 대한 첫 번째 연결이 열릴 때 생성되고, 보통 데이터베이스에 대한 마지막 연결이 닫힐 때 제거돼요. 하지만 마지막 연결이 깨끗하게 종료되지 않으면 WAL 파일이 파일시스템에 남아 있고, 다음에 데이터베이스가 열릴 때 자동으로 정리돼요.
2.3. 공유 메모리 파일
WAL 모드에서 작동할 때, 같은 데이터베이스 파일과 관련된 모든 SQLite 데이터베이스 연결은 WAL 파일의 인덱스로 사용되는 일부 메모리를 공유해야 해요. 대부분의 구현에서 이 공유 메모리는 이 목적을 위해 생성된 파일(공유 메모리 파일)에 mmap()을 호출해 구현돼요. 공유 메모리 파일은 존재한다면 데이터베이스 파일과 같은 디렉터리에 위치하며, "-shm"이라는 4개 문자를 붙인 것 외에는 데이터베이스 파일과 같은 이름을 가져요. 공유 메모리 파일은 WAL 모드에서 실행 중일 때만 존재해요.
공유 메모리 파일은 영구 콘텐츠를 포함하지 않아요. 공유 메모리 파일의 유일한 목적은 WAL 모드에서 같은 데이터베이스에 접근하는 여러 프로세스가 사용할 공유 메모리 블록을 제공하는 거예요. VFS가 공유 메모리에 접근하는 대체 방법을 제공할 수 있다면, 공유 메모리 파일 대신 그 대체 방법이 사용될 수 있어요. 예를 들어 PRAGMA locking_mode가 EXCLUSIVE로 설정되면(즉, 단 하나의 프로세스만 데이터베이스 파일에 접근할 수 있음) 공유 메모리는 공유 메모리 파일이 아니라 힙에서 할당되고, 공유 메모리 파일은 결코 생성되지 않아요.
공유 메모리 파일은 관련된 WAL 파일과 같은 수명을 가져요. 공유 메모리 파일은 WAL 파일이 생성될 때 생성되고 WAL 파일이 삭제될 때 삭제돼요. WAL 파일 복구 중에는 공유 메모리가 복구 중인 WAL 파일의 콘텐츠에 기반해 처음부터 다시 생성돼요.
2.4. 슈퍼 저널 파일
슈퍼 저널 파일은 단일 트랜잭션이 ATTACH 문을 사용해 단일 데이터베이스 연결에 추가된 여러 데이터베이스에 변경을 가할 때 원자적 커밋 과정의 일부로 사용돼요. 슈퍼 저널 파일은 항상 기본 데이터베이스 파일(기본 데이터베이스 파일은 데이터베이스 연결을 만든 원래 sqlite3_open(), sqlite3_open16(), 또는 sqlite3_open_v2() 호출에서 식별된 데이터베이스)과 같은 디렉터리에 임의의 접미사를 붙여 위치해요. 슈퍼 저널 파일은 트랜잭션 동안 변경된 다양한 ATTACH된 보조 데이터베이스의 이름을 포함해요. 슈퍼 저널 파일이 삭제되면 다중 데이터베이스 트랜잭션이 커밋돼요. 자세한 내용은 Atomic Commit In SQLite 문서를 참고하세요.
슈퍼 저널이 없으면 다중 데이터베이스 트랜잭션의 커밋은 각 데이터베이스별로는 원자적이지만, 모든 데이터베이스에 걸쳐서는 원자적이지 않을 거예요. 즉, 커밋이 도중에 크래시나 정전으로 중단되면, 한 데이터베이스에 대한 변경은 완료될 수 있지만 다른 데이터베이스에 대한 변경은 롤백될 수 있어요. 슈퍼 저널은 모든 데이터베이스의 모든 변경이 함께 롤백되거나 함께 커밋되게 해요.
슈퍼 저널 파일은 다음 요구 사항을 모두 충족하는 데이터베이스가 두 개 이상인 다중 데이터베이스 파일이 관련된 COMMIT 작업에 대해서만 생성돼요:
- 데이터베이스가 트랜잭션에 의해 수정됨
- PRAGMA synchronous 설정이 OFF가 아님
- PRAGMA journal_mode가 OFF, MEMORY, WAL이 아님
이는 데이터베이스 파일의 synchronous가 꺼져 있거나 OFF, MEMORY, WAL의 저널 모드를 사용할 때, SQLite 트랜잭션이 정전 시 여러 데이터베이스 파일에 걸쳐 원자적이지 않다는 것을 의미해요. synchronous OFF와 저널 모드 OFF 및 MEMORY의 경우, 트랜잭션 커밋이 정전으로 중단되면 데이터베이스가 보통 손상돼요. WAL 모드의 경우 개별 데이터베이스 파일은 정전 시 원자적으로 갱신되지만, 다중 파일 트랜잭션의 경우 전원이 복원된 후 일부 파일은 롤백되고 다른 파일은 롤포워드될 수 있어요.
2.5. 문 저널 파일
문 저널 파일은 더 큰 트랜잭션 안에서 단일 문의 부분 결과를 롤백하는 데 사용돼요. 예를 들어 UPDATE 문이 데이터베이스의 100개 행을 수정하려 한다고 가정해봐요. 하지만 처음 50개 행을 수정한 후, UPDATE가 전체 문을 차단해야 하는 제약 위반에 부딪힌다면, 문 저널이 처음 50개 행 변경을 되돌려 데이터베이스가 문 시작 시점의 상태로 복원되게 해요.
문 저널은 데이터베이스의 여러 행을 변경할 수 있고 트리거 내에서 제약이나 RAISE 예외에 부딪혀 부분 결과를 되돌려야 할 수 있는 UPDATE 또는 INSERT 문에 대해서만 생성돼요. UPDATE나 INSERT가 BEGIN...COMMIT 안에 포함되어 있지 않고 같은 데이터베이스 연결에 다른 활성 문이 없으면, 일반적인 롤백 저널을 대신 사용할 수 있으므로 문 저널이 생성되지 않아요. 대체 충돌 해결 알고리즘이 사용되는 경우에도 문 저널은 생략돼요. 예를 들어:
UPDATE OR FAIL ...
UPDATE OR IGNORE ...
UPDATE OR REPLACE ...
UPDATE OR ROLLBACK ...
INSERT OR FAIL ...
INSERT OR IGNORE ...
INSERT OR REPLACE ...
INSERT OR ROLLBACK ...
REPLACE INTO ....
문 저널은 임의의 이름을 받고, 반드시 기본 데이터베이스와 같은 디렉터리일 필요는 없으며, 트랜잭션이 끝나면 자동으로 삭제돼요. 문 저널의 크기는 그 문 저널을 생성하게 만든 UPDATE 또는 INSERT 문이 구현한 변경의 크기에 비례해요.
2.6. TEMP 데이터베이스
"CREATE TEMP TABLE" 구문으로 만든 테이블은 "CREATE TEMP TABLE" 문이 원래 평가된 데이터베이스 연결에서만 보여요. 이 TEMP 테이블은 관련된 인덱스, 트리거, 뷰와 함께 첫 번째 "CREATE TEMP TABLE" 문이 보이는 즉시 생성되는 별도의 임시 데이터베이스 파일에 함께 저장돼요. 이 별도의 임시 데이터베이스 파일에도 관련된 롤백 저널이 있어요. TEMP 테이블을 저장하는 데 사용되는 임시 데이터베이스 파일은 sqlite3_close()로 데이터베이스 연결이 닫힐 때 자동으로 삭제돼요.
TEMP 데이터베이스 파일은 ATTACH 문으로 추가된 보조 데이터베이스 파일과 매우 유사하지만, 몇 가지 특별한 속성이 있어요. TEMP 데이터베이스는 데이터베이스 연결이 닫힐 때 항상 자동으로 삭제돼요. TEMP 데이터베이스는 항상 synchronous=OFF와 journal_mode=PERSIST PRAGMA 설정을 사용해요. 그리고 TEMP 데이터베이스는 DETACH와 함께 사용할 수 없고, 다른 프로세스가 TEMP 데이터베이스를 ATTACH할 수도 없어요.
TEMP 데이터베이스와 그 롤백 저널과 관련된 임시 파일은 애플리케이션이 "CREATE TEMP TABLE" 문을 사용하는 경우에만 생성돼요.
2.7. 뷰와 서브쿼리의 자료화
서브쿼리를 포함하는 쿼리는 때때로 서브쿼리를 별도로 평가하고 결과를 임시 테이블에 저장한 다음, 그 임시 테이블의 콘텐츠를 사용해 외부 쿼리를 평가해야 해요. 우리는 이를 서브쿼리를 "자료화(materializing)"한다고 불러요. SQLite의 쿼리 최적화기는 자료화를 피하려 하지만, 때로는 쉽게 피할 수 없어요. 자료화로 만들어진 임시 테이블은 각각 자신의 별도 임시 파일에 저장되며, 쿼리가 끝나면 자동으로 삭제돼요. 이 임시 테이블의 크기는 물론 서브쿼리 자료화의 데이터 양에 따라 달라져요.
IN 연산자의 오른쪽에 있는 서브쿼리는 흔히 자료화되어야 해요. 예를 들어:
SELECT * FROM ex1 WHERE ex1.a IN (SELECT b FROM ex2);
위 쿼리에서 서브쿼리 "SELECT b FROM ex2"가 평가되고 그 결과가 임시 테이블(실제로는 임시 인덱스)에 저장되어, 값 ex2.b가 존재하는지 간단한 이진 검색으로 판단할 수 있게 해요. 이 테이블이 구성되면 외부 쿼리가 실행되고, 각 결과 후보 행에 대해 ex1.a가 임시 테이블 안에 포함되어 있는지 확인해요. 검사가 참인 경우에만 행이 출력돼요.
임시 테이블 생성을 피하려면 쿼리를 다음과 같이 다시 쓸 수 있어요:
SELECT * FROM ex1 WHERE EXISTS(SELECT 1 FROM ex2 WHERE ex2.b=ex1.a);
최근 SQLite 버전(3.5.4 2007-12-14 이후)은 ex2.b 컬럼에 인덱스가 존재하면 이 재작성을 자동으로 수행해요.
IN 연산자의 오른쪽이 다음과 같은 값 리스트라면:
SELECT * FROM ex1 WHERE a IN (1,2,3);
그것들은 자료화되어야 하는 서브쿼리로 취급돼요. 즉, 앞선 문은 마치 다음과 같이 작동해요:
SELECT * FROM ex1 WHERE a IN (SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3);
IN 연산자의 오른쪽이 값 리스트일 때는 그 값을 보유하기 위해 항상 임시 인덱스가 사용돼요.
서브쿼리는 SELECT 문의 FROM 절에 나타날 때도 자료화가 필요할 수 있어요. 예를 들어:
SELECT * FROM ex1 JOIN (SELECT b FROM ex2) AS t ON t.b=ex1.a;
쿼리에 따라 SQLite는 "(SELECT b FROM ex2)" 서브쿼리를 임시 테이블로 자료화한 다음 ex1과 임시 테이블 사이의 조인을 수행해야 할 수 있어요. 쿼리 최적화기는 쿼리를 "평탄화(flattening)"해 이것을 피하려 해요. 앞선 예시에서 쿼리는 평탄화될 수 있고, SQLite는 쿼리를 자동으로 다음으로 변환해요:
SELECT ex1.*, ex2.b FROM ex1 JOIN ex2 ON ex2.b=ex1.a;
더 복잡한 쿼리는 임시 테이블을 피하기 위해 쿼리 평탄화를 사용할 수도 있고 못 할 수도 있어요. 쿼리가 평탄화될 수 있는지 여부는 서브쿼리나 외부 쿼리가 집계 함수, ORDER BY 또는 GROUP BY 절, LIMIT 절 등을 포함하는지 같은 요인에 따라 달라져요. 쿼리가 평탄화될 수 있고 없는 규칙은 매우 복잡하며 이 문서의 범위를 벗어나요.
2.8. 일시적 인덱스
SQLite는 다음과 같은 SQL 언어 기능을 구현하기 위해 일시적 인덱스를 사용할 수 있어요:
- ORDER BY 또는 GROUP BY 절
- 집계 쿼리의 DISTINCT 키워드
- UNION, EXCEPT, INTERSECT로 결합된 복합 SELECT 문
각 일시적 인덱스는 자신의 임시 파일에 저장돼요. 일시적 인덱스의 임시 파일은 그것을 사용하는 문이 끝날 때 자동으로 삭제돼요.
SQLite는 기존 인덱스를 사용해 ORDER BY 절을 구현하려 노력해요. 적절한 인덱스가 이미 존재하면 SQLite는 기반 테이블 대신 그 인덱스를 따라가 요청된 정보를 추출하고, 따라서 행이 원하는 순서로 나오게 해요. 하지만 SQLite가 적절한 인덱스를 찾지 못하면 쿼리를 평가하고 각 행을 일시적 인덱스에 저장하는데, 그 인덱스의 데이터는 행 데이터이고 키는 ORDER BY 항목이에요. 쿼리가 평가된 후 SQLite는 돌아가 일시적 인덱스를 처음부터 끝까지 따라가 행을 원하는 순서로 출력해요.
SQLite는 GROUP BY 항목이 제안하는 순서로 출력 행을 정렬해 GROUP BY를 구현해요. 각 출력 행을 이전 행과 비교해 새 "그룹"을 시작하는지 확인해요. GROUP BY 항목에 의한 정렬은 ORDER BY 항목에 의한 정렬과 정확히 같은 방식으로 수행돼요. 가능하면 기존 인덱스가 사용되지만, 적절한 인덱스가 없으면 일시적 인덱스가 생성돼요.
집계 쿼리의 DISTINCT 키워드는 임시 파일에 일시적 인덱스를 만들고 각 결과 행을 그 인덱스에 저장해 구현돼요. 새 결과 행이 계산될 때 그것들이 이미 일시적 인덱스에 존재하는지 검사하고, 존재하면 새 결과 행이 버려져요.
복합 쿼리의 UNION 연산자는 임시 파일에 일시적 인덱스를 만들고 왼쪽과 오른쪽 서브쿼리의 결과를 그 일시적 인덱스에 저장하면서 중복을 버려 구현돼요. 두 서브쿼리가 모두 평가된 후, 일시적 인덱스를 처음부터 끝까지 따라가 최종 출력을 생성해요.
복합 쿼리의 EXCEPT 연산자는 임시 파일에 일시적 인덱스를 만들고, 왼쪽 서브쿼리의 결과를 이 일시적 인덱스에 저장한 다음, 오른쪽 서브쿼리의 결과를 일시적 인덱스에서 제거하고, 마지막으로 인덱스를 처음부터 끝까지 따라가 최종 출력을 얻어 구현돼요.
복합 쿼리의 INTERSECT 연산자는 각각 별도의 임시 파일에 있는 두 개의 별도 일시적 인덱스를 만들어 구현돼요. 왼쪽과 오른쪽 서브쿼리가 각각 별도의 일시적 인덱스로 평가돼요. 그런 다음 두 인덱스를 함께 따라가며 두 인덱스 모두에 나타나는 항목이 출력돼요.
복합 쿼리의 UNION ALL 연산자는 그 자체로는 일시적 인덱스를 사용하지 않는다는 점을 주목하세요 (물론 UNION ALL의 오른쪽과 왼쪽 서브쿼리는 구성 방식에 따라 일시적 인덱스를 사용할 수 있어요.)
2.9. VACUUM이 사용하는 일시적 데이터베이스
VACUUM 명령은 임시 파일을 만든 다음 전체 데이터베이스를 그 임시 파일로 재구축해 작동해요. 그런 다음 임시 파일의 콘텐츠를 원래 데이터베이스 파일로 다시 복사하고 임시 파일을 삭제해요.
VACUUM 명령이 만든 임시 파일은 명령 자체가 지속되는 동안만 존재해요. 임시 파일의 크기는 원래 데이터베이스보다 크지 않을 거예요.
3. SQLITE_TEMP_STORE 컴파일 타임 매개변수와 Pragma
트랜잭션 제어와 관련된 임시 파일, 즉 롤백 저널, 슈퍼 저널, write-ahead log (WAL) 파일, 공유 메모리 파일은 항상 디스크에 기록돼요. 하지만 다른 종류의 임시 파일은 메모리에만 저장되고 디스크에 기록되지 않을 수 있어요. 롤백, 슈퍼, 문 저널 이외의 임시 파일이 디스크에 기록되는지 메모리에만 저장되는지는 SQLITE_TEMP_STORE 컴파일 타임 매개변수, temp_store pragma, 그리고 임시 파일의 크기에 따라 달라져요.
SQLITE_TEMP_STORE 컴파일 타임 매개변수는 값이 0과 3 사이의 정수(포함)인 #define이에요. SQLITE_TEMP_STORE 컴파일 타임 매개변수의 의미는 다음과 같아요:
- 임시 파일은 temp_store pragma의 설정과 관계없이 항상 디스크에 저장돼요.
- 임시 파일은 기본적으로 디스크에 저장되지만 temp_store pragma로 재정의될 수 있어요.
- 임시 파일은 기본적으로 메모리에 저장되지만 temp_store pragma로 재정의될 수 있어요.
- 임시 파일은 temp_store pragma의 설정과 관계없이 항상 메모리에 저장돼요.
SQLITE_TEMP_STORE 컴파일 타임 매개변수의 기본값은 1로, 임시 파일을 디스크에 저장하지만 temp_store pragma를 사용해 동작을 재정의하는 옵션을 제공한다는 뜻이에요.
temp_store pragma는 임시 파일을 저장할 위치 결정에도 영향을 주는 정수 값을 가져요. temp_store pragma의 값은 다음 의미를 가져요:
- 임시 파일에 대해 SQLITE_TEMP_STORE 컴파일 타임 매개변수가 결정한 대로 디스크 또는 메모리 저장을 사용해요.
- SQLITE_TEMP_STORE 컴파일 타임 매개변수가 임시 파일에 메모리 저장을 지정하면 그 결정을 재정의하고 대신 디스크 저장을 사용해요. 그 외에는 SQLITE_TEMP_STORE 컴파일 타임 매개변수의 권고를 따라요.
- SQLITE_TEMP_STORE 컴파일 타임 매개변수가 임시 파일에 디스크 저장을 지정하면 그 결정을 재정의하고 대신 메모리 저장을 사용해요. 그 외에는 SQLITE_TEMP_STORE 컴파일 타임 매개변수의 권고를 따라요.
temp_store pragma의 기본 설정은 0으로, SQLITE_TEMP_STORE 컴파일 타임 매개변수의 권고를 따르는 것을 의미해요.
다시 강조하면, SQLITE_TEMP_STORE 컴파일 타임 매개변수와 temp_store pragma는 롤백 저널과 슈퍼 저널 이외의 임시 파일에만 영향을 주고, 롤백 저널과 슈퍼 저널은 SQLITE_TEMP_STORE 컴파일 타임 매개변수와 temp_store pragma의 설정과 관계없이 항상 디스크에 기록돼요.
4. 기타 임시 파일 최적화
SQLite는 최근에 읽고 쓴 데이터베이스 페이지의 페이지 캐시를 사용해요. 이 페이지 캐시는 기본 데이터베이스 파일뿐만 아니라 임시 파일에 저장된 일시적 인덱스와 테이블에도 사용돼요. SQLite가 임시 인덱스나 테이블을 사용해야 하고 SQLITE_TEMP_STORE 컴파일 타임 매개변수와 temp_store pragma가 임시 테이블과 인덱스를 디스크에 저장하도록 설정되어 있어도, 정보는 여전히 처음에는 페이지 캐시의 메모리에 저장돼요. 페이지 캐시가 가득 찰 때까지 임시 파일이 열리지 않고 정보가 실제로 디스크에 기록되지 않아요.
이것은 임시 테이블과 인덱스가 작은(페이지 캐시에 들어갈 만큼 작은) 많은 일반적인 경우에 임시 파일이 생성되지 않고 디스크 I/O가 발생하지 않는다는 것을 의미해요. 임시 데이터가 RAM에 들어가기에는 너무 커질 때만 정보가 디스크로 넘쳐 흘러요.
각 임시 테이블과 인덱스는 SQLITE_DEFAULT_TEMP_CACHE_SIZE 컴파일 타임 매개변수가 결정한 최대 데이터베이스 페이지 수를 저장할 수 있는 자신의 페이지 캐시를 받아요. (기본값은 500페이지예요.) 페이지 캐시의 최대 데이터베이스 페이지 수는 모든 임시 테이블과 인덱스에 대해 동일해요. 이 값은 런타임에 또는 테이블별·인덱스별로 변경할 수 없어요. 각 임시 파일은 자신의 SQLITE_DEFAULT_TEMP_CACHE_SIZE 페이지 한도를 가진 자신의 개인 페이지 캐시를 받아요.
5. 임시 파일 저장 위치
임시 파일이 생성되는 디렉터리 또는 폴더는 OS별 VFS가 결정해요.
unix 계열 시스템에서 디렉터리는 다음 순서로 검색돼요:
- PRAGMA temp_store_directory 또는 sqlite3_temp_directory 전역 변수가 설정한 디렉터리
- SQLITE_TMPDIR 환경 변수
- TMPDIR 환경 변수
- /var/tmp
- /usr/tmp
- /tmp
- 현재 작업 디렉터리 (".")
위 중 존재하고 쓰기 및 실행 비트가 설정되어 있는 것으로 발견된 첫 번째 것이 사용돼요. 마지막 "." 대체는 표준 임시 파일 위치를 사용할 수 없는 chroot 감옥 안에서 SQLite를 사용하는 일부 애플리케이션에 중요해요.
Windows 시스템에서 폴더는 다음 순서로 검색돼요:
- PRAGMA temp_store_directory 또는 sqlite3_temp_directory 전역 변수가 설정한 폴더
- GetTempPath() 시스템 인터페이스가 반환한 폴더
SQLite 자체는 이 경우 환경 변수를 전혀 신경 쓰지 않아요. 다만 GetTempPath() 시스템 호출이 그럴 것이라고 추정해요. 검색 알고리즘은 CYGWIN 빌드에서 다르게 작동해요. 자세한 내용은 소스 코드를 확인하세요.
더 알아보기 (Learn more)
- Atomic Commit In SQLite — 원자적 커밋 상세 동작
- Write-Ahead Logging — WAL 모드
- PRAGMA journal_mode / temp_store — 관련 pragma
- VACUUM — VACUUM 명령