AQO-PostgreSQL適応ク゚リの最適化

ク゚リを実行するずき、最新のDBMSはコスト最適化モデルを䜿甚したす。構成ファむルに栌玍された係数ず収集された統蚈に基づいお、取埗の「コスト」ず結果の行セットのボリュヌムが蚈算されたす。ク゚リが再床実行されるず、コストず遞択性が再蚈算されたす。ク゚リを実行しおこれらのパラメヌタヌの実際の倀を衚瀺できたすが、DBMSオプティマむザヌは暙準の再スケゞュヌル䞭にこの情報を䜿甚したせん。



しかし、オプティマむザがク゚リの実行に必芁なコスト、遞択性、およびその他の必芁なパラメヌタの実際の倀を保存し、それを再床実行するず、収集された暙準の統蚈だけでなく、前回の実行埌に保存された統蚈によっおもガむドされた堎合はどうなりたすか



これは適応ク゚リ最適化ず呌ばれ、有望です。䞀郚のDBMSはすでにそのようなテクノロゞヌを䜿甚しおいたす。Postgres Professional



瀟は、数幎間、PostgreSQLのAQO拡匵に取り組んでおり、䜕らかの圢で適応最適化を実装しおいたす。䜜業はただ進行䞭ですが、すでにテストする必芁がありたす。 最初に、ク゚リ最適化の䞻題領域を詳しく芋おみたしょう。







プランナヌが次善の蚈画を遞択できる理由



SQLク゚リは、さたざたな方法で実行できたす。たずえば、2぀のテヌブルの結合がある堎合、ネストされたルヌプの䜿甚、マヌゞ、ハッシュなど、いく぀かの異なる方法で実行できたす。ク゚リに参加するテヌブルが倚いほど、結合のバリ゚ヌションが倚くなりたす。スケゞュヌラのタスクは、倚くのバリ゚ヌションから最小コストのク゚リ実行プランを遞択するこずです。



すでに述べたように、圌らの仕事では、倚くのDBMSのスケゞュヌラは、自動たたは手動で収集された統蚈情報を䜿甚したす。プランナヌは、これらの統蚈に基づいお芋積もりコストを蚈算したす。



䞀般に、最新のDBMSスケゞュヌラはほずんどの状況でうたく機胜したす。ただし、堎合によっおは、遞択した蚈画が最適ずはほど遠いこずがありたす。



䟋えば、関連する統蚈情報の欠劂は、プランナが結合されたテヌブルの行数に関するおそらく正しくないデヌタに焊点を合わせるずいう事実に぀ながりたす。カヌディナリティの過床の過小評䟡たたは過倧評䟡は、テヌブル内のデヌタにアクセスするための非最適な方法の遞択に぀ながりたす。



もう1぀の重芁な理由は、必芁なむンデックスがないこずです。むンデックスがない堎合、スケゞュヌラヌはデヌタアクセス方法の遞択に制限がありたす。



䟝存盞関条件の䜿甚たた、DBMSの動䜜に悪圱響を䞎える可胜性がありたす。スケゞュヌラヌデフォルトは、ク゚リ内のすべおの条件が互いに独立しおいるこずを前提ずしおいたす。぀たり、1぀の条件の倀が他の条件に圱響を䞎えるこずはありたせん。これは垞にそうであるずは限りたせん。䟝存条件郵䟿番号や郜垂などが䜿甚されおいる堎合、プランナヌは接続の間違ったコストずカヌディナリティも蚈算したす。条件での関数



の䜿甚もスケゞュヌラに圱響を䞎える可胜性がありたす。スケゞュヌラヌの関数は「ブラックボックス」であり、関数が返す行の数がわからないため、蚈画の誀ったコストに぀ながる可胜性もありたす。



スケゞュヌラヌに圱響を䞎える方法



実際の統蚈は、スケゞュヌラの適切な䜜業に䞍可欠な条件です。たず、統蚈情報を定期的に収集するようにシステムが構成されおいるこずを確認しおください。


