Skip to content
Back to skills

Db Maintenance

ASecurity

tiguclaw 데이터베이스(대화·기억·세션)의 백업·용량·아카이브를 다룬다. "백업 해줘·백업 되고 있어?·DB 가 너무 커·용량·디스크·오래된 대화 정리·데이터 옮기기·복구·날아가면" 류 요청, 그리고 데이터가 걸린 유지보수를 시작하기 전에 사용한다. ★백업은 v0.27.0 부터 **자동**이다(스케줄이 아니라 데몬 유지보수 루프) — 스케줄 목록에 없다고 '백업이 없다'고 결론내지 말고 `/status` 나 `<home>/data/backup/` 을 보라. 정리 ≠ 삭제: 콜드 레코드는 옮기되 지우지 않는다.

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
ai-agentsbashsql

Security analysis

A100/100

Scanned September 20, 2026

npx -y skills add tigu77/tiguclaw --skill db-maintenance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Db Maintenance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Db Maintenance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tigu77-db-maintenance/badge)](https://www.skillsdirectory.com/skills/tigu77-db-maintenance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: db-maintenance
description: tiguclaw 데이터베이스(대화·기억·세션)의 백업·용량·아카이브를 다룬다. "백업 해줘·백업 되고 있어?·DB 가 너무 커·용량·디스크·오래된 대화 정리·데이터 옮기기·복구·날아가면" 류 요청, 그리고 데이터가 걸린 유지보수를 시작하기 전에 사용한다. ★백업은 v0.27.0 부터 **자동**이다(스케줄이 아니라 데몬 유지보수 루프) — 스케줄 목록에 없다고 '백업이 없다'고 결론내지 말고 `/status` 나 `<home>/data/backup/` 을 보라. 정리 ≠ 삭제: 콜드 레코드는 옮기되 지우지 않는다.
reach: main
---

# DB 유지보수

`<home>/data/tiguclaw.db` 한 파일에 **기억·세션·대화 기록이 전부** 있다. 그래서 이 문서의
순서는 백업 → 용량 → 아카이브다. 크기는 나중에 문제가 되지만 **백업은 처음부터 문제다.**

## 0. 먼저 잰다 — **숫자를 여기서 읽지 말고 지금 재라**

★이 문서에 "현재 몇 MB · 몇 년 뒤 1GB" 같은 **기준선을 적어두지 않는다.** 그런 값은 적는
순간부터 늙고, 다음 사람이 그걸 사실로 믿는다. **재는 법만 둔다.**

```bash
DB="<home>/data/tiguclaw.db"
sqlite3 "$DB" "
WITH sz AS (SELECT
    (SELECT SUM(pgsize) FROM dbstat WHERE name='transcripts') t,
    (SELECT SUM(pgsize) FROM dbstat WHERE name LIKE 'transcripts_fts%') f,
    (SELECT page_count*page_size FROM pragma_page_count(), pragma_page_size()) total),
 grow AS (SELECT sum(length(content))*1.0 c, (max(ts)-min(ts))/86400000.0 days
    FROM transcripts WHERE ts > (strftime('%s','now')-30*86400)*1000)
SELECT
 '현재 '||round(total/1048576.0)||'MB (대화 '||round(t/1048576.0)||' + 색인 '||round(f/1048576.0)||' = '||round(100.0*(t+f)/total)||'%)',
 '월 증가 ~'||round((c/nullif(days,0))*30*(1.0+f*1.0/nullif(t,0))/1048576.0)||'MB',
 '1GB 까지 '||round((1073741824.0-total)/nullif((c/nullif(days,0))*30*(1.0+f*1.0/nullif(t,0)),0)/12.0,1)||'년'
FROM sz, grow;"
```

★**증가는 대화 원문만 세면 틀린다** — 검색 색인이 원문에 비례해 따라 붙는다(그 비율도 위
쿼리가 현재 값으로 잰다). 원문만 세서 증가율을 절반으로 잡은 적이 있다.

**판정:** `transcripts`(대화 원문) + `transcripts_fts`(그 색인)가 대부분이면 정상이다 —
둘 다 설계상 무한 증가고 대화가 쌓이는 만큼 는다. `events` 는 상한이 있어 안 는다.
**1GB 까지 몇 년이 남았는지**가 구조를 바꿀 때인지 아닌지의 눈금이고, SQLite 는 수 GB 도
잘 다루므로 **수백 MB 에서 손대는 건 이르다.**

## 1. 백업 — **이미 자동으로 돈다**

★**먼저 이것부터 알아라: 백업은 v0.27.0 부터 자동이다.** 스케줄이 아니라 **데몬 유지보수
루프**(코어 자기보전 루프)에서 하루 한 벌씩 `VACUUM INTO` 로 뜨고 7벌만
남긴다. **스케줄 목록에는 안 보인다** — 없다고 "백업 체계가 없다"고 결론내지 마라(중복 스케줄을 제안하지 마라).

**상태 확인은 두 가지로:**
```
/status                     → "백업: 3시간 전 · 7벌 (940MB)" 한 줄
ls -lt <home>/data/backup/  → 실제 파일
```
성공은 **일부러 조용하다**(매일 알림은 배경 소음이 된다). 실패와 첫 벌만 알린다.
끄려면 `settings.json` 의 `backup.enabled: false` — 기본은 켜짐이다.

**손으로 한 벌 더 뜨려면:** 대상 인스턴스의 실제 경로를 확인하고, 기존 백업과 겹치지 않는
새 출력 경로로 `VACUUM INTO`를 실행한다. 날짜만 붙이면 당일 자동 백업과 충돌할 수 있다.
출력 파일은 없거나 비어 있어야 하며, 기존 백업을 지워서 재시도하지 않는다.

```bash
DB="<home>/data/tiguclaw.db"
BACKUP="<home>/data/backup/manual-$(date +%Y%m%d-%H%M%S)-$$.db"
test -f "$DB" &&
mkdir -p "<home>/data/backup" &&
sqlite3 "$DB" "VACUUM INTO '$BACKUP';" &&
sqlite3 -readonly "$BACKUP" "PRAGMA integrity_check;"
```

실제 경로에 작은따옴표가 있으면 SQL 문자열에 그대로 삽입하지 말고 바인딩 가능한 도구를 쓴다.
위 `manual-*` 사본은 자동 7벌 보관 및 `/status` 집계 대상이 아니다. 보관·정리는 별도로 관리한다.
명령 성공과 검사 결과 `ok`를 확인한 뒤 백업 완료로 보고한다. 중단된 출력은 정상 백업으로 쓰지 않는다.

- 실행 중인 DB 본파일만 `cp`로 복사하면 WAL에 있는 최신 내용이 빠질 수 있다.
  `VACUUM INTO`는 원본을 변경하지 않고 일관된 사본을 만든다. 읽기 트랜잭션을 사용하므로
  **락이 전혀 없다**고 보장하지 않는다. 백업 중 원본의 `-wal`·`-shm`을 지우거나 옮기지 않는다.
- **주기·보관은 기본 매일 1회·7벌**이다. 같은 일을 하는 스케줄을 중복 등록하지 않는다.
- 같은 디스크의 사본은 디스크 고장을 대비하지 못한다. 외장·클라우드 이전은 사용자와 목적지를 정한다.

### 복구 — 백업 생성과 별도 작업

복구는 현재 DB를 선택한 시점으로 교체한다. 대상 인스턴스·백업 시점·교체 영향을 확인하고
사용자가 승인한 복구 범위에서 다음 순서로 진행한다.

1. 백업을 읽기 전용으로 열어 `PRAGMA integrity_check` 결과 `ok`와 필요한 데이터가 있는지 확인한다.
2. 대상 데몬과 그 DB를 여는 다른 프로세스를 모두 멈추고, 자동 재시작도 중지됐는지 확인한다.
3. 현재 `tiguclaw.db`와 존재하는 `-wal`·`-shm`을 **한 묶음으로 별도 위치에 보존**한다.
   기존 WAL을 복구할 DB 옆에 남기지 않는다. 실행 중 체크포인트만 했다는 이유로 이 단계를 생략하지 않는다.
4. 검증한 백업의 사본을 대상 `tiguclaw.db` 위치에 놓고 소유권·권한을 확인한다. 원본 백업은 보존한다.
5. 데몬을 시작해 상태와 복구하려던 기록을 확인한다. 실패하면 다시 모든 접근을 멈추고
   보존한 묶음으로 되돌린다. 새로 생긴 WAL과 이전 묶음을 섞지 않는다.

근거: [SQLite VACUUM INTO](https://www.sqlite.org/lang_vacuum.html),
[WAL 파일의 수명과 보존](https://www.sqlite.org/wal.html#the_wal_file).

## 2. WAL 파일이 커 보일 때 — **최고수위지 누수가 아니다**

`ls` 에서 `-wal` 이 수십~수백 MB 로 보여도 그 자체는 이상이 아니다. SQLite 는 WAL 파일을
**줄이지 않고 재사용**한다 — 큰 작업이 한 번 지나가면 그 크기가 그대로 박힌다.

★**파일 크기로 판정하지 마라. 실제 내용은 pragma 로 잰다:**

```bash
sqlite3 "$DB" "PRAGMA wal_checkpoint(PASSIVE);"   # → busy | log | checkpointed  (단위: 페이지)
```

`busy=0` 이고 `log` 가 작으면 **정상**이다(1페이지 = 4KB). 134페이지(=549KB)뿐인 경우가 있었고, 파일 크기만 보고 "체크포인트가 막혔다"고 오판했다.

파일 자체를 줄이려면 `PRAGMA wal_checkpoint(TRUNCATE)` 인데 **짧은 쓰기 락**이 걸린다 —
데몬을 멈춘 뒤나 재시작 직후에 한 번 하면 된다. 급한 일이 아니다(디스크만 차지한다).

## 3. 아카이브 — **"오래된"이 아니라 "안 읽힌 지 오래된"**

날짜로 자르면 **3년 전 것인데 어제도 읽은 대화**가 잘려나간다. 기준은 마지막으로 쓰인 때다.

- ★**정리 ≠ 삭제.** 콜드 레코드는 **별도 파일로 옮기고** 본 DB 에선 뺀다 — 지우지 않는다.
  사용자가 "정리해줘" 라고 해도 **삭제는 명시 승인 없이는 하지 않는다**(비가역).
- **파생물은 다르다.** `transcripts_fts` 는 원문에서 **다시 만들 수 있으므로** 아카이브
  대상이 아니다. 색인만 재생성해도 큰 폭으로 줄어드는 경우가 있다(원문은 안 건드린다).
- **착수 조건**: DB 가 수 GB 이거나 검색·부팅이 체감되게 느려질 때. 그 전엔 하지 마라 —
  안 쓰는 구조를 몇 년 유지보수하게 된다.

★**설계 전에 재료부터 확인하라.** 세션 마지막 사용 시각(`threads.last_used_at`)은 있지만,
`transcripts` 행이 그 세션과 **이어지지 않는 경우가 많다**(서브에이전트·백그라운드 작업 등이
남긴 기록으로 **추정** — 확인된 바 아님). 한 인스턴스에선 대부분이 그랬다. 비율은 인스턴스마다
다르니 **아래 쿼리로 지금 재라.** 이걸 모르고 "마지막 사용 시각으로 자르면 된다"고 설계하면 **대부분을 못 건드리거나, 못
건드리는 것을 오래됐다고 지운다.** 먼저 그 비율부터 세라:

```bash
sqlite3 "$DB" "SELECT CASE WHEN th.claude_session_id IS NULL THEN '세션 연결 없음' ELSE '연결됨' END,
  count(*), round(sum(length(t.content))/1048576.0,1)||'MB'
  FROM transcripts t LEFT JOIN threads th ON th.claude_session_id = t.claude_session_id GROUP BY 1;"
```

## 4. 하지 말 것

- **DB 파일을 `cp`·`rsync` 로 백업하지 마라**(§1).
- **대화 기록을 지우지 마라.** 용량이 급해도 먼저 옮긴다. 삭제는 사용자 명시 승인 사안이다.
- **크기만 보고 손대지 마라** — 수백 MB 는 SQLite 에게 작다. 바꿀 근거는 *체감되는 느려짐*이다.
- **`VACUUM`(INTO 없이)을 습관처럼 돌리지 마라** — 전체를 다시 쓰느라 오래 걸리고 그동안 잠긴다.
  조각이 실제로 문제일 때만(`PRAGMA freelist_count` 가 크다) 데몬을 멈추고 한다.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.