このトピックでは、アプリケーションでコードを使用してデータを効率的に抽出する方法について説明します。
概要
データ抽出とは、コードまたはデータエクスポートツールを使用して、一度にPolarDB-Xから完全なデータを読み取るプロセスです。 次のシナリオでデータを抽出できます。- すべてのデータは、データエクスポートツールを使用して下流システムにエクスポートされます。 PolarDB-Xは複数のデータエクスポートツールをサポートしています。
- PolarDB-Xでデータが処理された場合、または一度に複数のクエリ結果がユーザーに返された場合、外部ツールは使用できません。 この場合、コードを使用してPolarDB-Xの完全なデータを抽出する必要があります。
このトピックでは、PolarDB-Xのコードを使用して効率的にデータを抽出する方法について説明します。 一度に完全なデータを抽出したり、ページクエリを実行したりできます。
完全なデータを抽出する
ほとんどの場合、完全なデータを抽出するSQL文には、テーブルのシャードキーが含まれていません。 フルテーブルスキャンを実行してフルデータを抽出する場合、読み取りデータの量は、データの抽出に必要な時間と正の相関があります。 ヒントを使用して、SQL文を物理シャードに送信し、シャードからデータをプルできます。 これにより、ネットワークまたは接続リソースが節約されます。 次のJavaコードは、ヒントを使用して完全なデータを抽出する方法の例を示しています。 詳細については、「ヒントの使用方法」をご参照ください。
public static void extractData(Connection connection, String logicalTableName, Consumer<ResultSet> consumer)
SQLExceptionをスロー {
final String topology = "{0} からトポロジを表示する";
final String query = "/* + TDDL:NODE({0})*/select * from {1}";
try (final Statement statement = connection.createStatement()) {
最終マップ <String, List<String>> partitionTableMap = new LinkedHashMap<>();
// 指定された論理テーブルのパーティションidと物理テーブル名を取得する
try (final ResultSet rs = statement.exe cuteQuery(MessageFormat.format(topology, logicalTableName))) {
while (rs.next()) {
partitionTableMap.com puteIfAbsent(rs.getString(2), (k) -> new ArrayList<>()).add(rs.getString(3));
}
}
// 各パーティションからデータをシリアルに抽出する
for (Map.Entry<String, List<String>> entry : partitionTableMap.entrySet()) {
for (String tableName : entry.getValue()) {
try (final ResultSet rs=ステートメント
. executeQuery(MessageFormat.format(query, entry.getKey(), tableName)) {
// データを消費する
consumer.accept(rs);
}
}
}
}
}
ページ化されたクエリの実行
リスト内のデータをユーザーに表示する場合は、ページの読み込みを高速化するために、データをページごとに表示する必要があります。 これにより、過剰な冗長情報が表示されることを防ぐ。 このクエリメソッドは、ページクエリと呼ばれます。 リレーショナルデータベースは、セグメントごとにテーブルのデータを返すことはできません。 ページ化されたクエリを効率的に実行するには、データベースの特性に基づいてクエリ文を記述する必要があります。
次のコードブロックは、MySQLでページ化されたクエリの例を示します。 この例では、pagedクエリはlimit offsetとpageSizeを使用して実行されます。
select * from t_order where user_id = xxx order by gmt_create, id limit offset, pageSize
gmt_create列の値は重複している可能性があります。 order by句に含まれるid列は、結果が正しい順序でソートされるようにするために使用されます。
user_id列とgmt_create列にローカルセカンダリインデックス (LSI) を作成する場合、WHERE句にはuser_id列のみが指定されているため、一度にスキャンされるデータレコードの総数はoffsetパラメーターとpageSizeパラメーターで指定された値の合計になります。 offsetパラメーターに指定された値が大きくなると、スキャンされたレコードの数はテーブル内のレコードの総数に近くなります。 これにより、クエリに必要な時間が増加します。 分散データベースでは、データノードを追加してテーブルソートのスループットを上げることはできません。
解決策1ページのクエリ結果は、前のページが終了した時点から開始するように指定できます。 これにより、offsetパラメーターを指定する必要なく、テーブル全体のスキャンを防ぐことができます。 次のコードブロックは、ID列に基づくページ化クエリの例を示しています。
select * from t_order where id > lastMaxId order by id limit pageSize
この例では、クエリされた最初のページに条件が指定されていません。 2番目のページが照会されると、最初のページに対して照会されたid列の最大値が照会条件に指定されます。 このようにして、データベースはindex列のlastMaxIdパラメーターの値を識別し、クエリ結果を返します。 pageSizeパラメーターは、返すレコードの数を指定します。
MySQLでは、Row Constructor Expressionを使用して複数の列の値を比較できます (これはPolarDB-Xにも適用されます) 。
(c2,c3) > (1,1)
に相当します。c2 > 1 OR ((c2 = 1) AND (c3 > 1))
次の構文を使用して、ページクエリを実行できます。
select * from t_order
user_id = xxxおよび (gmt_create, id) > (lastMaxGmtCreate, lastMaxId)
order by user_id, gmt_create, id limit pageSize
最初のページが照会されると、条件は指定されません。 2番目のページが照会されると、最初のページで照会されるgmt_create列とid列の最大値が照会条件に指定されます。 同様のルールが他のページにも有効です。 行構築式を使用して、gmt_create列の重複値を処理できます。
次のコードブロックは、PolarDB-Xインスタンスでページ化クエリを実行するためのベストプラクティスを提供します。
-- lastMaxGmtCreateがnullではない
select * from t_order
user_id = xxx
と (
(gmt_create > lastMaxGmtCreate)
または ((gmt_create = lastMaxGmtCreate) および (id > lastMaxId))
)
order by user_id, gmt_create, id limit pageSize
-- lastMaxGmtCreateはnullです
select * from t_order
user_id = xxx
と (
(gmt_createはnullではありません)
または (gmt_createはnull、id > lastMaxId)
)
order by user_id, gmt_create, id limit pageSize