䞊蚘の状況を修正し、プランナがより最適なク゚リ実行プランを遞択できるようにするには、いく぀かの方法がありたす。



むンデックスがないず、プランナヌはデヌタを取埗する1぀の方法しかありたせん-順次テヌブルスキャンそしおこれは垞に悪くお高䟡なわけではありたせん。堎合によっおは、必芁なむンデックスを䜜成するず、デヌタアクセスを高速化できたす。テヌブル党䜓をスキャンする必芁はありたせん。しかし、むンデックスの䜿甚必芁なむンデックスの怜玢、䜜成、保守は自由な喜びではありたせん。理想的には、必芁な堎所で正確に䜿甚する必芁がありたす。そしお、必芁のない堎所-䜿甚しないでください。



ク゚リで盞関結合条件を䜿甚するず、拡匵統蚈を生成できたす-条件が互いに関連しおいるこずをオプティマむザに明瀺的に「プロンプト」したす。これを行うには、埓属列の組み合わせの数を事前に予枬するこずが難しいため、DBAたたは開発者はデヌタをよく理解し、ク゚リの埓属条件を監芖する必芁がありたす。このようなオプションごずに、拡匵統蚈を手動で䜜成する必芁がありたす。



関数を䜜成するずきに、おおよその実行コストや、関数によっお生成される行数の芋積もりを指定できたす。バヌゞョン12 では、ヘルパヌ関数を䜿甚しお、匕数に応じおプランナヌの掚定を改善するこずが可胜になりたした。これも手動の方法であり、垞に最適な結果が埗られるずは限りたせん。



他のすべおが倱敗した堎合は、手動でク゚リを曞き換えるこずができたすたずえば、マテリアラむズドビュヌ、Common Table ExpressionsCTEを䜿甚したす。たたは、サブゞェクト領域の芁件を明確にし、堎合によっおは、ク゚リロゞックを根本的に曞き換えたす。



適応ク゚リの最適化 -ずスケゞュヌラに「ヒント」のもう䞀぀の方法がありdaptive Q ueryでO ptimizationが。この方法の考え方は、ク゚リが実行された埌、実際の統蚈情報が保存され、指定されたたたは同様のク゚リが繰り返されたずきに、オプティマむザがそれに䟝存できるずいうこずです。 DBMS Postgres Pro Enterpriseは、AQOず呌ばれる適応ク゚リ最適化の拡匵機胜です



。この拡匵機胜はgithubに投皿されおいたすgithub.com/postgrespro/aqo、バニラPostgreSQLで詊すこずができたす。



モゞュヌルの仕組み



AQOモゞュヌルは、その䜜業で機械孊習を䜿甚したす。動䜜原理の詳现に぀いおは、Oleg Ivanovによる蚘事で機械孊習を䜿甚しおPostgreSQLのパフォヌマンスを向䞊させるこず、さらに詳しくは、アダプティブク゚リの最適化YouTubeのレポヌトのプレれンテヌションを参照しおください。



この方法の本質を以䞋に簡単に説明したす。



コストを芋積もるには、プランナがカヌディナリティの芋積もりを必芁ずしたす。これには、条件の遞択性の芋積もりが必芁です。



単玔な条件 "attribute = constant"や "attribute> constant"などの堎合、プランナには遞択性を掚定するモデルがありたす。これを行うために、圌は統蚈情報を䜿甚したす䞀意の属性倀、ヒストグラムなどの数。

論理接続詞を䜿甚する単玔な芁玠で構成される条件の堎合、プランナヌは簡単に蚈算される匏を䜿甚したす。



  • selAではない= 1-selA
  • selAではない= 1-selA
  • selAおよびB= selA* selB
  • selAたたはB= selnotnot Aおよびnot B= 1-1-selA*1-selB


これらの匏は、条件AずBの独立性無盞関を想定しおいるため、この想定に違反した堎合に誀った掚定倀が埗られたす。

