Struchkov Mark
d20df572a6
All checks were successful
continuous-integration/drone/push Build is passing
144 lines
15 KiB
Markdown
144 lines
15 KiB
Markdown
---
|
||
parents:
|
||
- "[[Java конференция Joker]]"
|
||
aliases:
|
||
tags:
|
||
- maturity/🌱
|
||
- type/source/lecture
|
||
- type/source/best
|
||
date: 2022-10-14
|
||
---
|
||
**Организатор**:: [[../../meta/organizations/JUG Ru Group|JUG Ru Group]]
|
||
**Конференция**:: [[../conference/Конференция. Jpont 2022|Jpont 2022]]
|
||
**Автор**:: [[../../meta/people/Сальников Андрей|Сальников Андрей]]
|
||
**Ссылка**:: [Андрей Сальников — Индексы в PostgreSQL. Как понять, что создавать - YouTube](https://www.youtube.com/watch?v=ju9F8OvnL4E)
|
||
***
|
||
![Андрей Сальников — Индексы в PostgreSQL. Как понять, что создавать - YouTube](https://www.youtube.com/watch?v=ju9F8OvnL4E)
|
||
***
|
||
**О чем доклад:** Любой разработчик знает, что индексы — это мощный инструмент, который может улучшить работу запросов в базе данных и, как следствие, сократить отклик приложения или сервиса на внешние запросы. Но опыт Андрея, как ДБА, показывает, что у разработчиков нет понимания, какой, когда и из каких соображений можно создавать индекс. Спикер приведет простые и понятные примеры, которые вы сможете легко повторить на своих реальных базах данных.
|
||
|
||
В докладе в основном говорится об [[../../dev/database/Online Transaction Processing|OLTP]] нагрузке и объем баз данных от 20 Гб до 10 Тб.
|
||
## Тезисы
|
||
-
|
||
## Конспект
|
||
Многие разработчики не проводят исследовательскую работу перед созданием индексов и создают их как считают правильно, и не всегда это мнение совпадает с реальностью.
|
||
|
||
Первым делом стоит ознакомиться с документаций: [PostgreSQL: Documentation: 16: CREATE INDEX](https://www.postgresql.org/docs/current/sql-createindex.html)
|
||
|
||
Прежде чем создавать индексы, нужно понять что такое индексы. Фактически это легализованные косты для [[../../dev/database/Оптимизация SQL запросов|ускорения SQL запросов]]. В PostgreSQL индекс для первичного ключа индекс создается автоматически.
|
||
|
||
**Какие накладные расходы от индексов?**
|
||
- Замедление операций вставки и обновлений. Так как необходимо будет перестраивать индекс при вставке новых значений. Но это должно быть не так страшно, так как профиль нагрузки на реляционную базу данных выглядит следующим образом: 80% запросов это чтение, 20% запросов это запись. Если запросов на запись больше, то возможно реляционная база данных вам не подходит.
|
||
- Дополнительные объемы дискового пространства для хранения индекса. Размер индексов на таблицу в половину размера таблицы считается нормальным и оптимальным. Если размер всех индексов таблицы приближается или становится больше, значит что-то идет не так.
|
||
- Усложненное технического обслуживание. Индексы пухнут и переодически их нужно пересоздавать. В каких-то СУБД это происходит автоматически. Пересоздание индекса сложный процесс и может повлечь недоступность сервиса.
|
||
|
||
При создании индекса нужно провести анализ. Иначе можно получить все накладные расходы, и не получить преимущества.
|
||
|
||
**Что нужно для создания индекса?**
|
||
- Ориентироваться только на продуктовое окружение, так как тестовые окружения не соответствуют реальности.
|
||
- Собрать статистику нагрузки на БД от запросов. Чтобы понять какие запросы действительно требуют оптимизации. - [[../../dev/database/Плохой SQL запрос|Плохой SQL запрос]]. Для этого можно использовать различные инструменты
|
||
- [[../../dev/database/postgresql/pg_stat_statements|pg_stat_statements]]
|
||
- pgBadger - использовать с осторожностью. Собирает статистику из логов. Но в логи попадают не все запросы.
|
||
- Иметь примеры запросов с параметрами. Это необходимо для проверки проведенных оптимизаций.
|
||
- Нужно уметь читать статистику распределения данных - [[../../dev/database/postgresql/Таблица статистик pg_stats|Таблица статистик pg_stats]]. Это нужно, чтобы понимать как планировщик БД будет строить план выполнения запроса.
|
||
- По умолчанию PostgrteSQL использует для сбора статистики только 30k строк из таблицы. Из-за этого статистика может расходиться с реальностью. И нужно уметь собирать более полную статистику вручную. Когда есть подозрения, что в статистике есть существенные промахи.
|
||
|
||
Далее идет описание типов индексов, которые есть в Postgres, и которые перечислены в моей заметке [[../../dev/database/postgresql/Индекс в PostgreSQL#Типы индексов|Индекс в PostgreSQL]].
|
||
|
||
Для OLTP нагрузки не стоит использовать параллельное выполнение запроса. Так как это значит, что мы забираем ядро процессора у другого запроса. В OLTP нагрузке каждый запрос должен выполняться на одном процессе так быстро, как только возможно.
|
||
## Практика
|
||
![](../../meta/files/images/Pasted%20image%2020240331092706.png)
|
||
![](../../meta/files/images/Pasted%20image%2020240331092753.png)
|
||
- У таблицы есть первичный (bigint) и внешний ключи.
|
||
- Таблица имеет колонки различных типов данных.
|
||
- Таблица ссылается сама на себя, но это сделано для удобства доклада. Те же самые выводы распространятся и на связи с другими таблицами.
|
||
- Количество кортежей 10_000_000
|
||
- Индекс по первичному ключу занял 1/4 (214 Mb) от размера таблицы (816 Mb)
|
||
### Удаляем строку
|
||
Удаляем строку по первичному ключу.
|
||
|
||
![600](../../meta/files/images/Pasted%20image%2020240331093028.png)
|
||
В удалении задействован первичный ключ, поэтому используется поиск по индексу. Но по итогу ==самый долгий этап это проверка внешних связей с таблицей==. По итогу запрос вроде бы быстрый, но он не оптимальный, он потребляет намного больше ресурсов сервера, чем должен.
|
||
|
||
Под капотом для поиска внешних связей используется полное сканирование таблицы (Seq Scan). В данном примере специально включено паралеллельное выполнение, но это все равно занимает много времени.
|
||
|
||
![600](../../meta/files/images/Снимок%20экрана%202024-03-31%20в%2009.32.34.png)
|
||
|
||
Поэтому важно не забывать [[../../dev/database/Индекс для внешнего ключа таблицы БД|создавать индексы на Foreign Key]], чтобы различные проверки БД выполнялись с использованием индекса.
|
||
|
||
Добавляем индекс на FK и проверяем результат:
|
||
|
||
![600](../../meta/files/images/Pasted%20image%2020240331093524.png)
|
||
|
||
Время выполнения было 281 ms, а стало 0.1 ms!
|
||
|
||
Большинство разработчиков на этом этапе успокоится, но можно ли сделать лучше?
|
||
#### Смотрим статистику
|
||
- [Таблица статистик pg_stats](../../dev/database/postgresql/Таблица%20статистик%20pg_stats.md)
|
||
|
||
![600](../../meta/files/images/Снимок%20экрана%202024-03-31%20в%2009.36.56.png)
|
||
|
||
В данном случае у нас 92% значений в колонке это null значения.
|
||
|
||
При большом значении `null_frac` нас уже меньше волнуют остальные параметры. Основываясь на этой информации мы можем уменьшить размер индекса. Для этого изменим запрос на создание индекса, добавив `where fk_id is not null`.
|
||
|
||
![600](../../meta/files/images/Pasted%20image%2020240331094502.png)
|
||
|
||
Это не ускорит нам запрос, но таким образом у нас получилось сжать индекс в 14 раз:
|
||
|
||
![500](../../meta/files/images/Pasted%20image%2020240331095259.png)
|
||
### Поиск записей по статусу
|
||
Возьмем типичную табличку, в которой есть какие-то статусы мы хотим находить данные по этому статусу.
|
||
|
||
![300](../../meta/files/images/Pasted%20image%2020240331095959.png)
|
||
![600](../../meta/files/images/Pasted%20image%2020240331100144.png)
|
||
|
||
Часто появляется желание сделать индекс по полю статуса:
|
||
|
||
![600](../../meta/files/images/Снимок%20экрана%202024-03-31%20в%2010.07.02.png)
|
||
|
||
Но по факту мы индексируем поле, которое имеет небольшую [селективность](../../dev/database/Селективность%20колонки.md). Такой индекс не эффективный.
|
||
|
||
Хороший вариант в данном случае:
|
||
![600](../../meta/files/images/Снимок%20экрана%202024-03-31%20в%2010.13.39.png)
|
||
|
||
Почти идеальный:
|
||
![600](../../meta/files/images/Снимок%20экрана%202024-03-31%20в%2010.15.06.png)
|
||
|
||
![500](../../meta/files/images/Pasted%20image%2020240331101612.png)
|
||
|
||
Идеальный. Совмещаем и [[../../dev/database/postgresql/Составной индекс в PostgreSQL|составной индекс]] и [[../../dev/database/Частичный индекс|Частичный индекс]]
|
||
|
||
![[../../meta/files/images/Pasted image 20241021225124.png]]
|
||
### Онлайн статистика
|
||
Представим, что у вас небольшой продукт и PostgreSQL у вас единственное хранилище данных. И у вас есть задача показывать какую-то аналитику. Обычно для аналитических запросов хорошо использовать колоночные БД. Но так как ресурсов проекта у вас не много и задач на анализ не так много, то позволить себе такое вы не можете.
|
||
|
||
Можно сделать небольшой шаг в сторону колоночных БД в PostgreSQL за счет индексов.
|
||
|
||
В данном случае будем показывать сколько фруктов продано. Если использовать в лоб операции coun и sum, то они будут занимать достаточно много времени.
|
||
|
||
![[../../meta/files/images/Pasted image 20241021225909.png]]
|
||
|
||
Если не используем индексы
|
||
|
||
![[../../meta/files/images/Pasted image 20241021230132.png]]
|
||
|
||
Лучшее решение здесь это расчитывать заранее агрегирующие результаты за старые данные и обновлять их раз в сутки, а наиболее актуальные (за последние сутки) расчитывать отдельно и приплюсовывать к историческим.
|
||
|
||
Попробуем придумать индекс. Посмотрим на статистику по полю item. Видим, что больше половины таблицы занимает значение "дыня", значит с дыней придется прочитать половину таблицы.
|
||
|
||
![[../../meta/files/images/Pasted image 20241022212435.png]]
|
||
|
||
Поэтому первым стоит указать поле `created_at`, учитывая что оно участвует в запросе, а вторым полем добавить `item`.
|
||
|
||
![[../../meta/files/images/Pasted image 20241022212658.png]]
|
||
|
||
Но можно пойти еще дальше и использовать include(amount). Таким образом мы присоединим поле к индексу, оно не будет индексироваться. То есть значение amount будет лежать рядом, не нужно будет доставать значение из таблицы. Также используем where, чтобы отрезать все ненужные колонки.
|
||
|
||
![[../../meta/files/images/Pasted image 20241022213322.png]]
|
||
|
||
## Ответы на вопросы
|
||
- Автор в незнакомых базах смотрит
|
||
- на соотношение размеров таблицы и индексов.
|
||
- на количество чтений индексов
|
||
- дальше уже смотрит на то как были созданы индексы |