В PostgreSQL изначально была реализована дискреционная модель: владельцы объектов (таблиц, схем, баз данных и т.д.) самостоятельно определяют, кто и какие действия может выполнять с их объектами
В PostgreSQL каждый объект имеет владельца — пользователя или роль, создавшую объект. Владелец обладает полным контролем над этим объектом
Ролевая модель доступа (появилась в PostgreSQL 10): роль может быть назначена отдельному пользователю или группе. Роли удобны для централизованного управления правами
-- Создание роли с набором атрибутов
CREATE ROLE имя_роли WITH
LOGIN -- возможность входа
CREATEDB -- право создавать базы
CREATEROLE -- право изменять роли
SUPERUSER -- суперпользовательские права
INHERIT; -- унаследование прав от других ролей
Роли могут наследоваться (INHERIT) и передавать свои привилегии
Ролям можно присваивать следующие права для доступа к операциям над таблицами:
TRUNCATE быстрее DELETE, потому что:
- Не записывает удаление каждой строки в WAL по отдельности
- Освобождает целые страницы данных за один вызов
- Не вызывает сканирования таблицы для удаления строк
Привилегии на базы данных:
По умолчанию PostgreSQL создаёт новую базу как копию template0
или template1
Схемы — логические контейнеры объектов внутри базы
public
Лексический анализ
На этом этапе сервер принимает SQL-текст запроса и анализирует его на наличие синтакcических ошибок. Результатом является дерево разбора (parse tree), представляющее структуру запроса
Семантический разбор
Здесь заменяются представления на их определения, обрабатываются правила, определенные для таблиц, а так же проверяются права у пользователя к этим объектам
Планирование
PostgreSQL может выполнять один и тот же запрос разными способами. Планировщик генерирует альтернативные планы (деревья с физическими операциями)
Планировщик принимает переписанное дерево запроса и генерирует один или несколько вариантов выполнения запроса. Количество возможных планов экспоненциально зависит от количества соединяемых таблиц. Для сокращения пространства перебора традиционно используется алгоритм динамического программирования с эвристиками
С помощью ключевого слова EXPLAIN
можно узнать структуру плана запроса, а с помощью EXPLAIN ANALYZE
можно, выполнив его, узнать реальные затраты
Оптимизатор оценивает стоимость каждого варианта плана, используя статистические данные о таблицах. Точное решение задачи оптимизации не гарантирует, что найденный план будет действительно лучшим
Планы делятся на общие и частные. При выполнении подготовленных запросов план выполнения может быть сохранен и повторно использован
Общий план, который строится один раз без учета конкретных значений параметров, предоставленных во время исполнения
Частный план, который создается при каждом выполнении подготовленного запроса и учитывает конкретные значения параметров
Если запрос, выполненный по частному плану, поступает многократно, то частный план становится общим. Однако, если время ответа увеличилось, то он обратно становится частным
Выполнение
После выбора оптимального плана исполнитель запускает последовательность операций, предусмотренных планом. Он последовательно проходит по узлам плана
При выполнении запросов осуществляется доступ к данным через буферный менеджер, который минимизирует количество операций ввода/вывода на диск
Если запрос рассчитан на параллельное выполнение, результат собирается из нескольких рабочих процессов
Статистика - набор данных, который собирается системой для оценки распределения значений в таблицах и столбцах, что затем используется планировщиком
PostgreSQL хранит статистику в системной таблице pg_statistic
. Обычно используют представление pg_stats
Базовая статистика отношений хранится в таблице pg_class
pg_stats
хранит:
reltuples
- число строк в таблицеn_distinct
- оценка уникальных значенийmost_common_vals
, most_common_freqs
- список наиболее часто встречающихся значений и их частотыhistogram_bounds
- границы гистограммы распределения значений, которые используются для оценки селективности диапазонных запросовcorrelation
- оценка корреляции между порядком значений в столбце и порядком их храненияnull_frac
- доля NULL-значенийСтатистика собирается при анализе (ручном или автоматическом). Однако ввиду особой важности базовая статистика рассчитывается также при выполнении некоторых операций и уточняется при очистке
Для анализа случайно выбираются 300*default_statistics_target
строк. Поскольку размер выборки, достаточной для построения статистики заданной точности, слабо зависит от объема анализируемых данных, размер таблицы не учитывается
Для уточнения оценки при неравномерном распределении собирается статистика по наиболее часто встречающимся значениям и частоте их появления. Представление pg_stats
показывает два этих массива в столбцах most_common_vals
и most_common_freq
Список частых значений используется и для оценки селективности условий с неравенствами. Например, для условия вида “столбец < значение” надо найти в most_common_vals
все значения, меньшие искомого, и просуммировать частоты из most_common_freqs
Когда число различных значений слишком велико, чтобы записать их в массив, на помощь приходит гистограмма. Гистограмма состоит из нескольких корзин, представляющих некий диапазон
Поле correlation
показывается корреляцию между физическим расположением данных и логических порядком в смысле операций сравнения. Если значения хранятся строго по возрастанию, корреляция будет близка к 1; если по убыванию, то к -1. Если данные расположены хаотично, то корреляция близка к 0. Корреляция используется для оценки стоимости индексного сканирования
Последовательное сканирование
Полностью читается файл основного слоя таблицы. На каждой прочитанной странице проверяется видимость каждой версии строки
Чтение происходит через буферный кеш. При этом другие процессы, одновременно сканирующие ту же таблицу, присоединяются к кольцу и тем самым экономят операции дисковых чтений. Поэтому в общем случае сканирование может стартовать не с начала файла
Последовательное сканирование - самый эффективный способ прочитать всю таблицу или значительную ее часть. Последовательное сканирование хорошо работает при низкой селективности
В оценки стоимости оптимизатор учитывает, сколько будет стоить дисковый ввод-вывод и ресурсы процессора. Стоимость ввода-вывода рассчитывается как произведение числа страниц в таблице на стоимость чтения одной страницы
Соотношение по умолчанию подходит для HDD; для SDD имеет смысл существенно уменьшить значение параметра random_page_cost
- стоимость рандомного доступа к страницу (значение стоимости последовательного доступа seq_page_cost
, как правило, оставляют равным единице)
Параллельное последовательное сканирование
Чтение выполняется несколькими параллельно работающими процессами. Процессы синхронизируются между собой с помощью специально отведенного участка памяти
Сканирование только индекса
Быстро для точечных запросов
Эффективность зависит от correlation
и random_page_cost
Использует карту видимости, чтобы пропустить удалённые/невидимые строки. На оценку сканирования только индекса влияет доля табличных страниц, отмеченных в карте видимости
Сканирование по битовой карте
Ограничение индексного сканирования связано с тем, что при уменьшении корреляции увеличивается количество обращений к страницам, а характер чтения меняется с последовательного на случайный
PostgreSQL использует обычные индексы для получения списка идентификаторов строк, удовлетворяющих условию запроса. Вместо того, чтобы сразу возвращать строки, создается битовая карта - массив для какого-то условия, где 1 в нем - подходит ли кортеж в отношении этому условию.
Когда запрос использует несколько условий, можно создать битовые карты для каждого условия, а затем объединить их
Соединение можно делать:
Вложенными циклам (Nested Loop) - хорошо подходит для небольших таблиц или при наличии индексов
Сортировкой и слиянием (Merge Join) - алгоритм эффективен при предварительной сортировке
Хеш-соединение (Hash Join) - быстрое, используется хеш-таблица с цепочками
Параллельное хеш-соединение - для ускорения на многоядерных системах
PostgreSQL использует быструю сортировку (quicksort), чтобы выполнить операцию соединения. Размер выделенной памяти для сортировки контролируется параметром work_mem
. Если объем данных превышает work_mem
, PostgreSQL может производить сортировку с временным сохранением промежуточных результатов на диске
Агрегацию (вычисление значений, таких как COUNT
, AVG
и так далее) можно делать:
Хеш-агрегация — быстрая при достаточной памяти
Сортировочная агрегация — эффективна при ограничениях по памяти для хеш-агрегации или когда используются группировки по нехешируемым типам
Shared buffers - область оперативной памяти, где PostgreSQL хранит копии страниц таблиц и индексов. Повторный доступ к данным из буфера ускоряет работу
Управляется параметром shared_buffers
(рекомендуется ставить значение, равное 25–40% от объема оперативной памяти)