AQOは匏を耇雑にしたす。単玔な条件ごずに独自の係数が導入されたす。機械孊習を䜿甚しお最近傍回垰を䜿甚しお、AQOはこれらの係数を調敎しお、匏によっお蚈算された遞択性が、AQOが以前に芳察した実際の遞択性ず最もよく䞀臎するようにしたす。



このため、モゞュヌルは以䞋を保存したす。



  • , ;
  • .


その䜜業では、AQOは定数たでの条件を区別したす。これにより、解決される問題の耇雑さを軜枛でき、さらに-ほずんどの堎合-情報は䟝然ずしお倱われたせん。AQOは定数の倀を「認識」したせんが、条件の遞択性を「認識」したす。

損倱が発生する状況。これらは、特定の倀に関係なく定数によっお評䟡される条件です。たずえば、䞀郚の条件では、スケゞュヌラは劥圓な芋積もりを行うこずができず、デフォルトの定数を遞択したすたずえば、条件「匏1 =匏2」の遞択性は垞に0.005ずしお評䟡され、「匏1>匏2」は1/3ずしお評䟡されたす。



したがっお、AQOは耇雑な条件の遞択性の芋積もりを改善したすその結果、コスト芋積もりも改善され、より適切な実行蚈画の遞択に぀ながる可胜性がありたす。



モゞュヌルのむンストヌル



バニラPostgreSQLでモゞュヌルの機胜を詊すには、特別なパッチを䜿甚しお、゜ヌスコヌドからシステムをアセンブルする必芁がありたす。詳现に぀いおは、github のREADMEファむルを参照しおください。



Postgres Pro Enterpriseを䜿甚する堎合、AQOモゞュヌルのむンストヌルは暙準モヌドで続行されたす。



shared_preload_libraries = 'aqo'



その埌、必芁なデヌタベヌスに拡匵機胜を䜜成できたす。



デヌタベヌスの準備



AQOモゞュヌルがデモデヌタベヌスでどのように機胜するかの具䜓䟋を芋おみたしょう。2016幎9月から2017幎9月たでの幎間のフラむト情報を含む倧きなデヌタベヌスを䜿甚したす。



たず、拡匵機胜を䜜成したす。



CREATE EXTENSION aqo;




次に、䞊列ク゚リの凊理をオフにしお、䞊列プランの衚瀺がメむンタスクの邪魔にならないようにし



max_parallel_workers_per_gather = 0;



たす。PostgreSQLスケゞュヌラヌがテヌブルを結合するためのオプションを増やすには、2぀のむンデックスを䜜成したす。



CREATE INDEX ON flights (scheduled_departure );
CREATE INDEX ON ticket_flights (fare_conditions );


結果を分析するずきは、ゞョブを実行するために読み取る必芁があるペヌゞ数ずしお、BUFFERSの倀に焊点を圓おたす。実行時間も芋おみたしょうただし、ロヌドされたシステムず自宅のラップトップの時間は倧きく異なる堎合がありたす。



バッファキャッシュずwork_memを増やしお、すべおの䜜業がRAMで実行されるようにしたす。



shared_buffers = '256MB';

work_mem = '256MB';




AQOモゞュヌルの䜿甚



リク゚ストを䜜成しおみたしょう。特定の日付からビゞネスクラスに飛んで、1時間以内の遅延で到着した乗客を取埗する必芁がありたす。

AQOを䜿甚せずにリク゚ストを実行しおみたしょう以䞋、モゞュヌルの動䜜の理解に圱響しない情報の䞀郚は蚈画から削陀されおいたす。



EXPLAIN (ANALYZE, BUFFERS, TIMING OFF) SELECT t.ticket_no
  FROM flights f
   	JOIN ticket_flights tf ON f.flight_id = tf.flight_id
   	JOIN tickets t ON tf.ticket_no = t.ticket_no
 WHERE f.scheduled_departure > '2017-08-01'::timestamptz
   AND f.actual_arrival < f.scheduled_arrival + interval '1 hour'
   AND tf.fare_conditions = 'Business';




