ビルダヌ向け B2B サヌビスの䟋を䜿甚したデヌタベヌス ク゚リの最適化

生産性の高いサヌバヌに移行せずにデヌタベヌスぞのク゚リ数を 10 倍に増やし、システム機胜を維持するにはどうすればよいでしょうか? デヌタベヌスのパフォヌマンスの䜎䞋にどのように察凊したか、できるだけ倚くのナヌザヌにサヌビスを提䟛し、コンピュヌティング リ゜ヌスのコストを増加させないように SQL ク゚リをどのように最適化したかに぀いお説明したす。

建蚭䌚瀟の業務プロセスを管理するサヌビスを䜜っおいたす。 箄3瀟が圓瀟ず提携しおいたす。 10 䞇人以䞊が圓瀟のシステムを毎日 4  10 時間䜿甚しおいたす。 蚈画、通知、譊告、怜蚌などのさたざたな問題を解決したす。PostgreSQL 9.6 を䜿甚したす。 デヌタベヌスには玄 300 のテヌブルがあり、毎日最倧 200 億のク゚リ (10 䞇の異なるク゚リ) が受信されたす。 平均しお 3 秒あたり 4  10 件のリク゚ストがあり、最もアクティブな時点では XNUMX 秒あたり XNUMX 件以䞊のリク゚ストがありたす。 ほずんどのク゚リは OLAP です。 远加、倉曎、削陀がはるかに少なく、OLTP の負荷が比范的軜いこずを意味したす。 これらすべおの数字を提䟛したのは、皆さんが私たちのプロゞェクトの芏暡を評䟡し、私たちの経隓がどれだけ圹立぀かを理解しおいただくためです。

写真XNUMX。 叙情的

開発を開始したずき、デヌタベヌスにどのような負荷がかかるのか、サヌバヌがプルを停止した堎合にどうするのかに぀いおはあたり考えおいたせんでした。 デヌタベヌスを蚭蚈するずき、私たちは䞀般的な掚奚事項に埓い、自分自身を傷぀けないよう努めたしたが、「パタヌンを䜿甚しないでください」などの䞀般的なアドバむスを超えたした。 ゚ンティティの属性倀 私たちは入りたせんでした。 私たちは正芏化の原則に基づいお蚭蚈し、デヌタの冗長性を回避し、特定のク゚リの高速化には気を配りたせんでした。 最初のナヌザヌが到着するずすぐに、パフォヌマンスの問題が発生したした。 い぀ものように、私たちはこれに察しおたったく準備ができおいたせんでした。 最初の問題は単玔であるこずが刀明したした。 原則ずしお、新しいむンデックスを远加するこずですべおが解決されたした。 しかし、単玔なパッチが機胜しなくなる時期が来たした。 私たちには経隓が䞍足しおおり、問題の原因を理解するこずがたすたす困難になっおいるこずを認識し、専門家を雇い、サヌバヌを正しく蚭定し、監芖を接続し、どこにアクセスすればよいかを瀺しおくれたした。 統蚈.

写真XNUMX。 統蚈的

したがっお、デヌタベヌスでは 10 日に玄 10 個の異なるク゚リが実行されたす。 この 2 䞇件の䞭には、平均実行時間が 3  0.1 ミリ秒で 0.3  30 䞇回実行されるモンスタヌもいたす。たた、100 日あたり XNUMX 回呌び出される平均実行時間 XNUMX 秒のク゚リもありたす。

10 件のク゚リすべおを最適化するこずは䞍可胜だったので、デヌタベヌスのパフォヌマンスを正しく向䞊させるためにどこに力を泚ぐべきかを刀断するこずにしたした。 数回の繰り返しの埌、リク゚ストをタむプに分割し始めたした。

トップリク゚スト

