強制されたPostgreSQLクエリの最適化

最も最適な方法でデータベースにアクセスしないクローズドソースアプリケーションがある場合はどうしますか? アプリケーションやデータベース自体を変更せずにクエリを調整する方法は?



そのような質問をしていない場合、あなたは非常に成功した厳格なDBAです。



さて、尋ねられたら、苦しみと経験を共有させてください。



さらにデータを保存するか、タスクを設定する必要がある



問題の履歴が面白くない場合は、このセクションを安全にスクロールできます。



当初、独自のシステムを使用して、そのデータを閉じた形式からPostgreSQLデータベースに解析し、そこからこのデータを読み取り、分析、処理しました。



さらに、このシステムのツールも特定の操作にこのベースを使用したため、このベースを破棄し、その構造を持つコピーを作成することは無駄なアイデアのように思えました。



デフォルトでは、システムは1週間以上経過したレコードを自動的に削除したため、スタンドでのパフォーマンスの問題はありませんでした。



ただし、サーバーディスクに十分なスペースがある限り、データをより長く保存する必要があります。 まあ、このデータへのアクセスを失わず、古いデータであってもシステムの組み込みツールを使用することをお勧めします。



したがって、明らかな決定は、INSERT操作でパーティション化とトリガーを行うことでした。 焦点は非常にシンプルで効果的です。 データは必要なパーティションに挿入され、古いレコードの削除は無効になっています。すべて問題ないようです。



数年が経過し、データが十分に蓄積されなくなるまで。



ここで、使用されたシステムのツールキットによって行われたリクエストは、日付による選択を制限しない(または、パーティションが行われたフィールドに制限しない)ことが「突然」判明しました。 つまり 何かを探している場合-検索はすべてのパーティションで行われます。 UPDATE操作もスローダウンし始めました-状況ではID-shnikのみが使用されていました。



その結果、要求は長時間実行され、他のすべての要求が引き下げられ、負荷が急速に増大します。



もちろん、最初に頭に浮かぶのは、開発者に連絡することです。



ただし、ほとんどの場合、アクセスゾーンにないか、複数の行で完了するために別のシステムのコストを要求します。



したがって、私たちを助けることができる何らかの種類のプロキシがおそらく既にあるという考えが浮上しました。



プロキシが必要です



クイックグーグルでは、PostgreSQLまたは一部のサードパーティソフトウェアの側で受信クエリを書き換える方法の質問に対する明確な答えを見つけられませんでした。



そのため(もちろん、楽しみのためだけに)、クライアントからの接続を受け入れ、PostgreSQLでそれらをプロキシするかなり単純なソフトウェアが作成されました。 同時に、着信SQLクエリが読み取られ、必要に応じて置き換えられます。



githubへのリンクを共有する



バイナリパッケージは作成しませんでしたが、手が届きませんでした。 しかし、アセンブリは非常に簡単です。 すべてがC ++ / Qtで記述されているため、 私はこれについて長い間書いています...



構成は非常に簡単です。



リッスンするインターフェイスとポートを示します。



listen_address=0.0.0.0 listen_port=5433
      
      





PostgreSQLサーバーに直接接続する代わりに、怠慢なソフトウェアに指定されたアドレスへの接続を強制します。



接続の転送先を書き留めます(この例では、プロキシはPostgreSQLサーバーと同じマシンにあります)。



 dst_address=127.0.0.1 dst_port=5432
      
      





正規表現を設定して、目的のリクエストをキャッチします。



 query = SELECT \* FROM tablename WHERE (.+)
      
      





書き換える必要があると言います。



 action = rewrite
      
      





書き換え方法を説明します。



 rewrite = SELECT * FROM tablename WHERE (col3 >= '$(now-1M)') AND $(1)
      
      





この例では、日付を含む列によってクエリ条件にフィルターを追加し、先月のレコードのみに関心があることを示します。



次のように書くことができます:



 rewrite = SELECT * FROM tablename WHERE (col3 >= now() - interval '1 month') AND $(1)
      
      





ただし、now()関数が存在するため、リクエストは最適ではありません-すべてのパーティションで検索が実行されます。 必要なものだけを検索するには、定数値を指定する必要があります。 したがって、プロキシはタイムスタンプを$(now-1M)構造の代わりに1か月のシフトに置き換えます。



結果(ログから):



 ORIGINAL query: SELECT * FROM tablename WHERE id=1; MODIFIED query (rule 1): SELECT * FROM tablename WHERE (col3 >= '2018-11-12 11:25:23.0+00') AND id=1;
      
      





したがって、原則として、リクエストを置き換えることができます。 サーバーからの応答は変更されず、そのままクライアントに送信されます。 このようにして、伝送遅延が最小限に抑えられます。 また、アプリケーションは通常、特定の形式の応答を待機するため、要求と応答の列のセットを変更することは望ましくありません。



ログに関連するすべてのクエリを簡単に表示することもできます。



 query = .+ action = log
      
      





リポジトリには、例と詳細な説明を含む設定があります。



ちなみに、開発者がデータベースを操作するためにどの程度適切に書き込むかは簡単に判断できます。 たとえば、このような頻繁に実行されるリクエストが表示された場合は、誰かがマニュアルを吸うときです。



 INSERT INTO tablename (col1, col2, col3) VALUES('value1', 1, '2018-12-31')
      
      





次のようになります。



 INSERT INTO tablename (col1, col2, col3) VALUES($1::varchar, $2::integer, $3::date)
      
      





残念ながら、これまでのところ、プロキシは次のように記述できません:/しかし、これは難しくありません。 おそらく将来、最初の要求を2番目の要求に書き換えることが可能になるでしょう。



はい。重要な点は、SSLがまだサポートされていないため、クライアントからプロキシへのすべての接続が暗号化されないことです。



コメントやコメントをお待ちしております。



ユーザーの積極的な関心があれば、おそらく私はさらにプロジェクトを開発します。



他のデータベースで作業を追加できます。



All Articles