グローバルセカンダリインデックスは、Table Store の新しい機能です。 テーブルを作成すると、プライマリインデックスはすべてのプライマリキーで構成されます。 Table Store はプライマリキーを使用して、一意にテーブル内の各行を識別します。 ただし、より多くのシナリオでは、属性、プライマリキーまたは最初の列からではないプライマリキーでテーブルをクエリする必要があります。 インデックスが不十分な場合、テーブル全体をスキャンしてフィルター条件を設定することによってのみ結果を取得できます。 大量のデータを含むテーブルをクエリした後に結果がほとんど得られない場合は、クエリによってリソースが過剰に消費される可能性があります。

Table Store のグローバルセカンダリインデックス機能は、 DynamoDB GSIおよび HBase Phoenixの機能と似ています。 指定した 1 つ以上の属性を使ってインデックスを作成できます。 さらに、作成したインデックス内のデータを指定した属性で並べ替えることができます。 ベース テーブルに書き込むすべてのデータは、ベース テーブルに作成されたインデックスと非同期的に同期されます。 ベース テーブルにデータを書き込むだけでよく、このベース テーブルに作成されたインデックスをクエリすることができます。 この設定により、ほとんどのシナリオでクエリのパフォーマンスが大幅に向上します。 たとえば、一般的な通話履歴クエリのベース テーブルを次のように作成できます。
CellNumber StartTime (Unix タイムスタンプ) CalledNumber Duration BaseStationNumber
123456 1532574644 654321 60 1
234567 1532574714 765432 10 1
234567 1532574734 123456 20 3
345678 1532574795 123456 5 2
345678 1532574861 123456 100 2
456789 1532584054 345678 200 3
  • CellNumber および StartTime はプライマリキーで、それぞれ 通話番号 および 通話開始時刻を表しています。
  • CalledNumberDuration および BaseStationNumber は定義済み属性で、着信番号通話時間および基地局番号を表しています。

通話を終了すると、通話情報がこのテーブルに書き込まれます。 それぞれ CalledNumber および BaseStationNumber にグローバルセカンダリインデックスを作成して、さまざまなクエリ要件を満たすことができます。 インデックスの作成方法の詳細については、 「付録」の例をご参照ください。