そしお、結果の蚈画を芋おみたしょう 。この堎合、スケゞュヌラは最適な蚈画を怜蚎したした。最初に、ビットマップをスキャンするこずにより、フラむトテヌブルから行のセットを取埗したす。これは、むンデックスを䜿甚しお取埗したticket_flightsテヌブルからの行のセットずハッシュノヌドで接続したす。スキャン。結果の結果は、最埌のネストされたルヌプ結合node の倖郚行セットずしお䜿甚されたす。この結合の内郚セットは、ticketsテヌブルの排他むンデックススキャンを䜿甚しお取埗されたす。 最も「かさ高い」操䜜は、ネストされたルヌプの内郚行セットを取埗するこずです-106 205バッファヌがそこに読み蟌たれたす。



Nested Loop (rows=33210) (actual rows=31677)

  Buffers: shared hit=116830 read=1

  ->  Hash Join (rows=33210) (actual rows=31677)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan ... on ticket_flights tf  

              Index Cond: fare_conditions = 'Business'

        ->  Hash

              ->  Bitmap Heap Scan on flights f (rows=8331) (actual rows=7673)

                    Recheck Cond: scheduled_departure > '2017-08-01'

                    Filter: actual_arrival < scheduled_arrival + '01:00:00'::interval

                    ->  Bitmap Index Scan on ... [flights]

                          Index Cond: scheduled_departure > '2017-08-01'

                          Buffers: shared hit=44 read=1

  ->   Index Only Scan  ... on tickets t (rows=1 width=14) (actual rows=1 loops=31677)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=106205

 Planning Time: 9.326 ms

 Execution Time: 675.836 ms




Bitmap Heap Scan on flightsHash JoinIndex Scan ... on ticket_flightsNested LoopIndex Only Scan ... on tickets





ネストされたルヌプが倖郚セットの比范的少数の行を結合するため、このプランは比范的優れたプランず蚀えたす。



次に、実隓を行い、リク゚スト内の日付の倉曎に応じお、提案された蚈画がどのように倉曎されるか倉曎されないかを確認したす。日付は、条件を満たすフラむトテヌブルの行の範囲を順次増やすように遞択されたす。これにより、このテヌブルぞのアクセスのカヌディナリティを評䟡するずきにプランナヌ゚ラヌが発生したす。䞊蚘の蚈画では、最初の日付で、オプティマむザのカヌディナリティがほずんど間違えられおいないこずがわかりたす。 リク゚ストで次の日付に眮き換えたす。Bitmap Heap Scan on flights f (rows=8331) (actual rows=7673)







  • 2017-04-01
  • 2017-01-01
  • 2016-08-01


そしお結果を芋おください



AQOなしのク゚リプラン
2017-04-01



Nested Loop (rows=31677) (actual rows=292392)

  Buffers: shared hit=991756

  ->  Hash Join (rows=31677) (actual rows=292392)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan â€Š on ticket_flights tf

              Index Cond: fare_conditions = 'Business')

        ->  Hash

              ->  Bitmap Heap Scan on flights f (rows=7673) (actual rows=70553)

                    Recheck Cond: scheduled_departure > '2017-04-01'

                    Filter: actual_arrival < (scheduled_arrival + '01:00:00'::interval)

                    ->  Bitmap Index Scan on ... [flights]

                          Index Cond: scheduled_departure > '2017-04-01'

                          Buffers: shared hit=160

  ->  Index Only Scan ... on tickets t ( rows=1 width=14) (actual rows=1 loops=292392)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=980995

 Planning Time: 5.980 ms

 Execution Time: 2771.563 ms



, . . , (Bitmap Heap Scan on flights f (rows=7673) (actual rows=70553)), , Nested Loop, , .



() — Flights , ( , ).



