この記事では、prepare の生成、サーバー側での実行プロセス、jdbc の prepare および関連テストの処理など、MySQL prepare に関連する内容を主に紹介します。必要な方はさらに学習してください。お役に立てれば幸いです。
Prepare の利点
Prepare SQL が生成される理由。 mysql サーバー上で SQL を実行するプロセスから始めましょう。SQL 実行プロセスには、字句解析 -> 構文解析 -> 意味解析 -> 実行計画の最適化 -> 実行の段階が含まれます。字句解析 -> 文法解析 これら 2 つの段階はハード解析と呼ばれます。字句解析ではSQL中の各単語を識別し、構文解析ではSQL文がSQL文法に準拠しているかどうかを解析して構文木(Lex)を取得します。パラメータが異なるが他のパラメータが同じ SQL の場合、実行時間は異なりますが、ハード解析時間は同じです。同じ SQL のクエリ データが変更されると、複数のクエリの実行時間は異なる場合がありますが、ハード解析の時間は変わりません。 SQL の実行時間が短いほど、合計実行時間に対する SQL ハード解析時間の比率が高くなります。 Taobao 上のほとんどのトランザクション SQL アプリケーションでは、クエリはインデックスを経由するため、実行時間は比較的短くなります。したがって、淘宝網は大部分を占めるために db SQL ハード解析を使用します。
Prepare の登場は、ハード解析の問題を最適化することです。サーバー側でのPrepareの実行プロセスは以下の通りです
1) Prepareはクライアントから「?」付きのSQLを受け取り、ハードパースを行って構文ツリー(stmt->Lex)を取得し、スレッドが配置されているpreparestatementキャッシュ。このキャッシュは HASH MAP であり、キーは stmt->id などの情報がクライアントに返されます。
2) 実行はクライアントのstmt->idやパラメータなどの情報を受け取ります。クライアントはここで SQL を送信する必要がないことに注意してください。サーバーは、stmt->id に基づいてハード解析された stmt の preparestatement キャッシュを検索し、パラメータを設定すると、後続の最適化と実行を続行できます。
準備を行うと、実行段階での難しい解析時間を節約できます。 SQL が 1 回だけ実行され、準備モードで実行される場合、SQL の実行にはサーバーとの 2 つの対話 (準備と実行) が必要ですが、通常 (非準備) モードでは必要な対話は 1 つだけです。この方法で準備を使用すると、追加のネットワーク オーバーヘッドが発生するため、メリットに見合わない可能性があります。たとえば、同じ SQL が複数回実行される状況を見てみましょう。たとえば、準備モードで 10 回実行された場合、必要なハード解析は 1 回だけです。この時点で、追加のネットワーク オーバーヘッドは無視できる程度になります。したがって、prepare は頻繁に実行される SQL に適しています。
Prepare のもう 1 つの機能は SQL インジェクションを防ぐことですが、これはクライアント側の jdbc でエスケープすることで実現され、サーバーとは何の関係もありません。
ハードパースの割合
ストレステスト中のperfで得られた結果によると、ハードパースに関連する関数の割合が比較的高く(MYSQLparse 4.93%、lex_one_token 1.79%、lex_start 1.12%)、合計で8近くになっています。 %。したがって、サーバー上で prepare を使用すると、パフォーマンスがさらに向上する可能性があります。
jdbc と準備
jdbc サーバー側パラメーター:
useServerPrepStmts: サーバー準備スイッチを使用するかどうか
jdbc クライアント側パラメーター:
cachePrepStmts:デフォルトは false。prepareStatement オブジェクトをキャッシュするかどうか。各接続にはキャッシュがあり、これは SQL によって一意に識別される LRU キャッシュです。同じ接続の下では、異なる stmt が prepareStatement オブジェクトを再作成する必要はありません。
prepStmtCacheSize: LRU キャッシュ内の prepareStatement オブジェクトの数。通常は、最もよく使用される SQL の番号に設定されます。
prepStmtCacheSqlLimit: prepareStatement オブジェクトのサイズ。サイズを超えるとキャッシュされません。
Jdbc の prepare 処理:
useServerPrepStmts=true Jdbc の prepare 処理
1) PreparedStatement オブジェクトを作成し、COM_PREPARE コマンドをサーバーに送信し、サーバーは疑問符付きの SQL を送信します。 st mt->ID等の情報
2) COM_EXECUTEコマンドをサーバーに送信し、パラメータ情報を送信します。
useServerPrepStmts=false の場合の Jdbc の prepare 処理
1) PreparedStatement オブジェクトを作成します。この時点ではサーバーと対話しません。
2) パラメーターと PreparedStatement オブジェクトに基づいて完全な SQL を結合し、サーバーに QUERY コマンドを送信します
パラメーターcachePrepStmts がオンになっているときと、useServerPrepStmts が true または false のときの PreparedStatement を見てみましょう。オブジェクトがキャッシュされます。ただし、useServerPrepStmts が true の場合、キャッシュされた PreparedStatement オブジェクトにはサーバーの stmt->id およびその他の情報が含まれます。つまり、PreparedStatement オブジェクトを再利用すると、サーバーとの通信 (COM_PREPARE コマンド) のオーバーヘッドがなくなります。また、 useServerPrepStmts=false は、PreparedStatement オブジェクトをキャッシュするためにcachePrepStmts をオンにすることは、単純な SQL 解析情報にすぎないため、現時点でcachePrepStmts をオンにすることはあまり意味がありません。
Java コードの一部を見てみましょう
Connection con = null; PreparedStatement ps = null; String sql = "select * from user where id=?"; ps = con.prepareStatement(sql); ps.setInt(1, 1); ps.executeQuery(); ps.close(); ps = con.prepareStatement(sql); ps.setInt(1, 3); ps.executeQuery(); ps.close();
このコードは、同じセッション内で同じステートメントを 2 回準備して実行し、間に ps.close();
UseServerPrepStmts=false があるため、サーバーは同じ SQL を 2 回ハード解析します。
useServerPrepStmts=true およびcachePrepStmts=false の場合でも、サーバーは同じ SQL を 2 回ハード解析します。
UseServerPrepStmts=true、cachePrepStmts=true の場合、サーバーは SQL を 1 回だけハード解析します。
2 つの prepare の間に ps.close(); がない場合、cachePrepStmts=true とcachePrepStmts=false は 1 つのハード解析のみを必要とします
したがって、クライアントは同じ SQL に対して PreparedStatement オブジェクトを頻繁に割り当て、解放します。 cachePrepStmts パラメータを有効にする必要があります。
テスト
1) 主に準備の効果と useServerPrepStmts パラメーターの影響をテストする簡単なテストを作成しました
非準備時間と準備時間の比率
| useServerPrepStmts=false1.01 |
結論: | |
useServerPrepStmts= false の場合、prepare のパフォーマンスは非 prepa のパフォーマンスと同等になります。再。 |
注: このテストは、単一の接続と単一の SQL という理想的な条件下で実施されました。オンラインでは複数の接続と複数の SQL が存在し、SQL の実行頻度、SQL の複雑さなどにより、改善効果は異なります。準備の内容は特定の状況によって異なります。
以下は準備なしです
cnt = 5000; // no prepare String sql = "select biz_order_id,out_order_id,seller_nick,buyer_nick,seller_id,buyer_id,auction_id,auction_title,auction_price,buy_amount,biz_type,sub_biz_type,fail_reason,pay_status,logistics_status,out_trade_status,snap_path,gmt_create,status,ifnull(buyer_rate_status, 4) buyer_rate_status from tc_biz_order_0030 where " + "parent_id = 594314511722841 or parent_id =547667559932641;"; begin = new Date(); System.out.println("begin:" + df.format(begin)); stmt = con.createStatement(); for (int i = 0; i < cnt; i++) { stmt.executeQuery(sql); } end = new Date(); System.out.println("end:" + df.format(end)); long temp = end.getTime() - begin.getTime(); System.out.println("no perpare interval:" + temp); // test prepare sql = "select biz_order_id,out_order_id,seller_nick,buyer_nick,seller_id,buyer_id,auction_id,auction_title,auction_price,buy_amount,biz_type,sub_biz_type,fail_reason,pay_status,logistics_status,out_trade_status,snap_path,gmt_create,status,ifnull(buyer_rate_status, 4) buyer_rate_status from tc_biz_order_0030 where " + "parent_id = 594314511722841 or parent_id =?;"; ps = con.prepareStatement(sql); BigInteger param = new BigInteger("547667559932641"); begin = new Date(); System.out.println("begin:" + df.format(begin)); for (int i = 0; i < cnt; i++) { ps.setObject(1, param); ps.executeQuery(); } end = new Date(); System.out.println("end:" + df.format(end)); temp = end.getTime() - begin.getTime(); System.out.println("prepare interval:" + temp);
6.46% mysqld mysqld [.] _Z10MYSQLparsePv 3.74% mysqld libc-2.12.so [.] __memcpy_ssse3 2.50% mysqld mysqld [.] my_hash_sort_utf8 2.15% mysqld mysqld [.] cmp_dtuple_rec_with_match 2.05% mysqld mysqld [.] _ZL13lex_one_tokenPvS_ 1.46% mysqld mysqld [.] buf_page_get_gen 1.34% mysqld mysqld [.] page_cur_search_with_match 1.31% mysqld mysqld [.] _ZL14build_templateP19row_prebuilt_structP3THDP5TABLEj 1.24% mysqld mysqld [.] rec_init_offsets 1.11% mysqld libjemalloc.so.1 [.] free 1.09% mysqld mysqld [.] rec_get_offsets_func 1.01% mysqld libjemalloc.so.1 [.] malloc 0.96% mysqld libc-2.12.so [.] __strlen_sse42 0.93% mysqld mysqld [.] _ZN4JOIN8optimizeEv 0.91% mysqld mysqld [.] _ZL15get_hash_symbolPKcjb 0.88% mysqld mysqld [.] row_search_for_mysql 0.86% mysqld [kernel.kallsyms] [k] tcp_recvmsg
考察
1 前述したように、各接続にはキャッシュがあり、多くのサブテーブルと大規模な接続の場合、これは一意の識別子として SQL を持つ LRU キャッシュです。アプリケーションサーバーがメモリの問題を引き起こす可能性があります。ここでの前提は、ibatis がデフォルトで prepare を使用するということです。 mybatisでは、ラベルstatementTypeは、特定のSQLがprepare.
statementTypeのいずれかを使用するかどうかを指定できます。これにより、MyBatisはそれぞれStatement、PreparedStatement、またはCallableStatementを使用します。デフォルト: PREPARED.
これは正確です。頻度の高い SQL に対してのみ prepare の使用を制御することで、準備された SQL の数を制御し、メモリ消費を削減します。残念ながら、現在、ほとんどのグループが ibatis バージョン 2.0 を使用しているようですが、このバージョンでは、statementType タグがサポートされていません。
概要
以上がMySQLのprepare原則の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。