セカンダリインデックスを使用すると、データテーブルのプライマリキー以外の列を使って行をクエリできます。プライマリキーのみによるクエリでは必要なデータを効率的に取得できない場合、関連する事前定義列にセカンダリインデックスを作成して検索を高速化してください。インデックス作成後は、データテーブル全体をスキャンする代わりに、直接そのインデックスをクエリします。
前提条件
開始前に、以下の条件を満たしていることを確認してください。
データテーブルの Max Versions が 1 に設定されていること。
データテーブルの TTL が -1(データが有効期限切れにならない)に設定されているか、Allow Updates パラメーターが No に設定されていること。
セカンダリインデックスの TTL は、データテーブルの TTL と同じです。
ステップ 1:(任意)事前定義列の管理
セカンダリインデックスは、データテーブル作成時に事前定義列として宣言された列のみを使用できます。データテーブルに事前定義列がない場合や、既存の事前定義列がインデックス要件と一致しない場合は、インデックス作成前に事前定義列を追加または削除してください。
以下の例では、Tablestore SDK for Java を使用しています。Tablestore SDK for Go もサポートされています。
事前定義列の追加
メソッド
public AddDefinedColumnResponse addDefinedColumn(AddDefinedColumnRequest addDefinedColumnRequest) throws TableStoreException, ClientException
パラメーター
|
パラメーター |
型 |
説明 |
|
tableName (必須) |
String |
データテーブルの名前。 |
|
definedColumns (必須) |
List |
事前定義列の情報。各事前定義列には以下のパラメーターが含まれます。
|
サンプルコード
以下のサンプルコードは、test_table に name という名前の String 型の事前定義列を追加します。
public static void addDefinedColumnExample(SyncClient client) {
AddDefinedColumnRequest addDefinedColumnRequest = new AddDefinedColumnRequest();
addDefinedColumnRequest.setTableName("test_table");
addDefinedColumnRequest.addDefinedColumn("name", DefinedColumnType.STRING);
client.addDefinedColumn(addDefinedColumnRequest);
}
事前定義列の削除
メソッド
public DeleteDefinedColumnResponse deleteDefinedColumn(DeleteDefinedColumnRequest deleteDefinedColumnRequest) throws TableStoreException, ClientException
パラメーター
|
パラメーター |
型 |
説明 |
|
tableName (必須) |
String |
データテーブルの名前。 |
|
definedColumns (必須) |
List<String> |
削除する事前定義列の名前。 |
サンプルコード
以下のサンプルコードは、test_table から name という名前の事前定義列を削除します。
public static void deleteDefinedColumnExample(SyncClient client) {
DeleteDefinedColumnRequest deleteDefinedColumnRequest = new DeleteDefinedColumnRequest();
deleteDefinedColumnRequest.setTableName("test_table");
deleteDefinedColumnRequest.addDefinedColumn("name");
client.deleteDefinedColumn(deleteDefinedColumnRequest);
}
ステップ 2:セカンダリインデックスの作成
CreateIndex 操作を呼び出して、既存のデータテーブルに対してインデックステーブルを作成し、データクエリを高速化します。セカンダリインデックスは、グローバルセカンダリインデックスとローカルセカンダリインデックスに分類されます。ビジネス要件に基づいて、グローバルまたはローカルのセカンダリインデックスを作成できます。
CreateTable 操作を呼び出すことで、データテーブル作成時に同時に 1 つ以上のインデックステーブルを作成することも可能です。詳細については、「データテーブルの作成」をご参照ください。
以下の例では、Tablestore SDK for Java を使用しています。以下の SDK もサポートされています:Go、Python、Node.js、.NET、および PHP。
グローバルセカンダリインデックスの作成
以下のサンプルコードは、グローバルセカンダリインデックスを作成します。このインデックスは、DEFINED_COL_NAME_1 を最初のプライマリキー列として、PRIMARY_KEY_NAME_2 を 2 番目のプライマリキー列として使用します。DEFINED_COL_NAME_2 は属性列としてインデックスに含めることで、データテーブルを参照せずにインデックスから直接読み取れます。
IncludeBaseData を true に設定すると、既存のデータテーブルの行をインデックスに含めます。既存データのバックフィルに必要な時間は、データ量によって異なります。
private static void createIndex(SyncClient client) {
IndexMeta indexMeta = new IndexMeta("<INDEX_NAME>");
// DEFINED_COL_NAME_1 をインデックスの最初のプライマリキー列として設定します。
indexMeta.addPrimaryKeyColumn(DEFINED_COL_NAME_1);
// PRIMARY_KEY_NAME_2 をインデックスの 2 番目のプライマリキー列として設定します。
indexMeta.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
// DEFINED_COL_NAME_2 を属性列としてインデックスに含め、インデックスから直接読み取り可能にします。
indexMeta.addDefinedColumn(DEFINED_COL_NAME_2);
// 既存データをバックフィルせずにインデックスを作成します。
// 既存データを含める場合は、第 3 パラメーターを true に設定します。
CreateIndexRequest request = new CreateIndexRequest("<TABLE_NAME>", indexMeta, false);
client.createIndex(request);
}
ローカルセカンダリインデックスの作成
以下のサンプルコードは、ローカルセカンダリインデックスを作成します。インデックスの最初のプライマリキー列 (PRIMARY_KEY_NAME_1) は、データテーブルの最初のプライマリキー列と一致する必要があります。インデックスタイプは IT_LOCAL_INDEX に、更新モードは IUM_SYNC_INDEX(同期更新)に設定されています。
IncludeBaseData を true に設定すると、既存のデータテーブルの行をインデックスに含めます。既存データのバックフィルに必要な時間は、データ量によって異なります。
private static void createIndex(SyncClient client) {
IndexMeta indexMeta = new IndexMeta("<INDEX_NAME>");
// ローカルセカンダリインデックスの最初のプライマリキー列は、
// データテーブルの最初のプライマリキー列と一致する必要があります。
indexMeta.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1);
indexMeta.addPrimaryKeyColumn(DEFINED_COL_NAME_1);
indexMeta.addDefinedColumn(DEFINED_COL_NAME_2);
indexMeta.setIndexType(IT_LOCAL_INDEX);
indexMeta.setIndexUpdateMode(IUM_SYNC_INDEX);
// 既存データをバックフィルせずにインデックスを作成します。
// 既存データを含める場合は、第 3 パラメーターを true に設定します。
CreateIndexRequest request = new CreateIndexRequest("<TABLE_NAME>", indexMeta, false);
client.createIndex(request);
}
ステップ 3:インデックステーブルからのデータ読み取り
読み取りパスは、必要な属性列によって異なります。
必要な列がインデックステーブルに含まれている場合 — インデックステーブルから直接読み取ります。
必要な列がインデックステーブルに含まれていない場合 — インデックステーブルをスキャンして一致する各行のプライマリキーを取得し、その後データテーブルからそれらの列をフェッチします。この 2 ステップ方式では各行ごとに追加の読み取りが発生するため、インデックス作成時に頻繁にクエリされる属性列をインデックスに含めておくことを推奨します。
以下の例では、Tablestore SDK for Java を使用しています。以下の SDK もサポートされています:Go、Python、Node.js、.NET、および PHP。
単一行の読み取り
インデックステーブルのプライマリキーを構築し、getRow を呼び出します。以下のサンプルコードは、単一行を読み取った後、特定の列フィルターを使用して再度読み取ります。
private static void getRowFromIndex(SyncClient client) {
// インデックステーブルのプライマリキーを構築します。
// ローカルセカンダリインデックスの場合、最初のプライマリキー列は
// データテーブルの最初のプライマリキー列と一致する必要があります。
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromString("def1"));
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(100));
primaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromString("pri1"));
PrimaryKey primaryKey = primaryKeyBuilder.build();
SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<INDEX_NAME>", primaryKey);
criteria.setMaxVersions(1);
GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
Row row = getRowResponse.getRow();
// 行が存在しない場合は null を返します。
System.out.println("Read result: " + row);
// 特定の列を読み取ります。
criteria.addColumnsToGet("Col0");
getRowResponse = client.getRow(new GetRowRequest(criteria));
row = getRowResponse.getRow();
System.out.println("Read result: " + row);
}
複数行の範囲読み取り
インデックステーブル上で開始プライマリキーと終了プライマリキーを設定し、getRange を nextStartPrimaryKey が null になるまでループで呼び出します。
グローバルセカンダリインデックスの使用
必要なすべての列がインデックステーブルに含まれている場合、インデックスから直接読み取ります。
private static void scanFromIndex(SyncClient client) {
RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria("<INDEX_NAME>");
PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MIN);
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
rangeRowQueryCriteria.setMaxVersions(1);
System.out.println("Scan result of the index table:");
while (true) {
GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
for (Row row : getRangeResponse.getRows()) {
System.out.println(row);
}
if (getRangeResponse.getNextStartPrimaryKey() != null) {
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
} else {
break;
}
}
}
必要な列がインデックステーブルに含まれていない場合、インデックスをスキャンして一致する各行のプライマリキーを取得し、その後データテーブルからそれらの列をフェッチします。
private static void scanFromIndex(SyncClient client) {
RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria("<INDEX_NAME>");
PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MIN);
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
rangeRowQueryCriteria.setMaxVersions(1);
while (true) {
GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
for (Row row : getRangeResponse.getRows()) {
// インデックス行からデータテーブルのプライマリキーを抽出します。
PrimaryKey curIndexPrimaryKey = row.getPrimaryKey();
PrimaryKeyColumn pk1 = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_1);
PrimaryKeyColumn pk2 = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
PrimaryKeyBuilder mainTablePKBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, pk1.getValue());
mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, pk2.getValue());
PrimaryKey mainTablePK = mainTablePKBuilder.build();
// データテーブルから必要な列をフェッチします。
SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("<TABLE_NAME>", mainTablePK);
criteria.addColumnsToGet(DEFINED_COL_NAME_3);
criteria.setMaxVersions(1);
GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
Row mainTableRow = getRowResponse.getRow();
System.out.println(row);
}
if (getRangeResponse.getNextStartPrimaryKey() != null) {
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
} else {
break;
}
}
}
ローカルセカンダリインデックスの使用
必要なすべての列がインデックステーブルに含まれている場合、インデックスから直接読み取ります。
private static void scanFromIndex(SyncClient client) {
RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria("INDEX_NAME");
PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MIN);
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
rangeRowQueryCriteria.setMaxVersions(1);
System.out.println("Scan result of the index table:");
while (true) {
GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
for (Row row : getRangeResponse.getRows()) {
System.out.println(row);
}
if (getRangeResponse.getNextStartPrimaryKey() != null) {
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
} else {
break;
}
}
}
必要な列がインデックステーブルに含まれていない場合、インデックスをスキャンして一致する各行のプライマリキーを取得し、その後データテーブルからそれらの列をフェッチします。
private static void scanFromIndex(SyncClient client) {
RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria("<INDEX_NAME>");
PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MIN);
startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MIN);
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.INF_MAX);
endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
rangeRowQueryCriteria.setMaxVersions(1);
while (true) {
GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
for (Row row : getRangeResponse.getRows()) {
// インデックス行からデータテーブルのプライマリキーを抽出します。
PrimaryKey curIndexPrimaryKey = row.getPrimaryKey();
PrimaryKeyColumn pk1 = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_1);
PrimaryKeyColumn pk2 = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
PrimaryKeyBuilder mainTablePKBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, pk1.getValue());
mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, pk2.getValue());
PrimaryKey mainTablePK = mainTablePKBuilder.build();
// データテーブルから必要な列をフェッチします。
SingleRowQueryCriteria criteria = new SingleRowQueryCriteria("TABLE_NAME", mainTablePK);
criteria.addColumnsToGet(DEFINED_COL_NAME3);
criteria.setMaxVersions(1);
GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
Row mainTableRow = getRowResponse.getRow();
System.out.println(row);
}
if (getRangeResponse.getNextStartPrimaryKey() != null) {
rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
} else {
break;
}
}
}
付録:インデックステーブルの削除
インデックステーブルが不要になった場合は、削除してください。
以下の例では、Tablestore SDK for Java を使用しています。その他のサポートされている SDK は次のとおりです:Go、Python、Node.js、.NET、および PHP。
private static void deleteIndex(SyncClient client) {
DeleteIndexRequest request = new DeleteIndexRequest("<TABLE_NAME>", "<INDEX_NAME>");
client.deleteIndex(request);
}
よくある質問
参照
Tablestore コンソールまたは Tablestore CLI でセカンダリインデックスを使用します。詳細については、「Tablestore コンソールでのセカンダリインデックスの使用」および「セカンダリインデックス」をご参照ください。
全文検索、ブール値クエリ、プレフィックスクエリ、あいまい検索など、より柔軟なクエリオプションを利用する場合は、多次元インデックス機能を使用します。詳細については、「概要」をご参照ください。