前回の記事では「8.5 インデックス収集・予測・異常検知」の関連コンテンツをご紹介しましたが、今回は「8.6 AIクエリ時間予測」の関連コンテンツをご紹介します。
8.6 AIクエリ時間予測
先ほど紹介した「Slow SQL Discovery」機能の典型的なシナリオは、新サービスの開始前の検査であり、入力ソースは事前に収集されたSQLパイプラインデータです。低速 SQL 検出機能は、主に複数の SQL ステートメントのバッチ検査で使用されます。これには、SQL ステートメントが以前に実行されていることが必要です。そのため、得られる結果は主に定性的なものであり、シナリオによってはユーザーの要件を満たすことが難しい場合があります。評価精度。
したがって、上記のシナリオの欠点を補い、ユーザーのより正確な SQL 時間予測要件を満たし、AI オプティマイザーへの道を開くために、この章で説明する機能が実現されます。
実際のビジネス シナリオは複雑な性質を持っているため、データベースの既存の静的コスト見積もりモデルは統計結果が不正確であることが多く、不適切な実行計画を持ついくつかのパスが選択されます。したがって、上記の複雑なシナリオでは、データベースのコスト推定モデルには自己更新機能が必要です。この機能の主な機能は、クエリ ステートメントの履歴データに基づいて、現在実行されている SQL ステートメントのクエリ時間とカーディナリティを推定することです。
8.6.1 使用シナリオ
AIクエリ分析の前提は、実行計画を取得することです。まず、複雑なクエリの実際のクエリ プラン (プラン構造、演算子の種類、関連するデータ ソース、フィルター条件など)、各演算子ノードの実際の実行時間、オプティマイザによって推定されたコストを収集する必要があります。 、ユーザーのニーズに応じてクエリの実行中に返される実際の行数、オプティマイザによって推定される行数、SMP 同時スレッドの数、およびその他の情報。データテーブルに記録し、定期的なデータ無効化のクリーニングなど永続的な管理を行います。
この機能は主に行数推定とクエリ予測の2つに分かれており、前者は後者の良否を予測するための前提条件となります。現在、openGauss はオンライン学習に基づいて実行計画の各層の結果セット サイズを推定しますが、これは表示のみを目的としており、実行計画の生成には影響しません。フォローアップは、オプティマイザが結果セットをより正確に推定して、より適切な実行計画を取得するのに役立ちます。
現段階では、この需要に対して予測のためのシステム機能を提供し、実際の比較・検証のために説明を追加する予定です。
8.6.2 既存のテクノロジー
現在、AI4DBの分野では、機械学習に基づいて行数を推定し、クエリ遅延を予測する試みが数多く行われています。
1. 伝統的な手法
データベース最適化の専門家である Guy Lohman 氏がブログ「クエリの最適化は「解決された」問題ですか」で述べているように、従来のデータベース クエリのパフォーマンス予測の「アキレス腱」は、中間結果セットのサイズの推定です。行数の推定において、統計情報に基づいて行数を推定する従来の方法は、主に 3 種類の仮定に基づいています。
(1) データに依存しない分布の仮定。
(2) 一様分布の仮定。
(3) 主キーと外部キーの仮定。
ただし、実際のシナリオでは、データには特定の相関性や傾向があることが多く、現時点では上記の前提が崩れる可能性があり、従来のデータベース オプティマイザーでは複数テーブル結合の中間結果セットのサイズを見積もる際に数桁の誤差が生じる可能性があります。 。
2000 年以降、データ相関によって引き起こされる推定問題を解決するために、サンプリングベースの推定、サンプリングベースのカーネル密度関数推定、および複数列ヒストグラムに代表される統計手法が提案されてきました。しかし、これらの方法にはすべて共通の問題があり、モデルを段階的に保守することができず、これらの追加の統計情報を収集するとデータベース保守のオーバーヘッドが膨大になり、精度が大幅に向上しましたが、まだ広く採用されていません。大手データベースメーカー。
従来のパフォーマンス予測方法は主にコスト モデルに依存しており、次の側面で明らかな欠点があります。
(1) 精度: 基礎となるハードウェア アーキテクチャと最適化テクノロジが継続的に進化しているため、実際のパフォーマンス予測モデルの複雑さは線形モデルでモデル化されるものとは程遠いものになっています。
(2) スケーラビリティ: コストモデルの開発コストは比較的高く、ユーザーの特定のシナリオを包括的に最適化することはできません。
(3) 校正可能性:コストモデルの柔軟性は、各リソース次元の線形加算に使用される係数と一部のペナルティコストに限定されており、柔軟性が低く、実際の使用においてユーザが校正するのは困難である。
(4) 適時性: コスト モデルは統計情報の収集と使用に依存していますが、現時点では増分メンテナンス方法がないため、データ モビリティが高いシナリオでは統計情報が長期にわたって失われる可能性があります。
2. 機械学習の手法
モデルの複雑さ、調整可能性、および増分保守性の側面における機械学習モデルの利点により、従来のオプティマイザー コスト モデルの欠点を補うことができます。機械学習ベースのクエリ パフォーマンス予測は、データベースの学術界や業界で徐々に主流の研究になりつつあります。方向。
上記のセクション 8.3 の遅い SQL の検出セクションで紹介した関連手法に加えて、清華大学の学習コスト推定モデルは、マルチタスク学習と文字条件の単語埋め込み手法に基づいており、予測精度をさらに向上させています。
これまでのところ、機械学習手法は実験結果から高い精度を達成していますが、実際のビジネス シナリオにおける継続的なデータ分布の変化により、モデルのオンライン学習能力に対する要件が提示されています。openGaussはデータ駆動型のオンライン学習モデルを採用し、カーネルを通じて過去のジョブパフォーマンス情報を継続的に収集し、AIエンジン側でR-LSTM(再帰的長期短期記憶、再帰的長期短期記憶ネットワーク)モデルを使用して判断します。オペレーターレベルのクエリ遅延、および予測のための中間結果セットのサイズ。
8.6.3 実装原則
一般に、クエリ パフォーマンスの予測は、図 8-15 に示すように、データベース カーネル側と AI エンジン側の 2 つの部分で構成されます。
(1) データベース カーネル側は、基本的なデータベース機能を提供することに加えて、履歴データを収集して永続化し、curl を通じて AI エンジン側に HTTPS リクエストを送信する必要もあります。
(2) AI Engine はモデルのトレーニング、実行予測、モデル管理などのインターフェースを提供し、Flask フレームワークに基づくサーバーは HTTPS リクエストを受け入れます。プロセスは図 8-16 に示されています。
データ収集に関連するパラメーター (実際のビジネス負荷に応じて、パフォーマンスに約 5% 影響する可能性があります) を有効にすると、モデルのトレーニングのために履歴パフォーマンス データがデータベースのシステム テーブルに永続的に収集されます。
モデルをトレーニングする前に、ユーザーはモデルのパラメーターを設定する必要があります (詳細については、「8.6.5 使用例」を参照)。ユーザートレーニングコマンドが発行された後、カーネルプロセスはAIエンジン側に設定リクエストを送信し、機械学習モデルを初期化します。図 8-17 に、構成プロセスのシーケンスを示します。
モデルの構成が成功すると、図 8-18 に示すように、カーネル プロセスは AI エンジン側にトレーニング リクエストを送信してトレーニングをトリガーします。
このうち、Хt は現在のタイミング モジュールの入力、ht-₁ は現在のタイミング モジュールの入力、つまり前のタイミングの出力情報です。シグモイド (σ) 関数を使用して Οt の部分を取得します。現在のセル状態での出力; Ct はすべての履歴タイミングを表します 保持された情報は Tanh 関数で処理され、現在の状態の出力情報 Οt と乗算されて、この状態の出力 ht と 1 次元ベクトルの予測結果が得られます [起動時間、合計時間、カーディナリティ] の 3 つの要素が実際のデータと比較され、比率誤差を使用してモデルの損失関数が計算されます。
8.6.4 主要なソースコード分析
1. プロジェクトの構造
AI Engine 側に関係する主なファイル パスは openGauss-server/src/gausskernel/dbmind/tools/predictor であり、そのファイル構造は表 8-13 に示されています。
ファイル構造 |
説明する |
インストール |
デプロイに必要なファイルパス |
インストール/ca_ext.txt |
証明書プロファイル |
インストール/requirements-gpu.txt |
GPU (グラフィックス プロセッシング ユニット、グラフィックス プロセッサ) を使用して依存ライブラリのリストをトレーニングする |
インストール/requirements.txt |
CPUトレーニングに使用される依存ライブラリのリスト |
インストール/ssl.sh |
証明書生成スクリプト |
パイソン |
プロジェクトのコードパス |
Python/certs.py |
暗号化通信 |
Python/e_log |
syslog パス |
Python/ログ |
モデルトレーニングログのパス |
Python/log.conf |
設定ファイル |
Python/モデル.py |
機械学習モデル |
Python/run.py |
サーバーのメイン関数 |
Python/saved_models |
モデルトレーニングのチェックポイント |
python/settings.py |
プロジェクト設定ファイル |
Python/アップロード |
Curl 転送ファイルの保存パス |
主にカーネル側に関係するファイルパスはopenGauss-server/src/gausskernel/optimizer/util/learnであり、そのファイル構造を表8-14に示します。
ファイル構造 |
説明する |
通信.cpp |
通信層コードの実装 |
エンコーディング.cpp |
データエンコーディング |
ml_model.cpp |
汎用モデル呼び出しインターフェイス |
plan_tree_model.cpp |
ツリーモデル呼び出しインターフェース |
2. トレーニングプロセス
カーネル側のモデル トレーニング インターフェイスは、ModelTrainInternal 関数を通じて実装されます。この関数の主要な部分は次のとおりです。
static void ModelTrainInternal(const char* templateName, const char* modelName, ModelAccuracy** mAcc)
{
…
/* 对于树形模型调用对应的训练接口 */
char* trainResultJson = TreeModelTrain(modelinfo, labels);
/* 解析返回结果 */
…
ModelTrainInfo* info = GetModelTrainInfo(jsonObj);
cJSON_Delete(jsonObj);
/* 更新模型信息 */
Relation modelRel = heap_open(OptModelRelationId, RowExclusiveLock);
…
UpdateTrainRes(values, datumsMax, datumsAcc, nLabel, mAcc, info, labels);
HeapTuple modelTuple = SearchSysCache1(OPTMODEL, CStringGetDatum(modelName));
…
HeapTuple newTuple = heap_modify_tuple(modelTuple, RelationGetDescr(modelRel), values, nulls, replaces);
simple_heap_update(modelRel, &newTuple->t_self, newTuple);
CatalogUpdateIndexes(modelRel, newTuple);
…
}
カーネル側のツリー モデル トレーニング インターフェイスは TreeModelTrain 関数を通じて実装され、コア コードは次のとおりです。
char* TreeModelTrain(Form_gs_opt_model modelinfo, char* labels)
{
char* filename = (char*)palloc0(sizeof(char) * MAX_LEN_TEXT);
char* buf = NULL;
/* configure阶段 */
ConfigureModel(modelinfo, labels, &filename);
/* 将编码好的数据写入临时文件 */
SaveDataToFile(filename);
/* Train阶段 */
buf = TrainModel(modelinfo, filename);
return buf;
}
AI Engine 側で設定される Web サービスの URI は /configure で、トレーニング フェーズの URI は /train であり、次のコード スニペットはトレーニング プロセスを示しています。
def fit(self, filename):
keras.backend.clear_session()
set_session(self.session)
with self.graph.as_default():
# 根据模型入参和出参维度变化情况,判断是否需要初始化模型
feature, label, need_init = self.parse(filename)
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
epsilon = self.model_info.make_epsilon()
if need_init: # 冷启动训练
epoch_start = 0
self.model = self._build_model(epsilon)
else: # 增量训练
epoch_start = int(self.model_info.last_epoch)
ratio_error = ratio_error_loss_wrapper(epsilon)
ratio_acc_2 = ratio_error_acc_wrapper(epsilon, 2)
self.model = load_model(self.model_info.model_path,
custom_objects={'ratio_error': ratio_error, 'ratio_acc': ratio_acc_2})
self.model_info.last_epoch = int(self.model_info.max_epoch) + epoch_start
self.model_info.dump_dict()
log_path = os.path.join(settings.PATH_LOG, self.model_info.model_name + '_log.json')
if not os.path.exists(log_path):
os.mknod(log_path, mode=0o600)
# 训练日志记录回调函数
json_logging_callback = LossHistory(log_path, self.model_info.model_name, self.model_info.last_epoch)
# 数据分割
X_train, X_val, y_train, y_val = \
train_test_split(feature, label, test_size=0.1)
# 模型训练
self.model.fit(X_train, y_train, epochs=self.model_info.last_epoch,
batch_size=int(self.model_info.batch_size), validation_data=(X_val, y_val),
verbose=0, initial_epoch=epoch_start, callbacks=[json_logging_callback])
# 记录模型checkpoint
self.model.save(self.model_info.model_path)
val_pred = self.model.predict(X_val)
val_re = get_ratio_errors_general(val_pred, y_val, epsilon)
self.model_logger.debug(val_re)
del self.model
return val_re
3. 予測プロセス
カーネル側のモデル予測プロセスは、主に ModelPredictInternal 関数を通じて実装されます。ツリー モデルの予測プロセスは、TreeModelPredict 関数を通じて実装されます。カーネル側のツリー モデル予測プロセスでは、AI エンジンと通信するためにいくつかのシグナリングが必要になります。通信プロセスは次のとおりです。
char* TreeModelPredict(const char* modelName, char* filepath, const char* ip, int port)
{
…
if (!TryConnectRemoteServer(conninfo, &buf)) {
DestroyConnInfo(conninfo);
ParseResBuf(buf, filepath, "AI engine connection failed.");
return buf;
}
switch (buf[0]) {
case '0': {
ereport(NOTICE, (errmodule(MOD_OPT_AI), errmsg("Model setup successfully.")));
break;
}
case 'M': {
ParseResBuf(buf, filepath, "Internal error: missing compulsory key.");
break;
}
…
}
/* Predict阶段 */
…
if (!TryConnectRemoteServer(conninfo, &buf)) {
ParseResBuf(buf, filepath, "AI engine connection failed.");
return buf;
}
switch (buf[0]) {
case 'M': {
ParseResBuf(buf, filepath, "Internal error: fail to load the file to predict.");
break;
}
case 'S': {
ParseResBuf(buf, filepath, "Internal error: session is not loaded, model setup required.");
break;
}
default: {
break;
}
}
return buf;
}
AI Engine 側の Setup プロセスの Web インターフェイスは /model_setup、予測フェーズの Web インターフェイスは /predict で、プロトコルは両方とも Post です。
4. データのエンコード
データのエンコードは次の 2 つの次元に分かれています。
(1) 演算子次元: 表 8-15 に示すように、各実行計画演算子の属性が含まれます。
属性名 |
意味 |
コーディング戦略 |
オプション名 |
演算子の種類 |
ワンホット |
オリエンテーション |
タプルの保存形式を返す |
ワンホット |
ストラテジー |
論理属性 |
ワンホット |
オプション |
物理的特性 |
ワンホット |
どれ |
述語 |
ハッシュ |
投影 |
投影された列を返す |
ハッシュ |
(2) 計画次元。
各オペレーターについて、その固有の属性に加えて、openGauss はクエリ ID、プラン ノード ID、および親ノード ID も記録します。トレーニング/予測フェーズでは、これらの情報を使用してオペレーター情報をツリー プラン構造に再構築します。サブプラン ツリーはデータ拡張のために再帰的に構築できるため、モデルの汎化能力が向上します。ツリー データ構造を図 8-21 に示します。
5. モデル構造
AI エンジンのモデル分析、トレーニング、予測については、8.6.4 章を参照してください。次のコードはモデルの構造を示しています。
class RnnModel():
def _build_model(self, epsilon):
model = Sequential()
model.add(LSTM(units=int(self.model_info.hidden_units), return_sequences=True, input_shape=(None, int(self.model_info.feature_length))))
model.add(LSTM(units=int(self.model_info.hidden_units), return_sequences=False))
model.add(Dense(units=int(self.model_info.hidden_units), activation='relu'))
model.add(Dense(units=int(self.model_info.hidden_units), activation='relu'))
model.add(Dense(units=int(self.model_info.label_length), activation='sigmoid'))
optimizer = keras.optimizers.Adadelta(lr=float(self.model_info.learning_rate), rho=0.95)
ratio_error = ratio_error_loss_wrapper(epsilon)
ratio_acc_2 = ratio_error_acc_wrapper(epsilon, 2)
model.compile(loss=ratio_error, metrics=[ratio_acc_2], optimizer=optimizer)
return model
AI エンジンの損失関数は比率誤差を使用します (一部の文献では qerror が使用されています)。MRE や MSE と比較して、この損失関数の利点は、過大評価と過小評価を同等に罰できることです。式は次のとおりです: ε はパフォーマンスとして宣言されます
。分母が0にならないように予測値の微小な値を設定します。
8.6.5 使用例
AIクエリ時間予測機能の利用例は以下のとおりです。
① パフォーマンス予測モデルを定義します。コードは次のとおりです。
INSERT INTO gs_opt_model VALUES(‘rlstm’, ‘model_name’, ‘host_ip’, ‘port’);
② GUC パラメータを通じてデータ収集を開始し、パラメータ リストを構成します。コードは次のとおりです。
enable_resource_track = on;
enable_resource_record = on;
③ トレーニングデータをエンコードします。コードは次のとおりです。
SELECT gather_encoding_info('db_name');
④ 校正モデルのコードは次のとおりです。
SELECT model_train_opt('template_name', 'model_name');
⑤ トレーニングステータスを監視します。コードは次のとおりです。
SELECT track_train_process('host_ip', 'port');
⑥ Explain + SQL ステートメントにより SQL クエリのパフォーマンスを予測します。コードは次のとおりです。
EXPLAIN (..., predictor 'model_name') SELECT ...
結果を取得します。「p-time」列はラベル予測です。
Row Adapter (cost=110481.35..110481.35 rows=100 p-time=99..182 width=100) (actual time=375.158..375.160 rows=2 loops=1)
8.6.6 進化ルート
現在のモデルの汎化能力は外部 AI エンジン コンポーネントに依存しているため不十分であり、深層学習ネットワークは比較的重いため、展開が困難になります。モデルにはトレーニング用のデータが必要であり、コールド スタート段階での接続は必要ありません。以下の面からのフォローアップが進化します。
(1) 異なる複雑さのモデルを追加し、マルチモデル融合分析をサポートして、より堅牢なモデル予測結果と信頼性を提供します。
(2) AI エンジンはタスク キューへの参加を検討していますが、現時点では単一の同時予測/トレーニングのみをサポートしており、複数のサーバーによる同時ビジネスを検討できます。
(3) オンライン学習・転移学習の強化を踏まえ、災害忘れの問題を回避するために損失関数にアンカーペナルティコストを追加することを検討するとともに、データ管理モードの最適化、データスコアの仕組みの検討、データの適時性に従って重みを割り当てます。
(4) この機能をオプティマイザと統合して、AI ベースのパス選択方法を検討します。
第8章 AI技術「8.6 AIクエリ時間予測」の素晴らしい内容を学んでいただきありがとうございました、次回は「8.7 DeepSQL」の関連内容をご紹介します。
乞うご期待。