「この顧客の、まだ open な注文をすべて」。「このカートに何点入っているか」。これらはアプリケーションが毎秒何千回も行う読み取りであり、リレーショナルデータベースでは、そのひとつひとつが、裏にプランナを抱えたクエリになります。kevy なら、答えを用意しておけます。
キーバリューストアをアプリケーションのデータに使うことは、たいていひとつの反論で退けられます。キー以外のもので引きたいのだが、と。その反論は正しく、セカンダリインデックスは、まさにそのためにあります。
インデックスは、構築するものではなく宣言するものです。キーのパターンとフィールドを指定すれば、書き込みパスが、それを最新に保ちます。絞り込んだ一覧は、ふたたび参照になります。プランナもスキャンもクエリもありません。
ビューはさらに進んで、書き込み時に集計を最新に保ちます。件数や合計は、計算されるのではなく読まれます。ほとんどのアプリケーションが ORM に実際に求めているのはこれであり、データベースが忙しい理由も、これです。
そして、テーブル全体を一度に宣言できます。TABLE.DECLARE は型付きカラム、セカンダリインデックス、複合 ORDER BY パスを受け取り、宣言の時点で名前つきインデックスへコンパイルします——kevy-sql は、手元の PG/MySQL スキーマファイルから同じことをします。エンジンは相変わらず何もプランせず、スキーマを課しません。join とランタイム SQL は、名前つきで拒否されたままです。
「顧客 881 の注文をすべて」が、参照のままです。引きたいフィールドごとにインデックスを宣言し、普通に書き、値で読みます。
HSET order:1001 customer 881 status open total 4400
HSET order:1002 customer 881 status paid total 8400
HSET order:1003 customer 902 status open total 1200IDX.CREATE idx:cust ON PREFIX order: FIELD customer TYPE i64 KIND range
IDX.CREATE idx:status ON PREFIX order: FIELD status TYPE str KIND rangeIDX.QUERY idx:cust EQ 881
-> 1) "0" # cursor
2) 1) "order:1001" 2) "881"
3) "order:1002" 4) "881"IDX.QUERY COMPOSE AND idx:cust EQ 881 idx:status EQ open
-> 1) "0"
2) 1) 1) "order:1001"コストと制約 インデックスの代金は、読み取りではなく書き込みのたびに支払います——読み取り主体の配信には正しく、書き込み主体のログには間違った取引です。結合はありませんし、今後も持ちません。インデックスが答えるのは「どのキーがこれらのフィールドに合致するか」であって、「この 2 つのコレクションを結合せよ」ではありません。読み取りに本当に結合が要るなら、Postgres に置いておいてください——どれがそれに当たるのかは、RDS ワークロードのページに書いてあります。
絞り込まれ、順序のついた一覧を、書き込みパスが維持します——古くなることが決してないので、読み取りが計算し直すこともありません。
VIEW.CREATE v:open881 QUERY ( AND idx:cust EQ 881 idx:status EQ open ) ORDER BY idx:cust
-> OK括弧は、それぞれ独立した引数です。
VIEW.QUERY v:open881
-> 1) "0"
2) 1) "order:1001" 2) "881"コストと制約 ビューは書き込みパスの、終わらない仕事です。合致するキーへの書き込みは、今日それを読む人がいるかどうかに関係なく、毎回ビューを更新します。アプリケーションが実際に捌いている読み取りのためにビューを宣言し、捌かなくなったら落としてください。ビューが組み合わせるインデックスは、先に存在している必要があります。
リレーショナルなテーブルの読み取りパス——インデックスされた WHERE、残りのフィルタ、ORDER BY、ページング、COUNT——を、宣言 1 つで名前つきインデックスへコンパイルします。手元のスキーマファイルからでも。
TABLE.DECLARE orders PREFIX order: PK id COLUMN id str COLUMN customer i64 COLUMN status str COLUMN total f64 INDEX status range VALUES total customer ORDERPATH by_customer ON customer THEN total DESC
-> OK行はプレフィックス配下の普通のハッシュのままです——欠けたカラムは NULL。kevy-cli sql compile schema.sql が、CREATE TABLE / CREATE INDEX からこの行を出力します。
IDX.QUERY orders.status EQ open FILTER total RANGE 2000 inf LIMIT 20
-> 1) "0"
2) 1) "order:1001" 2) "open"
IDX.COUNT orders.status EQ open
-> (integer) 2IDX.QUERY orders.by_customer WHERE customer EQ 881 LIMIT 20 FIELDS status totalリレーショナルの複合インデックスと同じやり方で、複合インデックス 1 本が答えます——顧客ごとの注文を、大きい順に、再ソートなしで。
コストと制約 ランタイム SQL も join もありません。サーバーは SELECT を未知のコマンドとして拒否します。kevy-cli sql compile はビルド時に PG/MySQL のスキーマファイルを上の宣言に変え、JOIN、サブクエリ、GROUP BY を名前つきで拒否して、それぞれを置き換えるレシピを指し示します。一意性は強制ではなく検証で、制約はエンジンのチェックではなくレシピです。ティアリングをオンにすれば、index-only クエリは全行コールドでも RAM だけで答えます——コールドな行を読むのは最後の FIELDS ページだけ、1 行 1 回です。