Разумеем планы PostgreSQL-запытаў яшчэ зручней

Паўгода таму мы прадставілі explain.tensor.ru - публічны сэрвіс для разбору і візуалізацыі планаў запытаў да PostgreSQL.

Разумеем планы PostgreSQL-запытаў яшчэ зручней

За мінулыя месяцы мы зрабілі пра яго даклад на PGConf.Russia 2020, падрыхтавалі абагульняючую артыкул па паскарэнні SQL-запытаў на аснове рэкамендацый, якія ён выдае… але самае галоўнае - збіралі вашыя водгукі і глядзелі за рэальнымі use case.

І зараз гатовы расказаць аб новых магчымасцях, якімі вы можаце карыстацца.

Падтрымка розных фарматаў планаў

План з лога, разам з запытам

Прама з кансолі вылучаем увесь блок, пачынальна з радка з Query Text, з усімі лідзіруючымі прабеламі:

        Query Text: INSERT INTO  dicquery_20200604  VALUES ($1.*) ON CONFLICT (query)
                           DO NOTHING;
        Insert on dicquery_20200604  (cost=0.00..0.05 rows=1 width=52) (actual time=40.376..40.376 rows=0 loops=1)
          Conflict Resolution: NOTHING
          Conflict Arbiter Indexes: dicquery_20200604_pkey
          Tuples Inserted: 1
          Conflicting Tuples: 0
          Buffers: shared hit=9 read=1 dirtied=1
          ->  Result  (cost=0.00..0.05 rows=1 width=52) (actual time=0.001..0.001 rows=1 loops=1)

… і закідваем усё скапіяванае прама ў поле для плана, нічога не падзяляючы:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

На выхадзе атрымліваем бонусам да разабранага плана яшчэ і ўкладку «кантэкст», дзе наш запыт прадстаўлены ва ўсёй красе:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

JSON і YAML

EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT * FROM pg_class;

"[
  {
    "Plan": {
      "Node Type": "Seq Scan",
      "Parallel Aware": false,
      "Relation Name": "pg_class",
      "Alias": "pg_class",
      "Startup Cost": 0.00,
      "Total Cost": 1336.20,
      "Plan Rows": 13804,
      "Plan Width": 539,
      "Actual Startup Time": 0.006,
      "Actual Total Time": 1.838,
      "Actual Rows": 10266,
      "Actual Loops": 1,
      "Shared Hit Blocks": 646,
      "Shared Read Blocks": 0,
      "Shared Dirtied Blocks": 0,
      "Shared Written Blocks": 0,
      "Local Hit Blocks": 0,
      "Local Read Blocks": 0,
      "Local Dirtied Blocks": 0,
      "Local Written Blocks": 0,
      "Temp Read Blocks": 0,
      "Temp Written Blocks": 0
    },
    "Planning Time": 5.135,
    "Triggers": [
    ],
    "Execution Time": 2.389
  }
]"

Хоць з вонкавымі двукоссямі, як капіюе pgAdmin, хоць без - кідаем у тое ж поле, на выхадзе - прыгажосць:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

Пашыраная візуалізацыя

Planning Time / Execution Time

Цяпер лепш відаць, куды сышоў дадатковы час пры выкананні запыту:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

I/O Timing

Часам даводзіцца сутыкацца з сітуацыяй, калі ў плане накшталт і рэсурсаў чыталася-пісалася не занадта шмат, а накшталт і час выканання непараўнальна вялікае нейкае.

Тут даводзіцца казаць: "Ой, мусіць, у той момант кружэлка на серверы быў занадта перагружаны, таму чыталася так доўга!Але неяк гэта не занадта дакладна…

Але можна гэта вызначыць абсалютна дакладна. Справа ў тым, што сярод опцый канфігурацыі PG-сервера ёсць track_io_timing:

Уключае замер часу аперацый уводу/высновы. Гэты параметр па змаўчанні адключаны, бо для гэтага патрабуецца ўвесь час запытваць бягучы час у аперацыйнай сістэмы, што можа значна запаволіць працу на некаторых платформах. Для ацэньвання выдаткаў замеру часу на вашай платформе можна скарыстацца ўтылітай pg_test_timing. Статыстыку ўводу/высновы можна атрымаць праз уяўленне pg_stat_database, у выснове EXPLAIN (калі выкарыстоўваецца параметр BUFFERS) і праз уяўленне pg_stat_statements.

Гэты параметр можна ўключыць і ў рамках лакальнай сесіі:

SET track_io_timing = TRUE;

Ну, а зараз самае прыемнае - мы навучыліся разумець і адлюстроўваць гэтыя дадзеныя з улікам усіх трансфармацый дрэва выканання:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

Тут можна заўважыць, што з 0.790ms усяго часу выканання 0.718ms заняло чытанне адной старонкі дадзеных, 0.044ms - запіс яе ж, а на ўсю астатнюю карысную актыўнасць было выдаткавана ўсяго 0.028ms!

Будучыня з PostgreSQL 13

Азнаёміцца ​​з поўным аглядам новаўвядзенняў можна у падрабязнай артыкуле, а мы канкрэтна пра змены ў планах.

Planning buffers

Улік рэсурсаў, выдзеленых планавальніку, знайшоў сваё адлюстраванне яшчэ ў адным патчы, які не адносіцца да pg_stat_statements. EXPLAIN з опцыяй BUFFERS будзе паведамляць колькасць буфераў, скарыстаных на этапе планавання:

 Seq Scan on pg_class (actual rows=386 loops=1)
   Buffers: shared hit=9 read=4
 Planning Time: 0.782 ms
   Buffers: shared hit=103 read=11
 Execution Time: 0.219 ms

Разумеем планы PostgreSQL-запытаў яшчэ зручней

Інкрыментальная сартаванне

У выпадках, калі неабходна сартаванне па шматлікіх ключах (k1, k2, k3…), планавальнік зараз можа скарыстацца веданнем аб тым, што дадзеныя ўжо адсартаваныя па некалькіх з першых ключоў (напрыклад, k1 і k2). У гэтым выпадку можна не перасартаваць усе дадзеныя нанова, а падзяліць іх на паслядоўныя групы з аднолькавымі значэннямі k1 і k2, і "дасартаваць" па ключы k3.

Такім чынам усё сартаванне распадаецца на некалькі паслядоўных сартаванняў меншага памеру. Гэта зніжае аб'ём неабходнай памяці, а яшчэ дазваляе выдаваць першыя дадзеныя раней, чым усё сартаванне будзе выканана цалкам.

 Incremental Sort (actual rows=2949857 loops=1)
   Sort Key: ticket_no, passenger_id
   Presorted Key: ticket_no
   Full-sort Groups: 92184 Sort Method: quicksort Memory: avg=31kB peak=31kB
   ->  Index Scan using tickets_pkey on tickets (actual rows=2949857 loops=1)
 Planning Time: 2.137 ms
 Execution Time: 2230.019 ms

Разумеем планы PostgreSQL-запытаў яшчэ зручней
Разумеем планы PostgreSQL-запытаў яшчэ зручней

Паляпшэнні UI/UX

Скрыншоты, яны ўсюды!

Цяпер на кожнай укладцы з'явілася магчымасць хутка ўзяць скрыншот ўкладкі ў буфер абмену на ўсю шырыню і глыбіню ўкладкі - "прыцэл" справа-зверху:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

Уласна, большасць карцінак для гэтай публікацыі атрымана менавіта так.

Рэкамендацыі на вузлах

Іх не толькі стала больш, але і пра кожную можна падрабязна прачытаць у артыкуле, перайшоўшы па спасылцы:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

Выдаленне з архіва

Некаторыя вельмі прасілі дадаць магчымасць выдаляць "зусім" нават непублікаваныя ў архіве планы — калі ласка, дастаткова націснуць адпаведны абразок:

Разумеем планы PostgreSQL-запытаў яшчэ зручней

Ну, і не забываем, што ў нас ёсць група падтрымкі, куды можна пісаць свае заўвагі і прапановы.

Крыніца: habr.com

Дадаць каментар