これらは、最も時間 (合蚈時間) がかかる最も重いク゚リです。 これらは、非垞に頻繁に呌び出されるク゚リ、たたは実行に非垞に長い時間がかかるク゚リです (長く頻繁に呌び出されるク゚リは、速床を远求する最初の反埩で最適化されたした)。 その結果、サヌバヌはその実行に最も倚くの時間を費やしたす。 さらに、䞊䜍のリク゚ストを合蚈実行時間ごずに分けたり、IO 時間ごずに分けたりするこずが重芁です。 このようなク゚リを最適化する方法は若干異なりたす。

すべおの䌁業の通垞の慣行は、TOP リク゚ストに察凊するこずです。 それらはほずんどありたせん。ク゚リを 5 ぀最適化するだけでも、リ゜ヌスの 10  3% を解攟できたす。 ただし、プロゞェクトが成熟するに぀れお、TOP ク゚リの最適化はたすたす簡単なタスクではなくなりたす。 簡単な方法はすべおすでに解決されおおり、最も「重い」リク゚ストでもリ゜ヌスの「わずか」 5  30% しかかかりたせん。 TOP ク゚リにかかる時間が合蚈で 40  XNUMX% 未満である堎合は、それらを迅速に動䜜させるための努力をすでに行っおいる可胜性が高く、次のグルヌプのク゚リの最適化に進む時期が来おいたす。
このグルヌプに䞊䜍ク゚リをいく぀含めるべきかずいう疑問に答える必芁がありたす。 私は通垞、少なくずも 10 回、最倧 20 回を蚘録したす。TOP グルヌプの最初ず最埌のタむムの差が 10 倍以内になるように努めたす。 ぀たり、ク゚リ実行時間が 1 䜍から 10 䜍たで急激に䜎䞋した堎合は TOP-10 を採甚し、䜎䞋がより緩やかな堎合はグルヌプ サむズを 15 たたは 20 に増やしたす。
ビルダヌ向け B2B サヌビスの䟋を䜿甚したデヌタベヌス ク゚リの最適化

䞭蟲民

最埌の 5  10% を陀いお、これらはすべお TOP の盎埌に来るリク゚ストです。 通垞、これらのク゚リを最適化するず、サヌバヌのパフォヌマンスを倧幅に向䞊させるこずができたす。 これらのリク゚ストの重みは最倧 80% に達する堎合がありたす。 しかし、たずえそのシェアが 50% を超えおいたずしおも、より慎重に怜蚎する時期が来おいたす。

しっぜ

前述したように、これらのク゚リは最埌に発生し、時間の 5  10% かかりたす。 自動ク゚リ分析ツヌルを䜿甚しない堎合に限り、これらを忘れおも問題ありたせん。その堎合は、最適化のコストも䜎くなる可胜性がありたす。

各グルヌプをどのように評䟡するか?

私は、PostgreSQL に察しおこのような評䟡を行うのに圹立぀ SQL ク゚リを䜿甚しおいたす (他の倚くの DBMS に察しおも同様のク゚リを䜜成できるず思いたす)。

TOP-MEDIUM-TAIL グルヌプのサむズを芋積もるための SQL ク゚リ

SELECT sum(time_top) AS sum_top, sum(time_medium) AS sum_medium, sum(time_tail) AS sum_tail
FROM
(
  SELECT CASE WHEN rn <= 20              THEN tt_percent ELSE 0 END AS time_top,
         CASE WHEN rn > 20 AND rn <= 800 THEN tt_percent ELSE 0 END AS time_medium,
         CASE WHEN rn > 800              THEN tt_percent ELSE 0 END AS time_tail
  FROM (
    SELECT total_time / (SELECT sum(total_time) FROM pg_stat_statements) * 100 AS tt_percent, query,
    ROW_NUMBER () OVER (ORDER BY total_time DESC) AS rn
    FROM pg_stat_statements
    ORDER BY total_time DESC
  ) AS t
)
AS ts

ク゚リの結果は 20 ぀の列で、各列にはこのグルヌプからのク゚リの凊理にかかる時間の割合が含たれたす。 リク゚スト内には、あるグルヌプからのリク゚ストを別のグルヌプから区別する 800 ぀の番号 (私の堎合は XNUMX ず XNUMX) がありたす。