次のクエリ要件があるとします。

  • CellNumber の値が 234567 と一致する行を取得します。

    Table Store のプライマリキーでデータを並べ替えることができます。 さらに、 getRange メソッドを呼び出して、データを順次スキャンすることもできます。 getRange メソッドを呼び出す場合は、 234567 を PK0 (CellNumber) の最小値および最大値として指定する必要があります。 一方、 0 をPK1 (StartTime) の最小値として指定し 、 INT_MAX を PK1の最大値として指定する必要があります。 その後、ベース テーブルをクエリできます。

    private static void getRangeFromMainTable(SyncClient client, long cellNumber)
    {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(TABLE_NAME);
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(0));
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
        rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
    
        rangeRowQueryCriteria.setMaxVersions(1);
    
        String strNum = String.format("%d", cellNumber);
        System.out.println("A cell number " + strNum + "makes the following calls:");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            for (Row row : getRangeResponse.getRows()) {
                System.out.println(row);
            }
    
            // nextStartPrimaryKey の値が null 値でない場合は、ベース テーブルから引き続きデータを読み取ることができます。
            if (getRangeResponse.getNextStartPrimaryKey() ! = null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }
  • CalledNumberの値が 123456 である行を取得する場合。

    Table Store はすべての行をプライマリキーでソートします。 CalledNumber は事前定義された属性であるため、この属性によってテーブルを直接照会することはできません。 そのため、 CalledNumber に作成されたインデックスをクエリできます。

    IndexOnBeCalledNumber:

    PK0 PK1 PK2
    CalledNumber CellNumber StartTime
    123456 234567 1532574734
    123456 345678 1532574795
    123456 345678 1532574861
    654321 123456 1532574644
    765432 234567 1532574714
    345678 456789 1532584054
    Table Store はインデックスのプライマリキーを自動補完します。 このインデックスを作成するとき、Table Store はベーステーブルのすべてのプライマリキーをこのベーステーブルに作成されたインデックスに追加します。 したがって、インデックスには 3 つのプライマリキーが含まれています。

    IndexOnBeCalledNumber は CalledNumber に作成されたインデックスなので、このインデックスを直接クエリして結果を取得できます。

    private static void getRangeFromIndexTable(SyncClient client, long cellNumber) {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX0_NAME);
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_1, PrimaryKeyValue.fromLong(cellNumber));
        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);
    
        String strNum = String.format("%d", cellNumber);
        System.out.println("A cell number" + strNum + "was called by the following numbers");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            for (Row row : getRangeResponse.getRows()) {
                System.out.println(row);
            }
    
            // nextStartPrimaryKey の値が null 値でない場合は、ベース テーブルから引き続きデータを読み取ることができます。
            if (getRangeResponse.getNextStartPrimaryKey() ! = null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }
  • BaseStationNumber の値が 002 に、また、StartTime の値が 1532574740 に一致する行を取得する場合。

    このクエリでは、 BaseStationNumber および StartTime の両方を条件として指定しています。 したがって、 BaseStationNumber および StartTime に複合インデックスを作成できます。

    IndexOnBaseStation1:

    PK0 PK1 PK2
    BaseStationNumber StartTime CellNumber
    001 1532574644 123456
    001 1532574714 234567
    002 1532574795 345678
    002 1532574861 345678
    003 1532574734 234567
    003 1532584054 456789

    IndexOnBaseStation1 インデックスをクエリできます。

    private static void getRangeFromIndexTable(SyncClient client,
                                               long baseStationNumber,
                                               long startTime) {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX1_NAME);
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(startTime));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.INF_MAX);
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
        rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
    
        rangeRowQueryCriteria.setMaxVersions(1);
    
        String strBaseStationNum = String.format("%d", baseStationNumber);
        String strStartTime = String.format("%d", startTime);
        System.out.println("All called numbers forwarded by the base station" + strBaseStationNum + " that start from" + strStartTime + "are listed as follows:");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            for (Row row : getRangeResponse.getRows()) {
                System.out.println(row);
            }
    
            // nextStartPrimaryKey 値が null 値でない場合は、ベース テーブルから引き続きデータを読み取ることができます。
            if (getRangeResponse.getNextStartPrimaryKey() ! = null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }
  • BaseStationNumber の値 003 1532574861 から 1532584054の範囲内の StartTime 値と一致する行を取り出す場合。 期間だけが行に表示されます。

    このクエリでは、 BaseStationNumber および StartTime の両方を条件として指定します。 Duration のみ結果セットにが表示されます。 最後のインデックスに対してクエリを発行してから、ベーステーブルをクエリして Duration を取得することができます。

    private static void getRowFromIndexAndMainTable(SyncClient client,
                                                    long baseStationNumber,
                                                    long startTime,
                                                    long endTime,
                                                    String colName) {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX1_NAME);
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(startTime));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(endTime));
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
        rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
    
        rangeRowQueryCriteria.setMaxVersions(1);
    
        String strBaseStationNum = String.format("%d", baseStationNumber);
        String strStartTime = String.format("%d", startTime);
        String strEndTime = String.format("%d", endTime);
    
        System.out.println("The list of calls forwarded by the base station" + strBaseStationNum + "from" + strStartTime + "to" + strEndTime + "is listed as follows:");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            For (Row row: fig. getrows ()){
                PrimaryKey curIndexPrimaryKey = row.getPrimaryKey();
                PrimaryKeyColumn mainCalledNumber = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_1);
                PrimaryKeyColumn callStartTime = curIndexPrimaryKey.getPrimaryKeyColumn(PRIMARY_KEY_NAME_2);
                PrimaryKeyBuilder mainTablePKBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
                mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, mainCalledNumber.getValue());
                mainTablePKBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, callStartTime.getValue());
                PrimaryKey mainTablePK = mainTablePKBuilder.build(); // You can specify primary keys for the base table.
    
                // ベース テーブルをクエリすることができます。
                SingleRowQueryCriteria criteria = new SingleRowQueryCriteria(TABLE_NAME, mainTablePK);
                criteria.addColumnsToGet(colName); // You can read the Duration attribute value of the base table.
                // 最新のデータバージョンが読み込まれることを示すために 1 を指定できます。
                criteria.setMaxVersions(1);
                GetRowResponse getRowResponse = client.getRow(new GetRowRequest(criteria));
                Row mainTableRow = getRowResponse.getRow();
    
                System.out.println(mainTableRow);
            }
    
            // nextStartPrimaryKey 値が null 値でない場合は、ベース テーブルから引き続きデータを読み取ることができます。
            if (getRangeResponse.getNextStartPrimaryKey() ! = null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }

    クエリのパフォーマンスを向上させるために、 BaseStationNumber および StartTimeに複合インデックスを作成できます。 Duration をこのインデックスの属性として指定できます。

    以下のインデックスが作成されます。

    IndexOnBaseStation2:

    PK0 PK1 PK2 Defined0
    BaseStationNumber StartTime CellNumber Duration
    001 1532574644 123456 600
    001 1532574714 234567 10
    002 1532574795 345678 5
    002 1532574861 345678 100
    003 1532574734 234567 20
    003 1532584054 456789 200

    IndexOnBaseStation2 インデックスをクエリできます:

    private static void getRangeFromIndexTable(SyncClient client,
                                               long baseStationNumber,
                                               long startTime,
                                               long endTime,
                                               String colName) {
        RangeRowQueryCriteria rangeRowQueryCriteria = new RangeRowQueryCriteria(INDEX2_NAME);
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder startPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        startPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(startTime));
        startPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MIN);
        rangeRowQueryCriteria.setInclusiveStartPrimaryKey(startPrimaryKeyBuilder.build());
    
        // プライマリキーを指定できます。
        PrimaryKeyBuilder endPrimaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
        endPrimaryKeyBuilder.addPrimaryKeyColumn(DEFINED_COL_NAME_3, PrimaryKeyValue.fromLong(baseStationNumber));
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_2, PrimaryKeyValue.fromLong(endTime));
        endPrimaryKeyBuilder.addPrimaryKeyColumn(PRIMARY_KEY_NAME_1, PrimaryKeyValue.INF_MAX);
        rangeRowQueryCriteria.setExclusiveEndPrimaryKey(endPrimaryKeyBuilder.build());
    
        // 読み込む属性名を指定できます。
        rangeRowQueryCriteria.addColumnsToGet(colName);
    
        rangeRowQueryCriteria.setMaxVersions(1);
    
        String strBaseStationNum = String.format("%d", baseStationNumber);
        String strStartTime = String.format("%d", startTime);
        String strEndTime = String.format("%d", endTime);
    
        System.out.println("The duration of calls forwarded by the base station" + strBaseStationNum + "from" + strStartTime + "to" + strEndTime + "is listed as follows:");
        while (true) {
            GetRangeResponse getRangeResponse = client.getRange(new GetRangeRequest(rangeRowQueryCriteria));
            for (Row row : getRangeResponse.getRows()) {
                System.out.println(row);
            }
    
            // nextStartPrimaryKey 値が null 値でない場合は、ベース テーブルから引き続きデータを読み取ることができます。
            if (getRangeResponse.getNextStartPrimaryKey() ! = null) {
                rangeRowQueryCriteria.setInclusiveStartPrimaryKey(getRangeResponse.getNextStartPrimaryKey());
            } else {
                break;
            }
        }
    }
    ​```

    したがって、 Duration をインデックス属性として指定しないと、ベーステーブルをクエリして Duration を取得する必要があります。 しかし、 Duration をインデックス属性として指定した場合、この属性データはベース テーブルとインデックスに保管されます。 この設定では、ディスクスペースの消費を犠牲にしてクエリのパフォーマンスが向上します。

  • 結果セットから、合計通話時間、平均通話時間、最大通話時間および最小通話時間の値を取得する場合。この結果セットは、StartTime の値が 1532574861 から 1532584054 の間にある 003 という BaseStationNumber の値です。

    最後のクエリと比較して、各通話時間に対してリターンは必要ありません。 ただし、期間統計には リターンが必要です。 最後のクエリと同じ方法で結果を取得できます。 その後、Duration 計算をして必要な結果を得ることができます。 さらに、SQL-on-OTS で SQL ステートメントを実行して統計を取得することもできます。 SQL-on-OTS を有効にする方法の詳細については、「Table StoreのOLAP: Data Lake Analytics でのサーバーレス SQL ビッグデータ分析」をご参照ください。 SQL-on-OTS では、ほとんどの MySQL 構文を使用できます。 さらに、SQL-on-OTS を使用すると、ビジネスに適した複雑な計算を簡単に処理できます。