본문으로 건너뛰기

SQL 미러

SqlMirror는 storh 컬렉션을 SQLite 또는 MySQL 테이블로 보내므로 파일을 표준 상태로 유지하면서 SQL에서 조인, 테이블 간 정렬, 부분 문자열 검색 및 보고를 수행할 수 있습니다. 데이터베이스이기도 한 보조 인덱스라고 생각하면 됩니다. 미러는 파생된 데이터이며 언제든 폐기하고 다시 만들 수 있습니다. sqlite 또는 mysql 드라이버를 사용하는 경우에만 ext-pdo가 필요하며, 핵심 패키지에는 종속성이 없습니다.

설정

PHP
use Storh\DocStore;
use Storh\Schema;
use Storh\SqlMirror;

$schema = Schema::collection('pages')
    ->string('slug')->unique()
    ->string('kind')->index()
    ->int('publishedAt')->range();

$pages  = new DocStore($root, 'pages', schema: $schema);
$events = new Storh\SegmentedLog($root, 'events');

$mirror = new SqlMirror(new PDO('sqlite:' . $root . '/mirror.db'), 'app_');
$mirror->collection($pages, 'pages', $schema);
$mirror->collection($events, 'events');

$mirror->install();
$mirror->push();

두 엔진 모두 미러링할 수 있습니다. DocStore 컬렉션과 SegmentedLog 스트림(라이브 레코드만 전송)입니다. Schema 전달은 선택 사항이며 저장소와 별개입니다. 선언된 string/int/float/bool 필드는 형식 지정된 인덱스 열이 되고, 나머지는 모두 data JSON 열에서 쿼리할 수 있습니다. mixed 필드에는 인덱스 플래그를 설정할 수 없습니다.

연결

생성자는 PDO 핸들(sqlite 또는 mysql 드라이버로 자동 감지), mysqli 핸들 또는 모든 SqlMirrorConnection 구현을 허용합니다:

PHP
$mirror = new SqlMirror(new PDO('sqlite:' . $root . '/mirror.db'));
$mirror = new SqlMirror(new PDO('mysql:host=127.0.0.1;dbname=app;charset=utf8mb4', $user, $password));
$mirror = new SqlMirror(new mysqli('127.0.0.1', $user, $password, 'app'), 'wp_storh_');

mysqli 경로는 두 가지 mysqli 오류 보고 모드 모두에서 작동하므로 호스트 애플리케이션의 기존 핸들(예: WordPress의 $wpdb->dbh)을 그대로 재사용할 수 있습니다. 드라이버 간 동작은 동일하며, 전송에 실패하면 모든 드라이버에서 해당 컬렉션의 트랜잭션을 롤백합니다.

각 테이블에는 id(기본 키), hash(증분 전송 처리), 형식 지정 열 및 data(전체 레코드 JSON)가 있습니다.

동기화

PHP
$mirror->push();                    // reconcile: insert, update, delete as needed
$mirror->push('pages');             // reconcile a single collection
$mirror->flush('pages', $ids);      // push or remove just these ids
$mirror->pull();                    // write mirror rows back into the store
$mirror->verify();                  // report missing, stale, and orphaned rows
$mirror->rebuild();                 // clear all rows and push from scratch
$mirror->uninstall();               // drop the mirror tables

push()는 레코드 해시를 미러와 비교하고 각 컬렉션을 하나의 트랜잭션으로 기록하므로 전송 도중 충돌이나 실패가 발생해도 배치가 일부만 적용되지 않습니다. 변경되지 않은 레코드에는 레코드 순회와 함께 SELECT 한 번의 비용이 듭니다. 주기적인 push()(예: cron 틱에서 실행)는 미러를 최신 상태로 유지합니다. 요청 중 기록한 ID에 대해 미러에서 읽기 직후 쓰기 결과를 확인해야 한다면 flush()를 호출하세요.

쿼리

일반 SQL로 미러를 쿼리합니다. table()는 미러링된 테이블 이름을 반환합니다:

PHP
$pdo->query(
    'SELECT p.slug, COUNT(*) AS sales
     FROM ' . $mirror->table('purchases') . ' o
     INNER JOIN ' . $mirror->table('products') . ' p ON p.id = o.product_id
     GROUP BY p.slug
     ORDER BY sales DESC'
);

파일로 다시 가져오기

pull()는 미러 행을 ID 순서로 읽어 등록된 저장소에 기록합니다. 두 가지 흐름을 지원합니다:

  • 복원: 파일은 사라졌지만 미러는 남아 있습니다. 같은 컬렉션 이름으로 새 저장소를 등록한 뒤 가져오세요.
  • 초기 데이터 채우기: 외부 도구로 미러 테이블에 불러온 행은 storh 레코드가 됩니다. 행에는 유효한 UUIDv7 ID와 data 열의 객체가 있어야 합니다. data가 전체 레코드이므로 가져올 때 형식 지정 열은 무시됩니다.
PHP
$restored = new Storh\DocStore($root, 'pages');
$mirror   = new SqlMirror($pdo);
$mirror->collection($restored, 'pages');
$mirror->pull();          // ['written' => n, 'unchanged' => m]

가져온 데이터도 일반 저장소 처리 과정을 거치므로 스키마 검증과 고유 인덱스가 적용되고 모든 레코드가 내구성 있게 기록됩니다. pull()는 로컬 레코드를 삭제하지 않으며, 데이터가 미러 행과 이미 일치하는 레코드는 건너뜁니다. 직접 삽입한 행을 초기 데이터로 사용한 뒤 push()를 한 번 실행하세요. 해시를 일치시키기 위해 해당 행이 표준 형식으로 다시 기록됩니다.

계약 및 한도

  • 파일이 표준 데이터입니다. 미러는 읽기 전용 출력이며, 직접 기록한 내용은 다음 push()에서 덮어쓰거나 삭제합니다.
  • 미러의 고유 열은 보조 안전장치이며 쓰기 시 적용되지 않습니다. 파일 저장소에서 허용한 중복 값도 전송 실패로 나타나며 전체 롤백됩니다. 쓰기 시 적용하려면 storh 고유 인덱스를 사용하세요.
  • 미러는 최대 한 번의 전송 간격만큼 파일보다 늦습니다. 단, flush()를 명시적으로 전송한 ID는 예외입니다.
  • 미러링을 사용해도 storh 자체에 트랜잭션이 추가되지는 않습니다. 여러 레코드에 걸친 원자적 쓰기는 기준 시스템인 실제 데이터베이스에서 처리해야 합니다.
  • 스키마를 변경하려면 미러를 삭제하고 다시 설치하세요: uninstall(), install(), push().