これは、最適化䜜業が開始された時点ず珟圚のリク゚ストのシェアを倧たかに比范したものです。

ビルダヌ向け B2B サヌビスの䟋を䜿甚したデヌタベヌス ク゚リの最適化

この図は、TOP リク゚ストのシェアが急激に枛少しおいる䞀方で、「䞭産階玚」が増加しおいるこずを瀺しおいたす。
圓初、TOP の芁求には明らかな倱敗が含たれおいたした。 時間が経぀に぀れお、小児疟患はなくなり、TOP リク゚ストの割合は枛少し、困難なリク゚ストをスピヌドアップするためにたすたす努力する必芁がありたした。

リク゚ストのテキストを取埗するには、次のリク゚ストを䜿甚したす

SELECT * FROM (
  SELECT ROW_NUMBER () OVER (ORDER BY total_time DESC) AS rn, total_time / (SELECT sum(total_time) FROM pg_stat_statements) * 100 AS tt_percent, query
  FROM pg_stat_statements
  ORDER BY total_time DESC
) AS T
WHERE
rn <= 20 -- TOP
-- rn > 20 AND rn <= 800 -- MEDIUM
-- rn > 800  -- TAIL

以䞋は、TOP ク゚リの高速化に圹立぀最も䞀般的に䜿甚されるテクニックのリストです。

  • システムの再蚭蚈。たずえば、デヌタベヌスぞの定期的なク゚リの代わりにメッセヌゞ ブロヌカヌを䜿甚しお通知ロゞックを再䜜成したす。
  • むンデックスの远加たたは倉曎
  • ORM ク゚リを玔粋な SQL に曞き盎す
  • 遅延デヌタ読み蟌みロゞックの曞き換え
  • デヌタの非正芏化によるキャッシュ。 たずえば、玍品 -> 請求曞 -> 芁求 -> 申請ずいうテヌブル接続がありたす。 ぀たり、各配信は他のテヌブルを通じおアプリケヌションに関連付けられたす。 各リク゚ストのすべおのテヌブルをリンクしないようにするために、配信テヌブル内のリク゚ストぞのリンクを耇補したした。
  • 参考曞籍を䜿甚しお静的テヌブルをキャッシュし、プログラム メモリ内のテヌブルをほずんど倉曎しない。

堎合によっおは、倉曎によっお印象的な再蚭蚈が行われるこずもありたしたが、システム負荷の 5  10% が発生し、正圓化されたした。 時間が経぀に぀れお、排気装眮はたすたす小さくなり、たすたす本栌的な再蚭蚈が必芁になりたした。

次に、私たちは 3 番目のグルヌプの芁求、぀たり䞭蟲民のグルヌプに泚目したした。 さらに倚くのク゚リが含たれおおり、グルヌプ党䜓を分析するにはかなりの時間がかかるように芋えたした。 ただし、ほずんどのク゚リは最適化が非垞に簡単であるこずが刀明し、倚くの問題がさたざたなバリ゚ヌションで䜕十回も繰​​り返されたした。 ここでは、数十の同様のク゚リに適甚した兞型的な最適化の䟋を瀺したす。最適化されたク゚リの各グルヌプは、デヌタベヌスを 5  XNUMX% アンロヌドしたした。

  • COUNT ず党テヌブル スキャンを䜿甚しおレコヌドの存圚をチェックする代わりに、EXISTS が䜿甚され始めたした。
  • DISTINCT を削陀したした (䞀般的なレシピはありたせんが、リク゚ストを 10  100 倍高速化するこずで簡単に削陀できる堎合がありたす)。

    たずえば、配送に関する倧きなテヌブル (DELIVERY) からすべおのドラむバヌを遞択するク゚リの代わりに

    SELECT DISTINCT P.ID, P.FIRST_NAME, P.LAST_NAME
    FROM DELIVERY D JOIN PERSON P ON D.DRIVER_ID = P.ID
    

    比范的小さなテヌブル PERSON に察しおク゚リを実行したした

    SELECT P.ID, P.FIRST_NAME, P.LAST_NAME
    FROM PERSON
    WHERE EXISTS(SELECT D.ID FROM DELIVERY WHERE D.DRIVER_ID = P.ID)
    

    盞関サブク゚リを䜿甚したように芋えたすが、10 倍以䞊の高速化が埗られたす。

  • 倚くの堎合、COUNT は完党に攟棄され、
    近䌌倀の蚈算に眮き換える
  • 代わりに
    UPPER(s) LIKE JOHN%’ 
    

    䜿甚

    s ILIKE “John%”
    

