사용량과 요금
사용량과 요금 (Usage & Billing)
Starter와 Scaler 플랜에서는 매 달력 월 동안의 사용량 관측치를 기준으로 Turso 사용량이 월간 제한돼요: 행 읽기, 행 쓰기, 총 스토리지 세 가지예요. 쿼리가 이 한도를 넘으면 어떻게 되는지, 어떻게 줄일 수 있는지 알아봐요.
출처: 문서
본문
Starter와 Scaler 플랜에서는 Turso 사용량이 각 달력 월 동안의 다음 사용량 관측치를 기준으로 월간 제한돼요:
- 테이블 행 읽기(rows read) 횟수
- 테이블 행 쓰기(rows written) 횟수
- 총 스토리지(total storage) 양
행 읽기, 행 쓰기, 총 스토리지에 월간 할당량이 포함된 요금 플랜에서는, 이 한도를 초과하는 쿼리는
BLOCKED오류 코드로 표시되는 실패로 이어져요.
행 읽기 (Rows Read)
SQLite에서 "행 읽기(row read)"는 실제로는 문(statement) 실행 중의 "행 스캔(row scan)"을 가리켜요. Turso CLI 메트릭에서 기억할 핵심은 이래요:
- SQL Queries(쿼리): 반환되는 행보다 더 많은 행을 스캔할 수 있어요.
- SQL Updates(업데이트): 업데이트되는 각 행마다 최소 한 번의 행 스캔이 발생해요.
집계 함수의 영향 (Aggregate Function Impact)
count, avg, min, max, sum 같은 함수를 쓰면 집계에 고려되는 모든 행마다 행 스캔이 발생해요.
집계 값을 별도 테이블에 저장하고, 기본 테이블 변경과 트랜잭션으로 함께 갱신하면 쿼리 효율을 높일 수 있어요.
전체 테이블 스캔 (Full Table Scans)
인덱스 지원이 없는 쿼리는 전체 테이블 스캔을 수행해서, 테이블의 각 행마다 행 스캔을 유발해요.
비용이 큰 테이블 스캔을 최소화할 전략을 찾아보세요.
복잡한 쿼리 비용 (Complex Query Costs)
테이블 조인, 서브쿼리, 복합 쿼리는 관련된 모든 테이블에서 고려되는 각 행마다 행 스캔을 유발해요.
업데이트의 메커니즘 (Update Mechanics)
SQL 업데이트는 자기가 수정하는 각 행을 읽고(그리고 씁니다)요. 행 필터링을 위한 인덱스가 없으면 전체 테이블 스캔이 수행되어, 테이블의 각 행마다 읽기가 한 번씩 더해지고 업데이트된 각 행마다 쓰기가 더해져요.
ALTER TABLE과 행 읽기 (ALTER TABLE and Row Reads)
ALTER TABLE 연산, 특히 행 내용을 다시 쓰는 연산은 전체 테이블 스캔이 필요하고 테이블의 각 행마다 읽기를 유발해요. 다만 DROP COLUMN 같은 모든 ALTER TABLE 작업이 전체 스캔으로 이어지는 건 아니에요. 잠재적인 행 쓰기도 함께 신경 쓰세요.
인덱싱 비용 (Indexing Costs)
기존 테이블에 인덱스를 추가하면 전체 테이블 스캔이 트리거되어, 기존 각 행마다 읽기가 한 번씩 발생해요.
SQLite 시스템 테이블 (SQLite System Tables)
dbstat 같은 SQLite 내부 테이블과 sqlite_ 접두사가 붙은 테이블은 쿼리에서 행 읽기를 유발하지 않아요.
읽기 없는 명령 (Zero-Read Commands)
행 읽기/쓰기를 수반하지 않는 명령(예: select 1)은 기본적으로 행 읽기 한 번으로 계산돼요.
행 쓰기 (Rows Written)
SQLite에서 "행 쓰기(row written)"는 새 행의 삽입과 기존 행의 업데이트를 모두 포괄해요.
ALTER TABLE과 행 쓰기 (ALTER TABLE and Row Writes)
ALTER TABLE 연산은 기존 각 행마다 행 쓰기를 유발할 수 있어요. 특히 처리 과정에서 행 데이터가 변경되는 경우에 그래요. 다양한 종류의 ALTER TABLE 문이 행 쓰기에 어떤 영향을 주는지 이해해 두는 게 중요해요.
중단된 트랜잭션의 영향 (Implications of Aborted Transactions)
트랜잭션이 커밋되지 않았더라도, 트랜잭션 도중에 삽입되거나 업데이트된 행은 행 쓰기를 유발해요. 데이터베이스 쓰기를 통제하려면 트랜잭션 관리가 얼마나 중요한지 보여 주는 대목이에요.
ALTER TABLE작업은 행 읽기로도 이어질 수 있어요. 테이블 구조를 변경할 때 함께 고려할 또 하나의 층이에요.
총 스토리지 (Total Storage)
SQLite는 가상 테이블 dbstat을 활용해 모든 테이블과 인덱스가 사용하는 총 공간을 계산해요. 이 측정의 기본 단위는 데이터베이스 파일 페이지(page)이고 4KB예요.
SQLite에서
VACUUM명령은 데이터베이스를 압축해 스토리지를 최적화하는 흔한 도구예요. 하지만 이 명령은 현재 Turso에서 비활성화되어 있다는 점을 유의하세요. 향후 업데이트에서 개발자가 데이터베이스의 총 스토리지 사용량을 효율적으로 관리·축소할 수 있는 옵션이 도입될 수 있어요.
사용량 줄이기 (Reducing Usage)
쿼리 실행 (Query Execution)
SQLite 쿼리 플래너를 익히면 쿼리가 어떻게 실행되는지 이해가 크게 깊어져요. 쿼리 효율 최적화에 결정적인 지식이에요.
쿼리 계획 (Query Planning)
EXPLAIN QUERY PLAN 문을 활용하면 쿼리의 실행 계획을 들여다볼 수 있어요. 쿼리가 전체 테이블 스캔을 하는지, 불필요한 읽기를 줄이기 위해 가장 효율적인 인덱스를 활용하고 있는지 파악하는 데 이만한 도구가 없어요.
인덱싱 (Indexing)
행 필터링에 인덱스를 활용하도록 쿼리를 설계하세요. 적절한 인덱스가 없으면 SQLite가 전체 테이블 스캔으로 전환하고, 테이블의 각 행마다 읽기 횟수가 하나씩 늘어나요. 효율적인 인덱싱이 이 오버헤드를 최소화하는 핵심이에요.
테이블 생성 단계에서 필요한 인덱스를 함께 만드는 게 모범 사례예요. 이미 행이 들어 있는 테이블에 인덱스를 추가하면 전체 테이블 스캔이 트리거되어 기존 각 행마다 읽기가 한 번씩 필요해져요. 능동적인 인덱스 관리가 최적의 데이터베이스 성능 유지에 꼭 필요해요.