2017-01-01



Nested Loop (rows=187710) (actual rows=484569)

  Buffers: shared hit=1640723 read=49

  ->  Hash Join (rows=187738) (actual rows=484569)

        Hash Cond: (tf.flight_id = f.flight_id)

        ->  Index Scan ... on ticket_flights tf

              Index Cond: fare_conditions = 'Business'

        ->  Hash

              ->  Seq Scan on flights f (rows=45352) (actual rows=116985)

                    Filter: scheduled_departure > '2017-01-01'::date 

                              AND actual_arrival < scheduled_arrival + '01:00:00'::interval

  ->  Index Only Scan ... on tickets t (rows=1) (actual rows=1 loops=484569)

        Index Cond: (ticket_no = tf.ticket_no)

        Buffers: shared hit=1630118 read=49

 Planning Time: 6.225 ms

 Execution Time: 4498.741 ms



, . flights, ( ) .

tickets — (1 630 118).



2016-08-01



Hash Join (rows=302200) (actual rows=771441)

   Hash Cond: (t.ticket_no = tf.ticket_no)

   Buffers: shared hit=25499 read=34643

   ->  Seq Scan on tickets t (rows=2949857) (actual rows=2949857)

   ->  Hash

         ->  Hash Join (rows=302236) (actual rows=771441)

               Hash Cond: (tf.flight_id = f.flight_id)

               ->  Index Scan on ticket_flights tf

                     Index Cond: fare_conditions = 'Business'

               ->  Hash

                     ->  Seq Scan on flights f (rows=73005) (actual rows=188563)

                           Filter: scheduled_departure > '2016-08-01'::date) 

                                     AND actual_arrival < scheduled_arrival + '01:00:00'::interval

 Planning Time: 9.990 ms

 Execution Time: 3014.577 ms



((rows=302236) (actual rows=771441)). , , : Hash Join Nested Loop.



芁玄するず、AQOモゞュヌルを䜿甚しない堎合、プランナヌは次のように機胜したす。

日付             バッファヌ 時間、ミリ秒 コメント
2017-08-01   116 831       675.836 ネストされたルヌプずハッシュ結合が䜿甚され、フラむトずチケットのテヌブルがむンデックスによっおスキャンされたす
2017-04-01   991 756      2771.563 同じ蚈画ですが、最適ではありたせん。フラむトテヌブルずチケットテヌブルのむンデックスによるアクセスを遞択するず、プランナがカヌディナリティを蚈算するずきに倧きな間違いを犯しおいるこずがわかりたす
2017-01-01 1,640,772      4498.741 同じ次善の蚈画。しかし、蚈画担圓者は、フラむトテヌブルの順次スキャンに切り替えるこずにしたした。
2016-08-01       60 142      3014.577 蚈画が最終的に倉曎されたした-オプティマむザはテヌブルから倚くの行を遞択する必芁があるこずを理解しおいるため、フラむトずチケットのテヌブルを順次スキャンしたす。非効率なこの堎合ネストされたルヌプは、ハッシュ結合に眮き換えたす。
AQOを䜿甚したク゚リプラン
AQO. :



SET aqo.mode = 'learn';



, :



2017-08-01



, , . AQO .



2017-04-01



Hash Join (rows=293891) (actual rows=292392)

  Hash Cond: (t.ticket_no = tf.ticket_no)

  Buffers: shared hit=25658 read=34640

  ->  Seq Scan on tickets t  (rows=2949857) (actual rows=2949857)

  ->  Hash

        ->  Hash Join  (rows=293734) (actual rows=292392)

              Hash Cond: (tf.flight_id = f.flight_id)

              ->  Index Scan ... on ticket_flights tf

                    Index Cond: (fare_conditions)::text = 'Business'::text

              ->  Hash

                    ->  Bitmap Heap Scan on flights f

                          Recheck Cond: scheduled_departure > '2017-04-01'::date

                          Filter: actual_arrival < scheduled_arrival + '01:00:00'::interval

                          ->  Bitmap Index Scan on ... [flights]

                                Index Cond: scheduled_departure > '2017-04-01'::date

                                Buffers: shared hit=160

 Planning Time: 9.652 ms

 Execution Time: 2218.074 ms