それぞれの特定のリク゚ストが 3  1000 倍高速化されるこずがありたした。 玠晎らしいパフォヌマンスにもかかわらず、最初は、完了たでに 10 ミリ秒かかり、3 番目に重いク゚リの XNUMX ぀であり、デヌタベヌス党䜓の読み蟌み時間の XNUMX 分の XNUMX を占めるク゚リを最適化するこずに意味がないず思われたした。 しかし、同じレシピを同じタむプのク゚リのグルヌプに適甚するこずで、数パヌセントの利益を埗るこずができたした。 䜕癟ものク゚リをすべお手動で確認する時間を無駄にしないために、正芏衚珟を䜿甚しお同じタむプのク゚リを怜玢する簡単なスクリプトをいく぀か䜜成したした。 その結果、ク゚リのグルヌプを自動的に怜玢するこずで、適床な劎力でパフォヌマンスをさらに向䞊させるこずができたした。

その結果、私たちは 30 幎間同じハヌドりェアに取り組んできたした。 70 日の平均負荷は玄 10%、ピヌク時には XNUMX% に達したす。 リク゚スト数もナヌザヌ数も玄XNUMX倍に増加したした。 これはすべお、TOP-MEDIUM リク゚ストの同じグルヌプを継続的に監芖しおいるおかげです。 TOPグルヌプに新しいリク゚ストが珟れるずすぐにそれを分析し、高速化を詊みたす。 私たちはク゚リ分析スクリプトを䜿甚しお週に XNUMX 回 MEDIUM グルヌプをレビュヌしたす。 最適化方法がすでにわかっおいる新しいク゚リに遭遇した堎合は、すぐに倉曎したす。 堎合によっおは、䞀床に耇数のク゚リに適甚できる新しい最適化方法が芋぀かるこずがありたす。

圓瀟の予枬によれば、珟圚のサヌバヌはナヌザヌ数がさらに 3  5 倍増加しおも耐えられるず考えられたす。 確かに、私たちにはもう XNUMX ぀切り札がありたす。掚奚されおいるように、ただ SELECT ク゚リをミラヌに転送しおいたせん。 しかし、私たちはこれを意識的に行っおいるわけではありたせん。なぜなら、「重砲」をオンにする前に、たず「スマヌト」な最適化の可胜性を完党に䜿い尜くしたいからです。
行われた䜜業を批刀的に芋るず、垂盎スケヌリングの䜿甚が提案される堎合がありたす。 専門家の時間を無駄にするのではなく、より匷力なサヌバヌを賌入しおください。 特に垂盎方向のスケヌリングの限界をただ䜿い果たしおいないため、サヌバヌにはそれほどコストがかからない可胜性がありたす。 ただし、リク゚ストの数だけが 10 倍に増加したした。 数幎の間にシステムの機胜が増加し、リク゚ストの皮類も増えたした。 キャッシュのおかげで、既存の機胜はより少ないリク゚ストで実行され、より効率的なリク゚ストが実行されたす。 これは、安党にさらに 5 を掛けお実際の加速係数を取埗できるこずを意味したす。 したがっお、最も控えめに芋積もっおも、加速は 50 倍以䞊であるず蚀えたす。 サヌバヌを垂盎にスむングするず、50 倍のコストがかかりたす。 特に、䞀床最適化を実行するず垞に動䜜し、レンタルサヌバヌの料金が毎月請求されるこずを考慮するず、

出所 habr.com

コメントを远加したす