This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 650" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Как читать EXPLAIN ANALYZE от результата к первому расхождению</title>
|
||||
<desc id="desc">Вертикальное дерево плана показывает Result, Filter и Index Scan. На каждом уровне сравниваются оценка строк, фактические строки, loops и buffers; расследование начинается с первого расхождения снизу.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f8fafc; }
|
||||
.node { fill: #ffffff; stroke: #94a3b8; stroke-width: 2; }
|
||||
.scan { fill: #ecfdf5; stroke: #059669; stroke-width: 2; }
|
||||
.warn { fill: #fff7ed; stroke: #ea580c; stroke-width: 2; }
|
||||
.note { fill: #eff6ff; stroke: #2563eb; stroke-width: 2; }
|
||||
.title { font: 700 24px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
|
||||
.label { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
|
||||
.text { font: 15px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #334155; }
|
||||
.mono { font: 600 14px ui-monospace, SFMono-Regular, Menlo, monospace; fill: #0f172a; }
|
||||
.arrow { stroke: #64748b; stroke-width: 3; fill: none; marker-end: url(#tip); }
|
||||
.dash { stroke: #ea580c; stroke-width: 3; stroke-dasharray: 7 6; fill: none; marker-end: url(#tipOrange); }
|
||||
</style>
|
||||
<marker id="tip" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#64748b" />
|
||||
</marker>
|
||||
<marker id="tipOrange" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#ea580c" />
|
||||
</marker>
|
||||
</defs>
|
||||
<rect class="bg" width="760" height="650" rx="28" />
|
||||
<text class="title" x="42" y="52">План — поток данных, не список красивых узлов</text>
|
||||
<rect class="node" x="150" y="86" width="360" height="100" rx="14" />
|
||||
<text class="label" x="178" y="122">Aggregate / Result</text>
|
||||
<text class="mono" x="178" y="152">rows=1 · actual rows=1 · loops=1</text>
|
||||
<path class="arrow" d="M330 188 L330 244" />
|
||||
<rect class="warn" x="150" y="254" width="360" height="116" rx="14" />
|
||||
<text class="label" x="178" y="291">Filter</text>
|
||||
<text class="mono" x="178" y="321">rows=100 · actual rows=100 000</text>
|
||||
<text class="text" x="178" y="347">первое заметное расхождение: ищем причину</text>
|
||||
<path class="arrow" d="M330 372 L330 428" />
|
||||
<rect class="scan" x="150" y="438" width="360" height="116" rx="14" />
|
||||
<text class="label" x="178" y="475">Index Scan</text>
|
||||
<text class="mono" x="178" y="505">Index Cond: state = 'ready'</text>
|
||||
<text class="mono" x="178" y="530">Buffers: shared hit/read · loops=1</text>
|
||||
<rect class="note" x="548" y="235" width="174" height="164" rx="14" />
|
||||
<text class="label" x="568" y="271">Проверить</text>
|
||||
<text class="text" x="568" y="302">1. rows vs actual</text>
|
||||
<text class="text" x="568" y="330">2. loops</text>
|
||||
<text class="text" x="568" y="358">3. Index Cond</text>
|
||||
<text class="text" x="568" y="386">4. Buffers</text>
|
||||
<path class="dash" d="M545 318 L515 318" />
|
||||
<rect class="node" x="76" y="580" width="608" height="42" rx="12" />
|
||||
<text class="text" x="106" y="607">Не лечим верхний узел: начинаем с первого места, где оценка потеряла связь с фактом.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.6 KiB |
@@ -0,0 +1,50 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 650" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Форма условия должна соответствовать индексному ключу</title>
|
||||
<desc id="desc">Индекс хранит created_at. Предикат created_at::date создаёт выражение и не совпадает с ключом напрямую, а полуоткрытый диапазон сравнивает исходный created_at и может стать кандидатом для B-tree после проверки плана и семантики времени.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f8fafc; }
|
||||
.key { fill: #e0f2fe; stroke: #0284c7; stroke-width: 2; }
|
||||
.bad { fill: #fef2f2; stroke: #dc2626; stroke-width: 2; }
|
||||
.good { fill: #ecfdf5; stroke: #059669; stroke-width: 2; }
|
||||
.note { fill: #fffbeb; stroke: #d97706; stroke-width: 2; }
|
||||
.title { font: 700 24px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
|
||||
.label { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
|
||||
.text { font: 15px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #334155; }
|
||||
.mono { font: 600 14px ui-monospace, SFMono-Regular, Menlo, monospace; fill: #0f172a; }
|
||||
.arrow { stroke: #64748b; stroke-width: 3; fill: none; marker-end: url(#tip); }
|
||||
.badline { stroke: #dc2626; stroke-width: 4; }
|
||||
</style>
|
||||
<marker id="tip" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#64748b" />
|
||||
</marker>
|
||||
</defs>
|
||||
<rect class="bg" width="760" height="650" rx="28" />
|
||||
<text class="title" x="42" y="52">Индекс хранит ключ, а не намерение запроса</text>
|
||||
<rect class="key" x="175" y="84" width="410" height="82" rx="14" />
|
||||
<text class="label" x="278" y="119">B-tree: (created_at)</text>
|
||||
<text class="text" x="246" y="146">ключ — исходный timestamp без cast</text>
|
||||
<path class="arrow" d="M278 168 L184 230" />
|
||||
<path class="arrow" d="M482 168 L576 230" />
|
||||
<rect class="bad" x="38" y="240" width="306" height="182" rx="14" />
|
||||
<text class="label" x="62" y="277">Выражение в WHERE</text>
|
||||
<text class="mono" x="62" y="311">created_at::date</text>
|
||||
<text class="mono" x="62" y="337">= DATE '2019-07-10'</text>
|
||||
<text class="text" x="62" y="373">ключ и выражение разные</text>
|
||||
<text class="text" x="62" y="399">проверяем range или</text>
|
||||
<text class="text" x="62" y="420">обоснованный expression index</text>
|
||||
<line class="badline" x1="78" y1="256" x2="304" y2="406" />
|
||||
<rect class="good" x="416" y="240" width="306" height="182" rx="14" />
|
||||
<text class="label" x="440" y="277">Полуоткрытый range</text>
|
||||
<text class="mono" x="440" y="311">created_at >= day</text>
|
||||
<text class="mono" x="440" y="337">AND created_at < day + 1</text>
|
||||
<text class="text" x="440" y="373">исходный ключ сохранён</text>
|
||||
<text class="text" x="440" y="399">кандидат для B-tree:</text>
|
||||
<text class="text" x="440" y="420">подтверждаем планом</text>
|
||||
<path class="arrow" d="M190 424 L285 486" />
|
||||
<path class="arrow" d="M570 424 L475 486" />
|
||||
<rect class="note" x="116" y="496" width="528" height="104" rx="14" />
|
||||
<text class="label" x="148" y="532">Сначала смысл времени, затем EXPLAIN ANALYZE</text>
|
||||
<text class="text" x="148" y="561">Проверить timezone, rows, Index Cond, Filter и Buffers.</text>
|
||||
<text class="text" x="148" y="584">Скорость не оправдывает неверную календарную границу.</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,47 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 760 620" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Селективность определяет ценность индекса</title>
|
||||
<desc id="desc">Из миллиона заказов условие ready оставляет девятьсот девяносто тысяч строк, а waiting десять тысяч. Для обоих существует индекс, но для частого значения последовательное чтение может быть дешевле.</desc>
|
||||
<defs>
|
||||
<style>
|
||||
.bg { fill: #f8fafc; }
|
||||
.card { fill: #ffffff; stroke: #cbd5e1; stroke-width: 2; }
|
||||
.source { fill: #e0f2fe; stroke: #0284c7; stroke-width: 2; }
|
||||
.hot { fill: #fff7ed; stroke: #ea580c; stroke-width: 2; }
|
||||
.cold { fill: #ecfdf5; stroke: #059669; stroke-width: 2; }
|
||||
.note { fill: #eff6ff; stroke: #2563eb; stroke-width: 2; }
|
||||
.title { font: 700 25px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
|
||||
.label { font: 700 18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #0f172a; }
|
||||
.text { font: 16px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #334155; }
|
||||
.small { font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; fill: #475569; }
|
||||
.mono { font: 600 15px ui-monospace, SFMono-Regular, Menlo, monospace; fill: #0f172a; }
|
||||
.line { stroke: #64748b; stroke-width: 3; fill: none; marker-end: url(#arrow); }
|
||||
</style>
|
||||
<marker id="arrow" markerWidth="10" markerHeight="10" refX="8" refY="3" orient="auto" markerUnits="strokeWidth">
|
||||
<path d="M0,0 L0,6 L9,3 z" fill="#64748b" />
|
||||
</marker>
|
||||
</defs>
|
||||
<rect class="bg" width="760" height="620" rx="28" />
|
||||
<text class="title" x="42" y="52">Индекс — кандидат; селективность решает цену</text>
|
||||
<rect class="source" x="210" y="82" width="340" height="86" rx="14" />
|
||||
<text class="label" x="290" y="119">work_orders</text>
|
||||
<text class="text" x="272" y="148">1 000 000 строк после ANALYZE</text>
|
||||
<path class="line" d="M300 170 L185 236" />
|
||||
<path class="line" d="M460 170 L575 236" />
|
||||
<rect class="hot" x="42" y="246" width="292" height="168" rx="14" />
|
||||
<text class="mono" x="66" y="281">WHERE state = 'ready'</text>
|
||||
<text class="label" x="66" y="320">990 000 строк</text>
|
||||
<text class="text" x="66" y="350">почти вся таблица</text>
|
||||
<text class="small" x="66" y="382">индекс + heap-обращения</text>
|
||||
<text class="small" x="66" y="403">могут стоить дороже Seq Scan</text>
|
||||
<rect class="cold" x="426" y="246" width="292" height="168" rx="14" />
|
||||
<text class="mono" x="448" y="281">WHERE state = 'waiting'</text>
|
||||
<text class="label" x="448" y="320">10 000 строк</text>
|
||||
<text class="text" x="448" y="350">около 1% таблицы</text>
|
||||
<text class="small" x="448" y="382">индекс может сузить путь</text>
|
||||
<text class="small" x="448" y="403">проверяем фактическим планом</text>
|
||||
<path class="line" d="M188 416 L285 482" />
|
||||
<path class="line" d="M572 416 L475 482" />
|
||||
<rect class="note" x="126" y="492" width="508" height="88" rx="14" />
|
||||
<text class="label" x="164" y="527">EXPLAIN (ANALYZE, BUFFERS)</text>
|
||||
<text class="text" x="164" y="555">сравнить rows, actual rows, loops и Buffers до нового CREATE INDEX</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.5 KiB |
Reference in New Issue
Block a user