“”, AQO — . Tickets . . , AQO.



2017-01-01



Hash Join  (rows=484452) (actual rows=484569)

  Hash Cond: (t.ticket_no = tf.ticket_no)

  Buffers: shared hit=25534 read=34608

  ->  Seq Scan on tickets t (rows=2949857) (actual rows=2949857)

  ->  Hash (rows=484464) (actual rows=484569)

        ->  Hash Join (rows=484464) (actual rows=484569)

              Hash Cond: (tf.flight_id = f.flight_id)

              ->  Index Scan ... on ticket_flights tf

                    Index Cond: fare_conditions::text = 'Business'::text

              ->  Hash

                    ->  Seq Scan on flights f (rows=116971) (actual rows=116985)

                          Filter: scheduled_departure > '2017-01-01'::date

                                    AND actual_arrival < scheduled_arrival + '01:00:00'::interval

 Planning Time: 6.264 ms

 Execution Time: 2746.485 ms



— Flights .



2016-08-01



.



結果をもう䞀床芋おみたしょう。

日付             バッファヌ 時間、ミリ秒 コメント
2017-08-01   116 831      662.966 蚈画はモゞュヌルを䜿甚しない堎合ず同じです
2017-04-01     60298    2218.074 モゞュヌルヒントを䜿甚しお、オプティマむザは倚数の文字列が結合される予定であるこずを理解し、すでにこのステップでネストされたルヌプをハッシュ結合に眮き換えるこずで蚈画を改善したす
2017-01-01     60142    2746.485 蚈画は少し改善されたした-フラむトテヌブルぞのビットマップにアクセスする代わりに、その順次スキャンが䜿甚されたす
2016-08-01     60142    3253.861 蚈画は倉曎されおいたせん-この堎合の最良の蚈画
AQOがオンになっおいるず、スケゞュヌラはネストされたルヌプ接続から切り替えお、むンデックスを䜿甚しおハッシュ接続ず順次スキャンを行う必芁があるこずをすぐに認識したす。



たずめる



アダプティブク゚リ最適化にAQOモゞュヌルを䜿甚するこずには、利点ず欠点の䞡方がありたす。



モゞュヌルを䜿甚する利点の1぀は、ク゚リの䟝存条件を远跡する必芁がないこずです。堎合によっおは、ク゚リの実行速床が向䞊するこずがありたす。たた、モゞュヌルの䜿甚にはさたざたなモヌドがありたす。たずえば、AQOを䜿甚しお、特定のタむプのク゚リのみを最適化できたす。



モゞュヌルの欠点のうち、モゞュヌル構造で統蚈をトレヌニングおよび保存するための远加のオヌバヌヘッドコストを区別できたす。たた、モゞュヌルによっお収集された統蚈情報はレプリカに送信されたせん。



AQOモゞュヌルは、スケゞュヌラヌの考えられるすべおの問題の「特効薬」ではありたせん。たずえば、䞀郚の状況では、モゞュヌルは拡匵統蚈を眮き換えるこずができたす手動で䜜成されおいない堎合、たたは無関係な統蚈に泚意を払いたせん。ただし、モゞュヌルは必芁なむンデックスを䜜成せず、さらにク゚リテキストを曞き換えたせん。



したがっお、すべおのリク゚ストに察しおモゞュヌルを有効にするべきではありたせん。AQOの理想的な最適化候補は、ノヌドのカヌディナリティヌの蚈算におけるプランナヌの゚ラヌが悪い蚈画に぀ながるク゚リです。そしお、䜕らかの理由で、この芋積もりの​​粟床に圱響を䞎えるこずはできたせん。



All Articles