複合行キーについて
複合行は、データベース内のユニークなエンティティを識別する2つ以上の属性で構成されます。複合行キーはGridDB v4.3で追加されたもので、コレクション内の複数のカラムによって行を一意に識別することができます。
なぜ複合行キーは役立つのでしょうか?多くの場合、データセットの1つのカラムが一意ではなく、インクリメントする整数のカラムを作成するか、他のカラムの値で構成される文字列を作成する必要があります。
TIMESTAMP | Location | Data |
---|---|---|
2021-07-15 21:05:58.898 | 4 | 76.85 |
2021-07-15 21:05:58.898 | 6 | 63.66 |
2021-07-15 21:05:59.398 | 3 | 56.12 |
2021-07-15 21:05:59.998 | 4 | 27.79 |
上記のスキーマに基づくと、ユニークキーとなるカラムはありませんが、TIMESTAMPとLOCATIONを組み合わせて合成行キーとすることで、各行を一意に識別することができます。行キーがないと、同じタイムスタンプやセンサーの位置を持つ別の行を挿入した時に、コレクションに別の行が追加されてしまいます。もし、タイムスタンプだけで行を構成した場合、同じ時間に発光した2つ目のセンサーが最初のセンサーを上書きしてしまうということが起こります。これをキーコリジョンと呼びます。
複合行キーを持つコンテナの作成
複合行キーを持つコレクションを作成するには、複合行キーを構成する列のリストを設定する前に、列情報オブジェクトを指定する必要があります。
ContainerInfo containerInfo = new ContainerInfo();
List<columninfo> columnList = new ArrayList</columninfo><columninfo>();
columnList.add(new ColumnInfo("name", GSType.STRING));
columnList.add(new ColumnInfo("phone_number", GSType.STRING));
columnList.add(new ColumnInfo("calls", GSType.INTEGER));
containerInfo.setColumnInfoList(columnList);
containerInfo.setRowKeyColumnList(Arrays.asList(0, 1));
Collection< ?, Row> col = store.putCollection(containerName, containerInfo, false);</columninfo>
そして、コレクションに行を追加することができるようになります。
col.setAutoCommit(false);
Row row = col.createRow();
row.setString(0, "John");
row.setString(1, "123-555-7890");
row.setInteger(2, 10);
col.put(row);
col.commit();
row = col.createRow();
row.setString(0, "John");
row.setString(1, "123-555-4567");
row.setInteger(2, 5);
col.put(row);
col.commit();
row = col.createRow();
row.setString(0, "John");
row.setString(1, "123-555-7890");
row.setInteger(2, 15);
col.put(row);
col.commit();
クエリ
また、Predicate RowKey Map を使って、複合行キーを使ったクエリを実行することもできます。Predicate RowKey Mapは、コンテナ名をキーとし、そのコンテナの値としてのキーのセットを値とするJava HashMapインターフェイスを使用して、複数のクエリを指定することができます。
RowKeyPredicate<Row.Key> predicate = RowKeyPredicate.create(containerInfo);
Map<String, RowKeyPredicate<Row.Key>> predMap = new HashMap<String, RowKeyPredicate<Row.Key>>();
Row.Key rowKey = store.createRowKey(containerInfo);
rowKey.setString(0, "John");
rowKey.setString(1, "123-555-7890");
predicate.add(rowKey);
predMap.put(containerName, predicate);
Map<String, List<row>> result = store.multiGet(predMap);
Row rrow = result.get(containerName).get(0);
System.out.println(rrow.getString(0)+" "+rrow.getString(1)+" "+rrow.getInteger(2));
</row>
注意点
GridDB の 複合行キーの実装にはいくつかの制限があり、主に Java API でしか利用できません。Python や他の言語でコンテナにクエリを実行すると、例外が発生します。JDBC コネクタを使用して複合行キーを持つコレクションを照会することはできますが、行の挿入や更新はできません。
複合行キーはTime Seriesコンテナでは使用できませんが、TIMESTAMPを複合行キーの一部として使用することができます。
想定される用途
以前のブログで、ニューヨークのタクシーの コミッションのデータを分析しました。そのブログでは、データセットに一意の識別子がなかったため、行キーの衝突が発生しました。一意の識別子がないことと、行キーがないことで、データを2回以上取り込むと、同じトリップが重複して含まれてしまい、エラーになる可能性があります。
時系列データの予測の記事でも、同じような現象が起こりました。そのチュートリアルでは、複合行キーを使用することで防ぐことができた暗黙の行キーの衝突がある集約を格納する中間コンテナがありました。
そのデータセットには、約125の選挙区の月次統計がそれぞれ保存されていました。そのため、スキーマを計画する際には、1つの管区の複数月分のデータを保存するため、管区を行キーとして使用することはできませんでした。そこで、行キーの衝突やデータの重複を防ぐために、行キーを境内とタイムスタンプを連結した文字列列に設定しました。これは非常に初歩的で遠回りな解決方法でしたが、GridDBの複合行キー機能を使えば簡単に解決できるはずです。
まとめ
複合行キーは、データ設計を行う際に、他の選択肢ではアプリケーションの要件を満たせない場合に、競合を防ぐための便利な仕組みです。インクリメントされた整数を管理したり、複数の文字列を付加したりするよりもきれいなソリューションですが、GridDBの柔軟性やキーコンテナデータアーキテクチャ全体を考慮すると、必要な場合を除いて避けるべきだと私たちは考えています。
例えば、各センサーIDに独自のコンテナを与え、そのコンテナ名をセンサーのIDにすることで、複合行キーを回避できることがよくあります。この場合、ほとんどのIoTユースケースでは、現実的にこの状況を避けることができます。しかし、どうしても避けられない場合もあります。そのような場合には、オプションとしてコンポジットロウが存在することで、安心することができます。
本ブログのソースコード一式は、GridDB.netのGitHubリポジトリに掲載されています。
ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb