├── .gitignore ├── architecture ├── architecture.md ├── db_cluster.png ├── db_memory.png ├── db_processes.png ├── db_structure.png └── db_tablespace.png ├── dangerous online operations └── online_on_big_data.md ├── dba scripts ├── PostgreSQL_DBA.md └── sql_tricks.md ├── disk storage ├── data_access.png ├── data_read.png ├── heap_file_page.png ├── postgresql_cheetsheets.md └── table_file.png ├── indexes └── reindex.md ├── kernel resources └── huge_pages.md ├── locks └── PostgreSQL_locking.md ├── logging └── PostgreSQL_logging.md ├── monitoring └── PostgreSQL_monitoring.md ├── psql └── PostgreSQL_psql.md ├── run-time parameters └── run-time.md ├── schemas and priveleges └── postgresql_schema.md ├── statistics and data analysis └── PostgreSQL_анализ_данных.md ├── time and timezones └── PostgreSQL_timestamp.md ├── transactions └── PostgreSQL_transactions.md ├── vacuum ├── heap_file_page.png └── postgresql_vacuum.md └── wal └── wal.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | -------------------------------------------------------------------------------- /architecture/architecture.md: -------------------------------------------------------------------------------- 1 | ## Архитектура PostgreSQL 2 | ### Внутренние процессы 3 | ![db_processes](db_processes.png) 4 | Сервер PostgreSQL состоит из следующих проессов 5 | - основной серверный процесс, являющийся родителем всех остальных процессов и управляющий ими; 6 | - backend процессы, отвечающие за выполнение запросов от клиентов; 7 | - внутренние процессы (background processes), обслуживающие БД (VACUUM, CHECKPOINT и другие); 8 | - процессы репликации (осуществляющие потоковую репликацию); 9 | - background worker процессы, созданные пользователями. 10 | 11 | Внутренние процессы 12 | 13 | | Процесс | Описание | 14 | | :------------- | :------------- | 15 | | background writer | In this process, dirty pages on the shared buffer pool are written to a persistent storage (e.g., HDD, SSD) on a regular basis gradually. (In version 9.1 or earlier, it was also responsible for checkpoint process.) | 16 | | checkpointer | In this process in version 9.2 or later, checkpoint process is performed. | 17 | | autovacuum launcher | The autovacuum-worker processes are invoked for vacuum process periodically. (More precisely, it requests to create the autovacuum workers to the postgres server.) | 18 | | WAL writer | This process writes and flushes periodically the WAL data on the WAL buffer to persistent storage. | 19 | | statistics collector | In this process, statistics information such as for pg_stat_activity and for pg_stat_database, etc. is collected. | 20 | | logging collector (logger) | This process writes error messages into log files. | 21 | | archiver | In this process, archiving logging is executed. | 22 | 23 | --- 24 | **NOTE** 25 | 26 | Больше информации про `VACUUM` в PostgreSQL можно найти в разделе [vacuum](https://github.com/yum-install-brains/postgresql-spot/blob/master/vacuum/postgresql_vacuum.md). 27 | 28 | --- 29 | 30 | ### Memory architecture 31 | ![db_memory](db_memory.png) 32 | Память в PostgreSQL делится на две большие категории 33 | - локальная память (своя у каждого процесса); 34 | - общая память (общая для использования всеми процессами). 35 | 36 | Каждый backend процесс выделяет себе локальную память для выполнения запросов. Локальная память в свою очередь разделяется на несколько областей 37 | - work_mem (Executor uses this area for sorting tuples by ORDER BY and DISTINCT operations, and for joining tables by merge-join and hash-join operations); 38 | - maintenance_work_mem (Some kinds of maintenance operations (e.g., VACUUM, REINDEX) use this area); 39 | - temp_buffers (Executor uses this area for storing temporary tables). 40 | 41 | Общая память выделяется серверу PostgreSQL при его запуске и также разделяется на несколько областей 42 | - shared buffer pool (PostgreSQL loads pages within tables and indexes from a persistent storage to here, and operates them directly); 43 | - WAL buffer (To ensure that no data has been lost by server failures, PostgreSQL supports the WAL mechanism. WAL data (also referred to as XLOG records) are transaction log in PostgreSQL; and WAL buffer is a buffering area of the WAL data before writing to a persistent storage); 44 | - commit log (Commit Log(CLOG) keeps the states of all transactions (e.g., in_progress,committed,aborted) for Concurrency Control (CC) mechanism). 45 | 46 | Также к общей памяти относятся области 47 | - Sub-areas for the various access control mechanisms. (e.g., semaphores, lightweight locks, shared and exclusive locks, etc) 48 | - Sub-areas for the various background processes, such as checkpointer and autovacuum. 49 | - Sub-areas for transaction processing such as save-point and two-phase-commit. 50 | - and others. 51 | 52 | ### Буферный кэш (shared buffers) 53 | Буферный кэш используется для сглаживания скорости работы памяти и дисков. Он состоит из массива буферов, которые содержат страницы (блоки) данных и дополнительную информацию об этих страницах. Размер страницы обычно составляет 8 КБ, хотя может устанавливаться при сборке. Буферный кэш, как и другие структуры в памяти, защищен блокировками от одновременного доступа. Блокировки организованы достаточно эффективно, чтобы не создавать большой конкуренции. Любая страница, с которой работает СУБД, попадает в кэш. Часто используемые страницы остаются в кэше надолго; редко используемые — вытесняются и заменяются другими страницами. Буфер, содержащий измененную страницу, называется «грязным». Процесс Background Writer постепенно записывает их на диск в фоновом режиме — это позволяет снизить нагрузку на диски и увеличить производительность. Если Background Writer не успевает записать вытесняемый серверным процессом грязный буфер, то процесс записывает его сам. С точки зрения производительности этого лучше не допускать. 54 | 55 | ### Журнал упреждающей записи (WAL) 56 | В журал упреждающей записи (Write Ahead Log, WAL) записывается информация, достаточная для повторного выполнения всех действия с базой данных. Записи этого журнала обязаны попасть на диск раньше, чем изменения в соответствующей странице. Тогда при восстановлении можно прочитать страницу с диска, посмотреть в ней номер последней записи WAL, и применить к странице все записи WAL, которые еще не были применены. Записью журнала занимается процесс WAL Writer. Можно сохранять записи журнала непосредственно при фиксации изменений. В таком случае гарантируется, что зафиксированные данные не пропадут при сбое. Второй вариант — сохранять записи асинхронно, что более эффективно. В этом случае некоторые зафиксированные данные могут пропасть, но согласованность все равно гарантируется. Журнал состоит из нескольких файлов (обычно по 16 МБ), которые циклически перезаписываются. Старые файлы могут сохраняться и архивироваться процессом WAL Archiver. Поскольку страница может долго не попадать на диск, возникает необходимость хранить неопределенно много журнальных записей. Чтобы избежать этого, периодически происходит так называемая контрольная точка, при которой все грязные буферы принудительно сбрасываются на диск. Этим занимается процесс Checkpointer. 57 | 58 | ### MVCC 59 | Идея многоверсионности (Muliversion Concurrency Control, MVCC) состоит в том, чтобы разделить два уровня представления данных. На нижнем уровне имеем дело со страницами и физическим хранением данных в них. На этом уровне при изменении строки таблицы хранятся несколько версий этой строки, как старые, так и текущая актуальная. При удалении строки она не удаляется физически из страницы, а помечается номером удалившей его транзакции. Новая строка помечается номером создавшей его транзакции. Обновление реализуется как удаление старой строки и вставка новой. Таким образом, в каждой версии строки хранится информация о начале и конце ее действия. На верхнем уровне имеем так называемые снимки данных. С помощью информации о начальном и конечном номере транзакции они отбирают версии строк, дающие согласованную картину на определенный момент времени. Транзакции работаю только со снимками данных. Они не имеют представления о физическом хранении и видят только те строки, которые предоставлены снимком. За счет этого достигается изоляция — каждая транзакция видит свою картину данных и не мешает другим, одновременно с ней работающим транзакциям. Преимущество многоверсионности перед реализациями, основанными только на блокировках, состоит в существенно большей эффективности, так как гарантируется, что блокируется только одновременное изменение одних и тех же строк. Но читатели никогда не блокируют писателей, а писатели — читателей. Старые версии строк, которые не видны ни одной из активных транзакций, должны быть физически удалены, чтобы освободить занимаемое ими место. Этим занимается процесс Autovacuum Launcher, запускающий для выполнения работы процессы Autovacuum Worker. Также очистку можно запустить вручную командой VACUUM. 60 | 61 | ### Изоляция 62 | --- 63 | **NOTE** 64 | 65 | Больше информации про изоляцию в PostgreSQL можно найти в разделе [transactions](https://github.com/yum-install-brains/postgresql-spot/blob/master/transactions/PostgreSQL_transactions.md). 66 | 67 | --- 68 | 69 | 70 | ### Логическая структура БД 71 | ![db_cluster.png](db_cluster.png) 72 | **Кластер** – группа баз данных, управляемых сервером PostgreSQL, находящаяся на одном хосте. 73 | 74 | **База данных** – коллекция объектов. Объект – структура данных, используемая для хранения или получения доступа к данным. В PostgreSQL объектами являются таблицы, индексы, последовательности, представления и т.д. 75 | 76 | У всех объектов есть OID (object identifier), представляющий собой 4-байтовый int. Чтобы сопоставить объект и его OID можно обратиться к системному каталогу: 77 | ```sql 78 | sampledb=# SELECT datname, oid FROM pg_database WHERE datname = 'sampledb'; 79 | datname | oid 80 | ----------+------- 81 | sampledb | 16384 82 | (1 row) 83 | 84 | sampledb=# SELECT relname, oid FROM pg_class WHERE relname = 'sampletbl'; 85 | relname | oid 86 | -----------+------- 87 | sampletbl | 18740 88 | (1 row) 89 | ``` 90 | 91 | ### Физическая структура 92 | ![db_structure.png](db_structure.png) 93 | На физическом уровне кластер БД представляет собой основной каталог (`$PGDATA`) и ряд подкаталогов и файлов. База данных – это подкаталог основного каталога и каждая таблица и индекс это как минимум один файл в этом подкаталоге. У каждого файла есть уникальный идентификатор – `relfilenode`. Таблицы и индексы как объекты БД идентифицируются с помощью `OID`, в то время как файлы таблиц и индексов идентифицируются с помощью `relfilenode`. Обычно, `OID` и `relfilenode` совпадают, но операции типа `TRUNCATE`, `REINDEX`, `CLUSTER` приводят к изменению `relfilenode`. Если размер файла таблицы или индекса превышает 1GB, создается дополнительный файл, называющийся `relfilenode.1`, затем `relfilenode.2` и так далее. 94 | 95 | Для определения `relfilenode` таблиц и индексов можно использовать функцию 96 | ```sql 97 | SELECT pg_relation_filepath('sampletbl'); 98 | pg_relation_filepath 99 | ---------------------- 100 | base/16384/18812 101 | ``` 102 | 103 | Файлы таблиц имеют дополнительные файлы, ассоциированные с ними. Они называются `relfilenode_fsm` (free space map) и `relfilenode_vm` (visibility map). Файлы таблиц имеют дополнительные файлы free space map, но не имеют visibility map. 104 | ```shell 105 | $ cd $PGDATA 106 | $ ls -la base/16384/18751* 107 | -rw------- 1 postgres postgres 8192 Apr 21 10:21 base/16384/18751 108 | -rw------- 1 postgres postgres 24576 Apr 21 10:18 base/16384/18751_fsm 109 | -rw------- 1 postgres postgres 8192 Apr 21 10:18 base/16384/18751_vm 110 | ``` 111 | 112 | --- 113 | **NOTE** 114 | 115 | Больше информации про физическое хранение данных в PostgreSQL можно найти в разделе [disk storage](https://github.com/yum-install-brains/postgresql-spot/blob/master/disk%20storage/postgresql_cheetsheets.md). 116 | 117 | --- 118 | 119 | #### Tablespace 120 | ![db_tablespace.png](db_tablespace.png) 121 | Tablespace – дополнительные данные, хранящиеся вне основного каталога. В основном каталоге хранятся только символьные ссылки на эти данные. 122 | 123 | При создании `tablespace` по указанному пути автоматически создается каталог `PG _ 'Major version' _ 'Catalogue version number'`. 124 | 125 | Например, при создании новой таблицы, принадлежащей БД, созданной в основном каталоге, сперва будет создан новый каталог под `tablespace`, в котором будут создан подкаталог с OID текущей БД и затем в нем создан файл таблицы. 126 | ```sql 127 | sampledb=# CREATE TABLE newtbl (.....) TABLESPACE new_tblspc; 128 | 129 | sampledb=# SELECT pg_relation_filepath('newtbl'); 130 | pg_relation_filepath 131 | ---------------------------------------------- 132 | pg_tblspc/16386/PG_9.4_201409291/16384/18894 133 | ``` 134 | 135 | #### Содержание PGDATA: 136 | 137 | | Элемент | Описание | 138 | | :------------- | :------------- | 139 | | PG_VERSION | Файл, содержащий номер основной версии Postgres Pro | 140 | | base | Подкаталог, содержащий подкаталоги для каждой базы данных | 141 | | current_logfiles | Файл, в котором отмечается, в какие файлы журналов производит запись сборщик сообщений | 142 | | global | Подкаталог, содержащий общие таблицы кластера, такие как pg_database | 143 | | pg_commit_ts | Подкаталог, содержащий данные о времени фиксации транзакций | 144 | | pg_dynshmem | Подкаталог, содержащий файлы, используемые подсистемой динамически разделяемой памяти | 145 | | pg_logical | Подкаталог, содержащий данные о состоянии для логического декодирования | 146 | | pg_multixact | Подкаталог, содержащий данные о состоянии мультитранзакций (используемые для разделяемой блокировки строк) | 147 | | pg_notify | Подкаталог, содержащий данные состояния прослушивания и нотификации (LISTEN/NOTIFY) | 148 | | pg_replslot | Подкаталог, содержащий данные слота репликации | 149 | | pg_serial | Подкаталог, содержащий информацию о выполненных сериализуемых транзакциях. | 150 | | pg_snapshots | Подкаталог, содержащий экспортированные снимки (snapshots) | 151 | | pg_stat | Подкаталог, содержащий постоянные файлы для подсистемы статистики. | 152 | | pg_stat_tmp | Подкаталог, содержащий временные файлы для подсистемы статистики | 153 | | pg_subtrans | Подкаталог, содержащий данные о состоянии подтранзакций | 154 | | pg_tblspc | Подкаталог, содержащий символические ссылки на табличные пространства | 155 | | pg_twophase | Подкаталог, содержащий файлы состояний для подготовленных транзакций | 156 | | pg_wal | Подкаталог, содержащий файлы WAL (журнал предзаписи) | 157 | | pg_xact | Подкаталог, содержащий данные о состоянии транзакции | 158 | | postgresql.auto.conf | Файл, используемый для хранения параметров конфигурации, которые устанавливаются при помощи ALTER SYSTEM | 159 | | postmaster.opts | Файл, содержащий параметры командной строки, с которыми сервер был запущен в последний раз | 160 | | postmaster.pid | Файл блокировки, содержащий идентификатор (ID) текущего управляющего процесса (PID), путь к каталогу данных кластера, временную метку запуска управляющего процесса, номер порта, путь к каталогу Unix-сокета (пустой для Windows), первый корректный адрес прослушивания (listen_address) (IP-адрес или *, либо пустое значение в случае отсутствия прослушивания по TCP), и ID сегмента разделяемой памяти (этот файл отсутствует после остановки сервера). | 161 | 162 | ### Источники 163 | - Документация и обучающие статьи postgrespro.ru 164 | - Материалы с сайта interdb.jp 165 | -------------------------------------------------------------------------------- /architecture/db_cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/architecture/db_cluster.png -------------------------------------------------------------------------------- /architecture/db_memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/architecture/db_memory.png -------------------------------------------------------------------------------- /architecture/db_processes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/architecture/db_processes.png -------------------------------------------------------------------------------- /architecture/db_structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/architecture/db_structure.png -------------------------------------------------------------------------------- /architecture/db_tablespace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/architecture/db_tablespace.png -------------------------------------------------------------------------------- /dangerous online operations/online_on_big_data.md: -------------------------------------------------------------------------------- 1 | ## Онлайн операции на больших данных 2 | ### Добавление ограничений CHECK и FOREIGN KEY на таблицу 3 | При добавлении ограничений на таблицу PostgreSQL берет `AccessExclusiveLock`. Этот лок гарантирует, что кроме транзакции, получившей эту блокировку, никакая другая транзакция не может обращаться к таблице каким-либо способом. Соответственно, все остальные транзакции повиснут до момента завершения транзакции с ограничением (для этого она должна просканировать всю таблицу). 4 | ```sql 5 | BEGIN; 6 | ALTER TABLE clients ADD CONSTRAINT clients_id_check CHECK (id > 0); 7 | SELECT locktype, mode FROM pg_locks WHERE pid = pg_backend_pid() AND relation = 'clients'::regclass; 8 | ┌──────────┬─────────────────────┐ 9 | │ locktype │ mode │ 10 | ├──────────┼─────────────────────┤ 11 | │ relation │ AccessExclusiveLock │ 12 | └──────────┴─────────────────────┘ 13 | COMMIT; 14 | ``` 15 | 16 | Но можно этого избежать и изменить уровень блокировки на менее строгий. Для этого сперва нужно добавить `NOT VALID` (проверка существующих в таблице данных не осуществляется, при новых операциях с таблицей проверка уже будет работать) ограничение и затем запустить операцию `VALIDATE`. На данный момент `NOT VALID` работает только с `CHECK` и `FOREIGN KEY`. В таком случае лок будет уже `ShareUpdateExclusiveLock` (блокирует `VACUUM`, создание индексов и многие операции `ALTER`) на время проверки и `AccessExclusiveLock` на время размещения самого ограничения. 17 | ```sql 18 | BEGIN; 19 | ALTER TABLE clients ADD CONSTRAINT clients_id_check CHECK (id > 0) NOT VALID; 20 | SELECT locktype, mode FROM pg_locks WHERE pid = pg_backend_pid() AND relation = 'clients'::regclass; 21 | ┌──────────┬─────────────────────┐ 22 | │ locktype │ mode │ 23 | ├──────────┼─────────────────────┤ 24 | │ relation │ AccessExclusiveLock │ 25 | └──────────┴─────────────────────┘ 26 | COMMIT; 27 | 28 | BEGIN; 29 | ALTER TABLE clients VALIDATE CONSTRAINT clients_id_check; 30 | SELECT locktype, mode FROM pg_locks WHERE pid = pg_backend_pid() AND relation = 'clients'::regclass; 31 | ┌──────────┬──────────────────────────┐ 32 | │ locktype │ mode │ 33 | ├──────────┼──────────────────────────┤ 34 | │ relation │ ShareUpdateExclusiveLock │ 35 | └──────────┴──────────────────────────┘ 36 | COMMIT; 37 | ``` 38 | 39 | ### Добавление нового поля в таблицу 40 | Операция происходит на уровне системного каталога и выполняется мгновенно. Тем не менее, на время выполнения операции берется `AccessExclusiveLock`, поэтому необходимо следить за блокировками. 41 | 42 | Особым случаем является добавление нового поля с `DEFAULT` значением. На больших объемах данных эта операция может занять продолжительное время, т.к. PostgreSQL должен записать дефолтное значение во все строки таблицы. Оба варианта операции ниже берут `AccessExclusiveLock` на все время выполнения операции: 43 | ```sql 44 | BEGIN; 45 | ALTER TABLE clients ADD COLUMN test_col text DEFAULT 'test'; 46 | SELECT locktype, mode FROM pg_locks WHERE pid = pg_backend_pid() AND relation = 'clients'::regclass; 47 | ┌──────────┬─────────────────────┐ 48 | │ locktype │ mode │ 49 | ├──────────┼─────────────────────┤ 50 | │ relation │ ShareLock │ 51 | │ relation │ AccessExclusiveLock │ 52 | └──────────┴─────────────────────┘ 53 | COMMIT; 54 | ``` 55 | ```sql 56 | BEGIN; 57 | ALTER TABLE clients ALTER COLUMN test_col SET DEFAULT 'text'; 58 | SELECT locktype, mode FROM pg_locks WHERE pid = pg_backend_pid() AND relation = 'clients'::regclass; 59 | ┌──────────┬─────────────────────┐ 60 | │ locktype │ mode │ 61 | ├──────────┼─────────────────────┤ 62 | │ relation │ AccessExclusiveLock │ 63 | └──────────┴─────────────────────┘ 64 | COMMIT; 65 | ``` 66 | Для того, чтобы не держать `AccessExclusiveLock` долго, лучше в отдельной транзакции добавить новое поле, затем командой `UPDATE` (с `ROW EXCLUSIVE` локом, который значительно слабее) заполнить его дефолтными значениями и после этого применить операцию `SET DEFAULT`, которая также пройдет быстро. 67 | 68 | --- 69 | **NOTE** 70 | 71 | PostgreSQL 11: 72 | В версии PostgreSQL 11 оптимизирована работа операции `DEFAULT`, теперь она происходит также быстро, как и добавление нового поля и выполняется на уровне системного каталога (верно для не `VOLATILE` значений). Подробнее в статье [Depesz](https://www.depesz.com/2018/04/04/waiting-for-postgresql-11-fast-alter-table-add-column-with-a-non-null-default/). 73 | 74 | Из документации: 75 | Добавление ограничений CHECK или NOT NULL влечёт за собой необходимость просканировать таблицу, чтобы проверить, что все существующие строки удовлетворяют ограничению, но перезаписывать таблицу при этом не требуется. 76 | 77 | --- 78 | 79 | ### Изменение типа поля 80 | Изменение типа поля может также надолго заблокироавть таблицу. В данном случае все зависит от изменяемого типа. Например, расширение `varchar` пройдет быстро (при этом, все равно придется перестраивать индексы). Но для типов, требующих перезаписи значения, придется изменить все значения поля в таблице. 81 | 82 | --- 83 | **NOTE** 84 | 85 | Из документации: 86 | Добавление столбца с предложением DEFAULT или изменение типа существующего столбца влечёт за собой перезапись всей таблицы и её индексов. Но возможно исключение при смене типа существующего столбца: если предложение USING не меняет содержимое столбца и старый тип двоично приводится к новому или является неограниченным доменом поверх нового типа, перезапись таблицы не требуется; хотя все индексы с затронутыми столбцами всё же требуется перестроить. При добавлении или удалении системного столбца oid также необходима перезапись всей таблицы. Для перестроения больших таблиц и/или их индексов может понадобиться довольно много времени и временно потребуется вдвое больше места на диске. 87 | 88 | --- 89 | 90 | ### Удаление поля 91 | Удаление поля происходит на уровне каталога и является быстрой операцией. Однако, надо помнить, что до выполнения `VACUUM FULL` высвобожденное место не возвращается системе. 92 | 93 | --- 94 | **NOTE** 95 | 96 | Из документации: 97 | Форма DROP COLUMN не удаляет столбец физически, а просто делает его невидимым для операций SQL. При последующих операциях добавления или изменения в этот столбец будет записываться значение NULL. Таким образом, удаление столбца выполняется быстро, но при этом размер таблицы на диске не уменьшается, так как пространство, занимаемое удалённым столбцом, не высвобождается. Это пространство будет освобождено со временем, по мере изменения существующих строк. (При удалении системного столбца oid это поведение не наблюдается, так как немедленно выполняется перезапись таблицы.) 98 | 99 | Чтобы принудительно высвободить пространство, занимаемое столбцом, который был удалён, можно выполнить одну из форм ALTER TABLE, производящих перезапись всей таблицы. В результате все строки будут воссозданы так, что в удалённом столбце будет содержаться NULL. 100 | 101 | --- 102 | 103 | ### pg_repack 104 | pg_repack – утилита, которая позволяет проводить операции `VACUUM FULL`, `CLUSTER`, `REINDEX` и `SET TABLESPACE` в онлайне. При этом нужно помнить, что pg_repack требует примерно x2 таблицы + всех ее индексов свободного места для выполнения. 105 | 106 | Подробнее про механизм работы pg_repack можно прочитать на [официальной странице расширения](http://reorg.github.io/pg_repack/) в разделе Details. 107 | 108 | ### Источники 109 | - [Блог](https://blog.2ndquadrant.com/how-to-check-the-lock-level-taken-by-operations-in-postgresql/) 2ndquadrant.com 110 | - [Блог](https://leopard.in.ua/2016/09/20/safe-and-unsafe-operations-postgresql#.Wz963JL4ksl) leopard.in.ua 111 | - [Документация](https://www.postgresql.org/docs/current/static/explicit-locking.html) PostgreSQL 112 | -------------------------------------------------------------------------------- /dba scripts/PostgreSQL_DBA.md: -------------------------------------------------------------------------------- 1 | ## Полезные запросы 2 | - Вставляем метаданные Liquibase 3 | ```sql 4 | delete from databasechangelog 5 | where id = 'JIRA-000' and author = 'yum_install_brains' and filename = 'dir/file.xml'; 6 | -- 7 | insert into databasechangelog (id,author,filename,dateexecuted,orderexecuted,exectype,md5sum,description,comments,tag,liquibase) 8 | values ('JIRA-000','yum_install_brains','dir/file.xml',now(),1,'EXECUTED',null,'sql','',null,'3.5.3'); 9 | ``` 10 | 11 | - Получаем лог выполнения pgagent джобов 12 | ```sql 13 | select * 14 | from pgagent.pga_joblog 15 | where jlgjobid 16 | in (select jobid 17 | from pgagent.pga_job 18 | where jobname = 'job_name') 19 | order by jlgstart desc 20 | limit 1; 21 | ``` 22 | 23 | - Какие и кому привелегии выданы на таблицу 24 | ```sql 25 | SELECT grantee, privilege_type 26 | FROM information_schema.role_table_grants 27 | WHERE table_name='pgbench_accounts'; 28 | ┌───────────┬────────────────┐ 29 | │ grantee │ privilege_type │ 30 | ├───────────┼────────────────┤ 31 | │ postgres │ INSERT │ 32 | │ postgres │ SELECT │ 33 | │ postgres │ UPDATE │ 34 | │ postgres │ DELETE │ 35 | │ postgres │ TRUNCATE │ 36 | │ postgres │ REFERENCES │ 37 | │ postgres │ TRIGGER │ 38 | │ test_user │ INSERT │ 39 | │ test_user │ SELECT │ 40 | └───────────┴────────────────┘ 41 | ``` 42 | 43 | - COPY шаблон 44 | ```sql 45 | COPY ( 46 | sql_text 47 | ) 48 | TO '/dir/copy.csv' WITH (format csv, header true, delimiter ';', null 'null', encoding 'WIN1251'); 49 | ``` 50 | 51 | - Мониторинг блокирующих запросов 52 | ```sql 53 | SELECT relation::regclass, * FROM pg_locks WHERE NOT GRANTED; 54 | ``` 55 | 56 | - Удаляем все символы переноса строки из текстового поля (для корректной выгрузки в Excel) 57 | ```sql 58 | select regexp_replace(field, E'[\\n\\r]+', ' ', 'g' ) 59 | ``` 60 | 61 | - Обнуление статистики pg_stat_statements 62 | ```sql 63 | select pg_stat_statements_reset(); 64 | ``` 65 | 66 | - Получение пути к файлу таблицы 67 | ```sql 68 | select pg_relation_filepath('pgbench_accounts'); 69 | pg_relation_filepath 70 | -- 71 | base/12994/16504 72 | ``` 73 | 74 | - Получение имени таблицы по OID tablespace + OID таблицы 75 | ```sql 76 | select pg_filenode_relation(0, 16504); 77 | pg_filenode_relation 78 | -- 79 | pgbench_accounts 80 | ``` 81 | 82 | - Читаем конфигурационный файл (или любой другой) из директории PGDATA или PGLOG 83 | ```sql 84 | select pg_read_file('postgresql.conf'); 85 | ``` 86 | 87 | - Создание внешнего сервера и пользовательских маппингов 88 | ```sql 89 | DROP SERVER IF EXISTS foreign_db CASCADE; 90 | CREATE SERVER foreign_db FOREIGN DATA WRAPPER dblink_fdw OPTIONS (host 'host-name', dbname 'foreign_db'); 91 | CREATE USER MAPPING FOR user_name SERVER foreign_db OPTIONS (USER 'foreign_user', PASSWORD 'foreign_pass'); 92 | GRANT USAGE ON FOREIGN SERVER foreign_db TO user_name; 93 | ``` 94 | 95 | - Получаем текст функции 96 | ```sql 97 | select pg_get_functiondef(select oid from pg_proc where proname = 'func_name'); 98 | ``` 99 | 100 | - Множественные условия в LIKE запросе 101 | ```sql 102 | SELECT * FROM test_table 103 | WHERE attr_name LIKE 104 | ANY (ARRAY['foo%', '%bar%', 'ba z.', 'daz%']) 105 | ``` 106 | 107 | - Создание таблицы с использованием INTO 108 | ```sql 109 | SELECT attr1, attr2, attr3 110 | INTO new_table 111 | FROM old_table; 112 | ``` 113 | 114 | - Скрипт пересоздания первичного ключа "на лету" 115 | ```sql 116 | BEGIN; 117 | CREATE UNIQUE INDEX CONCURRENTLY tab_pkey_idx2 ON tab(id); 118 | ALTER TABLE tab 119 | DROP CONSTRAINT tab_pkey CASCADE, 120 | ADD CONSTRAINT tab_pkey PRIMARY KEY USING INDEX tab_pkey_idx2; 121 | ALTER TABLE second_tab 122 | ADD CONSTRAINT second_tab_fkey FOREIGN KEY (tab_id) REFERENCES tab(id) NOT VALID; 123 | COMMIT; 124 | ``` 125 | 126 | - Распаковываем массивы в таблицы и проставляем каждой строке номер 127 | ```sql 128 | SELECT * 129 | FROM unnest('{1,2,3}'::int[], '{4,5,6,7}'::int[]) 130 | WITH ORDINALITY AS t(a1, a2, num) 131 | ORDER BY t.num DESC; 132 | a1 | a2 | num 133 | ------+----+----- 134 | null | 7 | 4 135 | 3 | 6 | 3 136 | 2 | 5 | 2 137 | 1 | 4 | 1 138 | ``` 139 | 140 | - Убиваем подключения к БД 141 | ```sql 142 | select pg_terminate_backend(pid) 143 | from pg_stat_activity 144 | where pid <> pg_backend_pid(); 145 | ``` 146 | 147 | - Создаем JSON 148 | ```sql 149 | select 150 | row_to_json(t1) 151 | from ( 152 | select 153 | 'joe' as username, 154 | (select project from (values(1, 'prj1')) as project(project_id, project_name)) as project 155 | ) t1; 156 | -- 157 | row_to_json 158 | {"username":"joe","project":{"project_id":1,"project_name":"prj1"}} 159 | ``` 160 | 161 | - Действия с массивами 162 | ```sql 163 | WITH my_table (ID, year, any_val) AS ( 164 | VALUES (1, 2017,56) 165 | ,(2, 2017,67) 166 | ,(3, 2017,12) 167 | ,(4, 2017,30) 168 | ,(5, 2020,8) 169 | ,(6, 2030,17) 170 | ,(7, 2030,50) 171 | ) 172 | SELECT year 173 | ,array_agg(any_val) -- собираю данные (по каждому году) в массив 174 | ,array_agg(any_val ORDER BY any_val) AS sort_array_agg -- порядок элементов можно отсортировать (с 9+ версии Postgres) 175 | ,array_to_string(array_agg(any_val),';') -- преобразовываю массив в строку 176 | ,ARRAY['This', 'is', 'my' , 'array'] AS my_simple_array -- способ создания массива 177 | FROM my_table 178 | GROUP BY year; -- группируем данные по каждому году 179 | -- 180 | year | array_agg | sort_array_agg | array_to_string | my_simple_array 181 | ------+---------------+----------------+-----------------+-------------------- 182 | 2017 | {56,67,12,30} | {12,30,56,67} | 56;67;12;30 | {This,is,my,array} 183 | 2020 | {8} | {8} | 8 | {This,is,my,array} 184 | 2030 | {17,50} | {17,50} | 17;50 | {This,is,my,array} 185 | ``` 186 | 187 | ## Немного bash 188 | - Создание дампа схемы (без данных) 189 | ``` 190 | pg_dump -t 'schema_name.table_name' --schema-only database_name 191 | ``` 192 | 193 | - Восстановление из дампа 194 | ``` 195 | psql -d database_name < table.dump 196 | ``` 197 | или 198 | ``` 199 | pg_restore --table=table_name table.dump 200 | ``` 201 | 202 | - Мониторинг размера темповых файлов 203 | ``` 204 | watch df -h /dir/PG_version_oid/pgsql_tmp 205 | ``` 206 | 207 | - Флаг для остановки группы скриптов при ошибке в одном из скриптов 208 | ``` 209 | psql -v ON_ERROR_STOP=1 210 | ``` 211 | или 212 | ``` 213 | \set ON_ERROR_STOP on 214 | ``` 215 | 216 | - Информация по объектам и их правам 217 | ``` 218 | \dfS+ 219 | ``` 220 | или 221 | ``` 222 | \dtS+ 223 | ``` 224 | 225 | - Выводим сумму в байтах темповых файлов, сгенеренных в логе 226 | ``` 227 | cat postgresql_file.log | grep "LOG: temporary file:" > temps.txt && awk '{s+=$15}END{print s}' temps.txt 228 | ``` 229 | 230 | - Массовое выполнение скриптов по цифровому порядку в их именах (1.sql, 2.sql, 3.sql) 231 | ``` 232 | for i in $(ls -1 /dir/*.sql|sort -n) ;do psql -d database_name -v ON_ERROR_STOP=ON -f $i ;done 233 | ``` 234 | 235 | ## Напоминалки 236 | - порядок выполнения операций в запросе SELECT 237 | ```sql 238 | FROM 239 | WHERE 240 | GROUP BY 241 | HAVING 242 | SELECT 243 | ORDER BY 244 | ``` 245 | 246 | - Drop column на самом деле не удаляет данные в колонке и колонку 247 | The DROP COLUMN form does not physically remove the column, but simply makes it invisible to SQL operations. 248 | Subsequent insert and update operations in the table will store a null value for the column. Thus, dropping a column is quick but it will not immediately reduce 249 | the on-disk size of your table, as the space occupied by the dropped column is not reclaimed. The space will be reclaimed over time as existing rows are updated. 250 | 251 | To force an immediate rewrite of the table, you can use VACUUM FULL, CLUSTER or one of the forms of ALTER TABLE that forces a rewrite. 252 | This results in no semantically-visible change in the table, but gets rid of no-longer-useful data. 253 | 254 | - Использование лидирующих % в запросах LIKE 255 | For this you need to install pg_trgm and create GIN/GIST index on field. 256 | https://stackoverflow.com/questions/1566717/postgresql-like-query-performance-variations 257 | -------------------------------------------------------------------------------- /dba scripts/sql_tricks.md: -------------------------------------------------------------------------------- 1 | ## Приемы разной степени сомнительности и полезности 2 | 3 | ### Временные переменные в рамках транзакции 4 | Значение `true` в `set_config` говорит о том, что переменная устанавливается локально (в рамках жизни транзакции). 5 | ```sql 6 | BEGIN; 7 | 8 | SELECT set_config('test.test_var', (SELECT col FROM some_table LIMIT 1), True); 9 | SELECT set_config('test.another_test_var', (SELECT 'some value'), True); 10 | SELECT current_setting('test.test_var'); 11 | current_setting 12 | ----------------- 13 | a 14 | SELECT current_setting('test.another_test_var'); 15 | current_setting 16 | ----------------- 17 | some value 18 | 19 | COMMIT; 20 | SELECT current_setting('test.test_var'); 21 | current_setting 22 | ----------------- 23 | 24 | (1 row) 25 | SELECT current_setting('test.another_test_var'); 26 | current_setting 27 | ----------------- 28 | 29 | (1 row) 30 | ``` 31 | 32 | ### Использование переменных (decorated literal expression) в psql 33 | ```sql 34 | \set start '2017-02-01' 35 | SELECT date:'start'; 36 | date 37 | ------------ 38 | 2017-02-01 39 | (1 row) 40 | SELECT date_col 41 | FROM test_table 42 | WHERE date_col >= date :'start' 43 | AND date_col < date :'start' + interval '1 month'; 44 | ``` 45 | 46 | ### ALTER типа поля вместе с нормализацией значений 47 | ```sql 48 | ALTER TABLE test_table 49 | ALTER col_one 50 | type bigint 51 | using replace(col_one, ',', '')::bigint, 52 | 53 | ALTER col_two 54 | type bigint 55 | using replace(col_two, ',', '')::bigint, 56 | 57 | ALTER col_three 58 | type bigint 59 | using substring(replace(col_three, ',', '') from 2)::numeric; 60 | ``` 61 | -------------------------------------------------------------------------------- /disk storage/data_access.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/disk storage/data_access.png -------------------------------------------------------------------------------- /disk storage/data_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/disk storage/data_read.png -------------------------------------------------------------------------------- /disk storage/heap_file_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/disk storage/heap_file_page.png -------------------------------------------------------------------------------- /disk storage/postgresql_cheetsheets.md: -------------------------------------------------------------------------------- 1 | ## Физическое устройство данных PostgreSQL 2 | 3 | #### Ссылки 4 | [Физическое хранилище в PostgreSQL](http://rachbelaid.com/introduction-to-postgres-physical-storage/) 5 | 6 | #### Термины 7 | filenode - id, представляющий ссылку на таблицу или индекс 8 | page - 8кб сегмент информации в файле, хранящем табличные данные 9 | CTID - физическое расположение версии строки в таблице 10 | 11 | #### Базы данных 12 | Данные хранятся в PGDATA. Для каждой БД в кластере создается отдельная директория в PGDATA/base по имени OID БД в pg_database. Файлы базы данных по умолчанию хранятся в этой директории. 13 | 14 | Чтобы получить OID БД по имени, можно выполнить запрос: 15 | ```sql 16 | select oid, datname from pg_database; 17 | 18 | oid | datname 19 | -------+------------- 20 | 1 | template1 21 | 12398 | template0 22 | 12403 | postgres 23 | 17447 | foo 24 | ``` 25 | Или можо воспользоваться пакетом oid2name в консоли: 26 | ```sh 27 | $> oid2name 28 | All databases: 29 | Oid Database Name Tablespace 30 | ---------------------------------- 31 | 17447 foo pg_default 32 | 12398 template0 pg_default 33 | 1 template1 pg_default 34 | ``` 35 | Файлы служебных таблиц (например, pg_database) хранятся в директории PGDATA/global. 36 | 37 | #### Таблицы 38 | Каждая таблица хранится в отдельном файле. Эти файлы называются по именам соответствующих filenode. Чтобы определить filenode таблицы по ее имени, можно выполнить запрос 39 | 40 | select pg_relation_filepath('bar'); 41 | pg_relation_filepath 42 | ---------------------- 43 | base/17447/27741 44 | 45 | или 46 | 47 | > oid2name -d foo -t bar 48 | 49 | From database "foo": 50 | Filenode Table Name 51 | ---------------------- 52 | 27741 bar 53 | 54 | Когда таблица превышает в размере 1 гигабайт, она разделяется на файлы по 1 гигабайту каждый. Имя первого файла будет совпадать с filenode таблицы, имя второго файла будет filenode.1, третьего filenode.2 и так далее. 55 | 56 | 57 | #### Строки 58 | ##### Структура 59 | ![table_file.png](table_file.png) 60 | ![heap_file_page](heap_file_page.png) 61 | Таблица хранится в виде массива страниц фиксированного размера (по умолчанию 8кб). Каждая страница внутри себя содержит коллекцию элементов (строк, хранящихся на странице). Заголовки страниц содержат метаданные, такие как начало свободного места, конец свободного места и тд. Элементы представляют собой массив указателей, ссылающихся на реальные строки (tupples). 62 | 63 | ##### CTID 64 | CTID является указателем на элемент и содержит номер страницы и номер элемента в массиве элементов на этой странице. CTID является постоянным, пока запись не обновлена или удалена. Vacuum не меняет CTID (хорошо бы проверить). 65 | 66 | ##### TOAST 67 | Одна строка всегда находится в пределах одной страницы (т.е. все поля строки находятся на одной странице и не могут продолжаться на другой странице). Но при этом сами значения в полях таблиы не ограничены размером 8кб. Это возможно благодаря тому, что большие значения обрабатываются TOAST(The Oversized-Attribute Storage Technique) механизмом. 68 | 69 | Каждая таблица имеет отдельную TOAST таблицу (с именем pg _ toast_(filenode). Большие значения, не помещающиеся в одну страницу, разибиваются части по 2кб и хранятся по частям в TOAST таблице. 70 | 71 | ##### Доступ к данным 72 | ![data_access](data_access.png) 73 | Допустим, файл таблицы содержит одну страницу и в ней записана только одна строка. В таком случае, при вставке новой строки она будет размещена последовательно после первой строки. При этом указатель pd_lower начнет ссылаться на следующий "line pointer" (указатель на строку, на картинке отмечен красным прямоугольником)и pd_upper начнет ссылаться на следующую строку (на картинке зеленый прямоугольник). 74 | 75 | ![data_read](data_read.png) 76 | Самыми распространенными методами доступа к данным являются 77 | - последовательный доступ; 78 | - индексный b-tree доступ. 79 | 80 | В первом случае, все строки во всех страницах будут последовательно считаны путем сканирования "line pointers" каждой страницы. 81 | 82 | В случае индексного b-tree доступа, сперва будет найдена запись в индексе (состоящем из пар ключ (значение предиката)- значение (TID или другими словами ссылка на "line pointer")), затем по этой записи произведено обращение к нужной странице файла таблицы. 83 | 84 | 85 | ##### FSM (free space map) 86 | ![alt text](https://www.postgresql.org/message-id/attachment/23510/fsm-drawing.png) 87 | Каждая таблица имеет FSM файл ((filenode)_ fsm), который хранит информацию о свободном месте, доступном в таблице. Информация в этом файле обновляется такими операциями, как VACUUM и вообще говоря может быть не абсолютно точной. FSM показывает, какие элементы были удалены или обновлены и их место может быть повторно переиспользовано. Данные в FSM организованы в виде дерева (в листьях хранится число свободных элементов на странице), чтобы можно было быстро найти страницу с требуемым числом свободных элементов. 88 | 89 | Полезно знать, что VACUUM перезаписывает страницы, содержащие мертвые строки так, чтобы не допустить фрагментацию свободного места на странице. Место, высвобожденное VACUUM, не возвращается в систему, а остается зарезервировано для последующей вставки элементов. 90 | 91 | ##### VM (visibility map) 92 | ![alt text](http://www.interdb.jp/pg/img/fig-6-02.png) 93 | Каждая таблица имеет VM файл ((filenode)_ vm) для хранения информации о страницах (в виде битовой карты), которые содержат только строки, видимые всем транзакциям. 94 | 95 | VM оптимизирует работу VACUUM, помечая некоторые страницы "полностью видимыми" (проставлена единица в битовой карте). В такие страницы VACUUM не заходит при сканировании таблицы. Бит видимости проставляется при выполнении VACUUM и снимается при выполнении операций INSERT, UPDATE, DELETE. 96 | 97 | Стоит отметить, что это не отменяет необходимость сканировать все индексы. 98 | 99 | #### Источники 100 | - Документация и обучающие статьи postgrespro.ru 101 | - Материалы с сайта interdb.jp 102 | -------------------------------------------------------------------------------- /disk storage/table_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/disk storage/table_file.png -------------------------------------------------------------------------------- /indexes/reindex.md: -------------------------------------------------------------------------------- 1 | ## Reindex 2 | 3 | ``` 4 | REINDEX [ ( VERBOSE ) ] { INDEX | TABLE | SCHEMA | DATABASE | SYSTEM } имя 5 | ``` 6 | 7 | `REINDEX` с параметром `SYSTEM` перестраивает все индексы в системных каталогах текущей базы данных. При этом обрабатываются также индексы в общих системных каталогах, но индексы в таблицах пользователя не затрагиваются. Эту форму `REINDEX` нельзя выполнить в блоке транзакции. 8 | 9 | `REINDEX` перестраивает индекс, обрабатывая данные таблицы, к которой относится индекс, и в результате заменяет старую копию индекса. Команда `REINDEX` применяется в следующих ситуациях: 10 | - Индекс был повреждён, его содержимое стало некорректным. Хотя в теории этого не должно случаться, на практике индексы могут испортиться из-за программных ошибок или аппаратных сбоев. В таких случаях `REINDEX` служит методом восстановления индекса. 11 | - Индекс стал «раздутым», то есть в нём оказалось много пустых или почти пустых страниц. Это может происходить с B-деревьями в PostgreSQL при определённых, достаточно редких сценариях использования. `REINDEX` даёт возможность сократить объём, занимаемый индексом, записывая новую версию индекса без «мёртвых» страниц. 12 | - Параметр хранения индекса (например, фактор заполнения) был изменён, и теперь требуется, чтобы это изменение вступило в силу в полной мере. 13 | - Построение индекса с параметром `CONCURRENTLY` завершилось ошибкой, в результате чего индекс оказался «нерабочим». Такие индексы бесполезны, но команда `REINDEX` предоставляет удобный способ перестроить их. Однако заметьте, что `REINDEX` перестраивает индекс не в параллельном режиме. Чтобы перестроить такой индекс, минимизируя влияние на производственную среду, его следует удалить, а затем снова выполнить команду `CREATE INDEX CONCURRENTLY`. 14 | 15 | 16 | На высоконагруженных базах имеет смысл создавать новый аналогичный индекс конкурентно, а затем удалять старый индекс. Можно также использовать команду `REINDEX`, но она будет сопровождаться исключительной блокировки таблицы. Важно отметить, что не все типы индексов поддерживают возможность конкурентного создания. 17 | 18 | Когда индекс используется для обеспечения уникальности или других ограничений, может потребоваться команда `ALTER TABLE`, чтобы поменять существующее ограничение на то, что обеспечивает новый индекс. Обстоятельно продумайте эту многоходовую процедуру, прежде чем выполнять её, так как не все индексы можно перестроить таким образом, и предусмотрите обработку ошибок. 19 | 20 | Для переиндексации всей базы данных, схемы, отдельной таблицы или индекса можно также воспользоваться консольной утилитой `reindexdb`. 21 | 22 | 23 | ### Уровни блокировок 24 | Действие `REINDEX` подобно удалению и пересозданию индекса в том смысле, что содержимое индекса пересоздаётся с нуля, но блокировки при этом устанавливаются другие. `REINDEX` блокирует запись, но не чтение родительской таблицы индекса. Эта команда также устанавливает исключительную блокировку для обрабатываемого индекса, что блокирует чтение таблицы, при котором задействуется этот индекс. DROP INDEX, напротив, моментально устанавливает исключительную блокировку на родительскую таблицу, блокируя и запись, и чтение. Последующая команда CREATE INDEX блокирует запись, но не чтение; так как индекс отсутствует, обращений к нему ни при каком чтении не будет, что означает, что блокироваться чтение не будет, но выполняться оно будет как дорогостоящее последовательное сканирование. 25 | 26 | ### Восстановление системных индексов 27 | Всё усложняется, если возникает необходимость восстановить повреждённый индекс системной таблицы. В этом случае важно, чтобы система сама не использовала этот индекс. (На самом деле в таких случаях вы, скорее всего, столкнётесь с падением процессов сервера в момент запуска, как раз вследствие испорченных индексов.) Чтобы надёжно восстановить рабочее состояние, сервер следует запускать с параметром -P, который отключает использование индексов при поиске в системных каталогах. 28 | 29 | Один из вариантов сделать это — выключить сервер PostgreSQL и запустить его снова в однопользовательском режиме, с параметром -P в командной строке. Затем можно выполнить `REINDEX` DATABASE`, `REINDEX SYSTEM`, `REINDEX TABLE` или `REINDEX INDEX`, в зависимости от того, что вы хотите восстановить. 30 | 31 | ``` 32 | $ export PGOPTIONS="-P" 33 | $ psql broken_db 34 | ... 35 | broken_db=> REINDEX DATABASE broken_db; 36 | broken_db=> \q 37 | ``` 38 | 39 | ### Источники 40 | - [Документация](https://postgrespro.ru/docs/postgrespro/10/sql-reindex) postgrespro.ru 41 | -------------------------------------------------------------------------------- /kernel resources/huge_pages.md: -------------------------------------------------------------------------------- 1 | ## Huge pages 2 | 3 | Huge pages есть смысл включать в тех случаях, когда под PostgreSQL выделены десятки и сотни гигабайт оперативной памяти. Они помогают сократить накладные расходы на адресацию памяти и поддержание страничных таблиц. 4 | 5 | ### Включаем huge pages 6 | Сперва проверим, сколько нам нужно huge pages: 7 | ``` 8 | # определяем PID postmaster'а 9 | head -1 $PGDATA/postmaster.pid 10 | 1640 11 | 12 | # определяем пик выделенной оперативной памяти 13 | grep ^VmPeak /proc/1640/status 14 | VmPeak: 344340 kB 15 | ``` 16 | 17 | По умолчанию 1 huge page имеет размер 2048 kB. Таким образом нам нужно 344340/2048 = 169 больших страниц. Зададим это значение в файле `/etc/sysctl.conf`. 18 | 19 | Далее отключим transparent huge pages – механизм, который автоматически выделяет huge pages по запросу. Transparent huge pages может приводить к деградации системы из-за постепеннной фрагментации памяти. 20 | ``` 21 | echo never > /sys/kernel/mm/transparent_hugepage/enabled 22 | ``` 23 | 24 | 25 | Теперь проверим, сколько у нас доступно huge pages: 26 | ``` 27 | cat /proc/meminfo | grep -i huge 28 | AnonHugePages: 4096 kB 29 | HugePages_Total: 170 30 | HugePages_Free: 170 31 | HugePages_Rsvd: 0 32 | HugePages_Surp: 0 33 | Hugepagesize: 2048 kB 34 | ``` 35 | 36 | В PostgreSQL за использование huge pages отвечает параметр `huge_page = [off|on|try]`. Установим значение этого параметра в on и запустим кластер БД. 37 | 38 | ### Ошибки 39 | ``` 40 | FATAL: could not map anonymous shared memory: Cannot allocate memory 41 | HINT: This error usually means that PostgreSQL's request for a shared memory segment exceeded available memory, swap space or huge pages. To reduce the request size (currently 148324352 bytes), reduce PostgreSQL's shared memory usage, perhaps by reducing shared_buffers or max_connections. 42 | ``` 43 | Если при старте появляется такая ошибка, значит скорее всего процессу не хватило свободных больших страниц (проверить HugePages_Free). Если при установке параметра `huge_pages=off` проблема уходит, то дело именно в этом. 44 | 45 | ### Источники 46 | - [Блог](https://blog.dbi-services.com/configuring-huge-pages-for-your-postgresql-instance-redhatcentos-version/) DBI; 47 | - [Статья](https://habr.com/post/228793/) Алексея Лесовского на хабре 48 | -------------------------------------------------------------------------------- /locks/PostgreSQL_locking.md: -------------------------------------------------------------------------------- 1 | ## Explicit locking 2 | https://postgrespro.ru/docs/postgrespro/10/explicit-locking 3 | #### Режимы блокировки на уровне таблицы 4 | Разные транзакции могут в любой момент времени владеть блокировками неконфликтующих типов, но не могут владеть блокировками конфликтующих типов на одну и ту же таблицу. 5 | 6 | | Режим | С кем конфликтует| Пример операции | 7 | | :------------- | :------------- |:------------- | 8 | | ACCESS SHARE | ACCESS EXCLUSIVE |SELECT | 9 | | ROW SHARE | EXCLUSIVE, ACCESS EXCLUSIVE |SELECT FOR UPDATE, SELECT FOR SHARE | 10 | | ROW EXCLUSIVE | SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |SELECT | 11 | | SHARE UPDATE EXCLUSIVE | SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE | VACUUM, ANALYZE, CREATE INDEX CONCURRENTLY, CREATE STATISTICS, ALTER TABLE VALIDATE | 12 | | SHARE | ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |CREATE INDEX | 13 | | SHARE ROW EXCLUSIVE | ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE |CREATE COLLATION, CREATE TRIGGER, многие формы ALTER TABLE | 14 | | EXCLUSIVE | ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE (т.е. совместим только с чтением таблицы) | REFRESH MATERIALIZED VIEW CONCURRENTLY | 15 | | ACCESS EXCLUSIVE | Конфликтует со всеми режимами блокировок и сам с собой | DROP TABLE, TRUNCATE, REINDEX, CLUSTER, VACUUM FULL, REFRESH MATERIALIZED VIEW, многие формы ALTER TABLE | 16 | 17 | #### Режимы блокировки на уровне строки 18 | | Режим | С кем конфликтует| Пример операции | 19 | | :------------- | :------------- |:------------- | 20 | | FOR UPDATE | Конфликтует со всеми режимам и сам с собой. Конфликтует с операциями UPDATE, DELETE, SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE или SELECT FOR KEY SHARE |SELECT FOR UPDATE | 21 | | FOR NO KEY UPDATE | FOR SHARE, FOR NO KEY UPDATE, FOR UPDATE | Запрашивается любой командой UPDATE, которая не требует блокировки FOR UPDATE. | 22 | | FOR SHARE | Не позволяет другим транзакциям выполнять с этими строками UPDATE, DELETE, SELECT FOR UPDATE или SELECT FOR NO KEY UPDATE, но допускает SELECT FOR SHARE и SELECT FOR KEY SHARE. Конфликтует с режимами FOR NO KEY UPDATE, FOR UPDATE | SELECT FOR SHARE и SELECT FOR KEY SHARE | 23 | | FOR KEY SHARE | Блокируется SELECT FOR UPDATE, но не SELECT FOR NO KEY UPDATE. не позволяет другим транзакциям выполнять команды DELETE и UPDATE, только если они меняют значение ключа (но не другие UPDATE), и при этом допускает выполнение команд SELECT FOR NO KEY UPDATE, SELECT FOR SHARE и SELECT FOR KEY SHARE. | SELECT FOR NO KEY UPDATE, SELECT FOR SHARE и SELECT FOR KEY SHARE | 24 | -------------------------------------------------------------------------------- /logging/PostgreSQL_logging.md: -------------------------------------------------------------------------------- 1 | ## Логирование 2 | Журнал сервера базы данных желательно сохранять где-либо, а не просто сбрасывать его в /dev/null. Этот журнал бесценен при диагностике проблем. Однако он может быть очень объёмным (особенно при высоких уровнях отладки), так что хранить его неограниченно долго вы вряд ли захотите. Поэтому необходимо организовать ротацию журнальных файлов так, чтобы новые файлы создавались, а старые удалялись через разумный промежуток времени. 3 | 4 | ### Куда записываем логи 5 | `log_destination (string)`: 6 | PostgreSQL поддерживает несколько методов протоколирования сообщений сервера: stderr, csvlog и syslog. На Windows также поддерживается eventlog. По умолчанию используется stderr. csvlog может быть полезен для машинной обработки логов и загрузки их в базу данных. syslog может не записывать некоторые сообщения, поэтому дальше не рассматривается. Можно задавать сразу несколько методов через запятую. 7 | 8 | Если присутствует указание stderr или csvlog, создаётся файл current_logfiles, в который записывается расположение файла(ов) журнала, в настоящее время используемого сборщиком сообщений для соответствующего назначения. 9 | 10 | `logging_collector (boolean)`: Параметр включает сборщик сообщений (logging collector). Это фоновый процесс, который собирает отправленные в stderr сообщения и перенаправляет их в журнальные файлы. 11 | 12 | | Параметр | Описание | 13 | | :------------- | :------------- | 14 | | log_directory (string) | При включённом logging_collector, определяет каталог, в котором создаются журнальные файлы. Можно задавать как абсолютный путь, так и относительный от каталога данных кластера. | 15 | | log_filename (string) | При включённом logging_collector задаёт имена журнальных файлов. Можно использовать спецификаторы % для включения в имена файлов информации о дате и времени (например postgresql-%Y-%m-%d_%H%M%S.log). | 16 | 17 | ### Ротация логов 18 | `log_truncate_on_rotation (boolean)`: Если параметр logging_collector включён, PostgreSQL будет перезаписывать существующие журнальные файлы, а не дописывать в них. Однако, перезапись при переключении на новый файл возможна только в результате ротации по времени, но не при старте сервера или ротации по размеру файла. При выключенном параметре всегда продолжается запись в существующий файл. Например, включение этого параметра в комбинации с log_filename равным postgresql-%H.log, приведёт к генерации 24-х часовых журнальных файлов, которые циклически перезаписываются. Параметр можно задать только в конфигурационных файлах или в командной строке при запуске сервера. 19 | 20 | **Пример**: для хранения журнальных файлов в течение 7 дней, по одному файлу на каждый день с именами вида server_log.Mon, server_log.Tue и т. д., а также с автоматической перезаписью файлов прошлой недели, нужно установить log_filename в server_log.%a, log_truncate_on_rotation в on и log_rotation_age в 1440. 21 | 22 | | Параметр | Описание | 23 | | :------------- | :------------- | 24 | | log_rotation_age (integer) | При включённом logging_collector определяет максимальное время жизни отдельного журнального файла в минутах. После того как прошло заданное количество минут, создаётся новый журнальный файл. | 25 | | log_rotation_size (integer) | Тоже самое, только в килобайтах. | 26 | 27 | ### Когда записываем в логи 28 | | Параметр | Описание | 29 | | :------------- | :------------- | 30 | | log_min_messages (enum) | DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL и PANIC. Каждый из перечисленных уровней включает все идущие после него. | 31 | | log_min_error_statement (enum) | DEBUG5, DEBUG4, DEBUG3, DEBUG2, DEBUG1, INFO, NOTICE, WARNING, ERROR, LOG, FATAL и PANIC. Управляет тем, какие SQL-операторы, завершившиеся ошибкой, записываются в журнал сервера. SQL-оператор будет записан в журнал, если он завершится ошибкой с указанным уровнем важности или выше. | 32 | | log_min_duration_statement (integer) | Записывает в журнал продолжительность выполнения всех команд, время работы которых равно или превышает указанное количество миллисекунд. Значение 0 (ноль) заставляет записывать продолжительность работы всех команд. Значение -1 (по умолчанию) запрещает регистрировать продолжительность выполнения операторов. | 33 | 34 | #### Уровни важности сообщений 35 | | Уровень | Описание | 36 | | :------------- | :------------- | 37 | | DEBUG1..DEBUG5 | Более детальная информация для разработчиков. Чем больше номер, тем детальнее. | 38 | | INFO | Неявно запрошенная пользователем информация, например вывод команды VACUUM VERBOSE. | 39 | | NOTICE | Информация, которая может быть полезной пользователям. Например, уведомления об усечении длинных идентификаторов. | 40 | | WARNING | Предупреждения о возможных проблемах. Например, COMMIT вне транзакционного блока. | 41 | | ERROR | Сообщает об ошибке, из-за которой прервана текущая команда. | 42 | | LOG | Информация, полезная для администраторов. Например, выполнение контрольных точек. | 43 | | FATAL | Сообщает об ошибке, из-за которой прервана текущая сессия. | 44 | | PANIC | Сообщает об ошибке, из-за которой прерваны все сессии.| 45 | 46 | ### Что записываем в логи 47 | `log_duration (boolean)`: Записывает продолжительность каждой завершённой команды. Включение этого параметра и установка log_min_duration_statement в 0 (ноль) различаются. Разница в том, что при превышении значения log_min_duration_statement, в журнал записывается текст запроса, а при включении log_duration нет. Таким образом, при log_duration = on и log_min_duration_statement больше нуля, в журнал сервера будет записываться продолжительность выполнения всех команд, а текст запросов только для команд, превысивших заданное значение. Такое поведение может оказаться полезным при сборе статистики в условиях большой нагрузки. 48 | 49 | `log_line_prefix (string)`: Строка, в стиле функции printf, которая выводится в начале каждой строки журнала сообщений. С символов % начинаются управляющие последовательности, которые заменяются статусной информацией, описанной ниже. Неизвестные управляющие последовательности игнорируются. Все остальные символы напрямую копируются в выводимую строку. Статусная информация может быть выровнена по ширине влево или вправо указанием числа после % и перед кодом последовательности. Отрицательное число дополняет значение пробелами справа до заданной ширины, а положительное число — слева. 50 | 51 | | Спецсимвол | Описание | 52 | | :------------- | :------------- | 53 | | %a | Имя приложения (application_name) | 54 | | %u | Имя пользователя | 55 | | %d | Имя базы данных | 56 | | %r | Имя удалённого узла или IP-адрес, а также номер порта | 57 | | %h | Имя удалённого узла или IP-адрес | 58 | | %p | Идентификатор процесса | 59 | | %t | Штамп времени, без миллисекунд | 60 | | %m | Штамп времени, с миллисекундами | 61 | | %n | Штамп времени, с миллисекундами (в виде времени Unix) | 62 | | %i | Тег команды: тип текущей команды в сессии | 63 | | %e | Код ошибки SQLSTATE | 64 | | %l | Номер строки журнала для каждой сессии или процесса. Начинается с 1 | 65 | | %s | Штамп времени начала процесса | 66 | | %v | Идентификатор виртуальной транзакции (backendID/localXID) | 67 | | %x | Идентификатор транзакции (0 если не присвоен) | 68 | | %| % Выводит % | 69 | 70 | | Параметр | Описание | 71 | | :------------- | :------------- | 72 | | log_checkpoints (boolean) | Включает протоколирование выполнения контрольных точек и точек перезапуска сервера. При этом записывается некоторая статистическая информация. Например, число записанных буферов и время, затраченное на их запись. | 73 | | log_connections (boolean) | Включает протоколирование всех попыток подключения к серверу, в том числе успешного завершения аутентификации клиентов. Некоторые программы, например psql, предпринимают две попытки подключения (первая для определения, нужен ли пароль). Поэтому дублирование сообщения «connection received» не обязательно говорит о наличии проблемы.| 74 | | log_disconnections (boolean) | Включает протоколирование завершения сеанса. В журнал выводится примерно та же информация, что и с log_connections, плюс длительность сеанса.| 75 | | log_error_verbosity (enum) | Управляет количеством детальной информации, записываемой в журнал сервера для каждого сообщения. Допустимые значения: TERSE, DEFAULT и VERBOSE. Каждое последующее значение добавляет больше полей в выводимое сообщение. Для TERSE из сообщения об ошибке исключаются поля DETAIL, HINT, QUERY и CONTEXT. Для VERBOSE в сообщение включается код ошибки SQLSTATE, а также имя файла с исходным кодом, имя функции и номер строки сгенерировавшей ошибку. | 76 | | log_lock_waits (boolean) | Определяет, нужно ли фиксировать в журнале события, когда сеанс ожидает получения блокировки дольше, чем указано в deadlock_timeout. Это позволяет выяснить, не связана ли низкая производительность с ожиданием блокировок. | 77 | | log_statement (enum) | Управляет тем, какие SQL-команды записывать в журнал. Допустимые значения: none (отключено), ddl, mod и all (все команды). ddl записывает все команды определения данных, такие как CREATE, ALTER, DROP. mod записывает все команды ddl, а также команды изменяющие данные, такие как INSERT, UPDATE, DELETE, TRUNCATE и COPY FROM. PREPARE, EXECUTE и EXPLAIN ANALYZE также записываются, если вызваны для команды соответствующего типа. Если клиент использует расширенный протокол запросов, то запись происходит на фазе выполнения и содержит значения всех связанных переменных (если есть символы одиночных кавычек, то они удваиваются). | 78 | | log_temp_files (integer) | Управляет включением в журнал информации об именах и размерах временных файлов. Временные файлы могут использоваться для сортировок, хеширования и временного хранения результатов запросов. На каждый временный файл, при его удалении, в журнал записывается отдельное сообщение. Значение 0 говорит о том, что нужно записывать информацию о всех временных файлах. Положительное значение задаёт размер временных файлов в килобайтах, при достижении или превышении которого, информация о временном файле будет записана. Значение по умолчанию -1, что отключает запись информации о временных файлах. | 79 | | llog_timezone (string) | Устанавливает часовой пояс для штампов времени при записи в журнал сервера. В отличие от TimeZone, это значение одинаково для всех баз данных кластера, поэтому для всех сессий используются согласованные значения штампов времени. | 80 | 81 | ### Дополнительные инструменты 82 | [pgBadger](http://dalibo.github.io/pgbadger/) – инструмент для сложного анализа файлов журнала. Можно строить различные html отчеты с разрезами топ медленных, топ частых запросов и т.д. Можно преобразовывать лог в json файл для машинной обработки. 83 | 84 | [check_postgres](https://bucardo.org/check_postgres/) – может посылать уведомления в Nagios, когда в журнале появляются важные сообщения, а также при обнаружении других нестандартных ситуаций. 85 | 86 | [tail_n_mail](https://bucardo.org/tail_n_mail/) – может делать рассылки на почту при обнаружении в логах чего-то подозрительного или требующего внимания. 87 | 88 | [Logstash](https://www.elastic.co/products/logstash) – можно использовать вместе с ELK стеком для парсинга (Logstash), хранения (Elastic), красивого отображения и анализа(Kibana) логов (продвинутый pgBadger). 89 | 90 | 91 | 92 | ### Источники 93 | - [Документация](https://postgrespro.ru/docs/postgrespro/10/runtime-config-logging) postgrespro.ru 94 | -------------------------------------------------------------------------------- /monitoring/PostgreSQL_monitoring.md: -------------------------------------------------------------------------------- 1 | ## Monitoring 2 | Более подробная информация доступна в документации – https://postgrespro.ru/docs/postgrespro/10/monitoring-stats. 3 | 4 | - Состояние запросов в динамике (выборка по 5 самым долгим) 5 | ```sql 6 | select datname, usename, pid as process_pid, 7 | coalesce(client_hostname, client_addr::text) as client, 8 | backend_type, now() - query_start as query_duration, state, wait_event, wait_event_type, 9 | query 10 | from pg_stat_activity order by 6 desc nulls last limit 5; 11 | ``` 12 | 13 | | Столбец | Описание | 14 | | :------------- | :------------- | 15 | | backend_type | Тип текущего серверного процесса. Возможные варианты: autovacuum launcher, autovacuum worker, background worker, background writer, client backend, checkpointer, startup, walreceiver, walsender и walwriter. | 16 | | state | Общее текущее состояние этого серверного процесса. | 17 | | wait_event | Имя ожидаемого события, если обслуживающий процесс находится в состоянии ожидания, а в противном случае — NULL. | 18 | |wait_event_type | Тип события, которого ждёт обслуживающий процесс, если это имеет место; в противном случае — NULL. | 19 | 20 | Более подробно про возможные значения и их описание можно прочитать в документации - https://postgrespro.ru/docs/postgrespro/10/monitoring-stats#PG-STAT-ACTIVITY-VIEW. 21 | 22 | - Мониторинг состояния реплики (куда стримим, лаг в секундах и мегабайтах) 23 | ```sql 24 | select coalesce(client_hostname, client_addr::text) as client, 25 | state, 26 | replay_lag as replication_lag, 27 | pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) / (1024 * 2) as replication_lag_megabytes 28 | from pg_stat_replication; 29 | ``` 30 | 31 | - функции для получения lsn текущего WAL и последнего переданного на standby WAL 32 | ```sql 33 | select pg_current_wal_lsn(); 34 | select pg_last_wal_receive_lsn(); 35 | ``` 36 | 37 | - Получаем список WAL файлов на сервере 38 | ```sql 39 | select name, size, modification 40 | from pg_ls_waldir() 41 | order by modification desc limit 5; 42 | ``` 43 | 44 | - Подсчет числа транзакций в базе данных 45 | ```sql 46 | select pg_stat_reset(); 47 | -- ждем 1 час или сколько нужно 48 | select datname, xact_commit 49 | from pg_stat_database; -- это и есть число транзакций за 1 час (или выжданное время) 50 | ``` 51 | 52 | - Смотрим как используется таблица 53 | ```sql 54 | select 55 | seq_scan, seq_tup_read, 56 | idx_scan, idx_tup_fetch, 57 | n_tup_ins, n_tup_upd, n_tup_del, 58 | n_tup_hot_upd, 59 | n_live_tup, n_dead_tup, 60 | last_autovacuum 61 | from pg_stat_all_tables where schemaname = 'schema_name' and relname = 'rel_name'; 62 | ``` 63 | 64 | | Столбец |Описание | 65 | | :------------- | :------------- | 66 | | seq_scan | Количество последовательных чтений, запущенных по этой таблице | 67 | | seq_tup_read | Количество "живых" строк, прочитанных при последовательных чтениях | 68 | | idx_scan | Количество сканирований по индексу, запущенных по этой таблице | 69 | | idx_tup_fetch | Количество "живых" строк, отобранных при сканированиях по индексу | 70 | | n_tup_ins | Количество вставленных строк | 71 | | n_tup_upd | Количество изменённых строк (включая изменения по схеме HOT) | 72 | | n_tup_del | Количество удалённых строк | 73 | | n_tup_hot_upd | Количество строк, обновлённых в режиме HOT (т. е. без отдельного изменения индекса) | 74 | | n_live_tup | Оценочное количество "живых" строк | 75 | | n_dead_tup | Оценочное количество "мёртвых" строк| 76 | | last_autovacuum | Время последней очистки таблицы фоновым процессом автоочистки | 77 | 78 | - Cмотрим, как используется индекс 79 | ```sql 80 | select 81 | schemaname, relname, 82 | idx_scan, 83 | idx_tup_read, -- возвращено строк 84 | idx_tup_fetch -- возвращено "живых" строк только при простом сканировании (без учета bitmap и тд) 85 | from pg_stat_all_indexes 86 | where indexrelname = 'index_name'; 87 | ``` 88 | 89 | - Смотрим, как работает таблица с диском 90 | ```sql 91 | select 92 | heap_blks_read, heap_blks_hit, 93 | idx_blks_read, idx_blks_hit, 94 | toast_blks_read, toast_blks_hit, 95 | tidx_blks_read as toast_idx_blcks_read, 96 | tidx_blks_hit as toast_idx_blcks_hit 97 | from pg_statio_all_tables 98 | where schemaname = 'schema_name' 99 | and relname = 'rel_name'; 100 | ``` 101 | 102 | - Смотрим, как работает индекс с диском 103 | ```sql 104 | select 105 | schemaname, relname, 106 | idx_blks_read, idx_blks_hit 107 | from pg_statio_all_indexes 108 | where indexrelname = 'index_name'; 109 | ``` 110 | 111 | - Смотрим, как скоро запустится wraparound vacuum по таблице (и большая ли эта таблица) 112 | ```sql 113 | SELECT 114 | oid::regclass::text AS table, 115 | age(relfrozenxid) AS xid_age, 116 | mxid_age(relminmxid) AS mxid_age, 117 | least( 118 | (SELECT setting::int 119 | FROM pg_settings 120 | WHERE name = 'autovacuum_freeze_max_age') - age(relfrozenxid), 121 | (SELECT setting::int 122 | FROM pg_settings 123 | WHERE name = 'autovacuum_multixact_freeze_max_age') - mxid_age(relminmxid) 124 | ) AS tx_before_wraparound_vacuum, 125 | pg_size_pretty(pg_total_relation_size(oid)) AS size, 126 | pg_stat_get_last_autovacuum_time(oid) AS last_autovacuum 127 | FROM pg_class 128 | WHERE relfrozenxid != 0 129 | ORDER BY tx_before_wraparound_vacuum; 130 | ``` 131 | -------------------------------------------------------------------------------- /psql/PostgreSQL_psql.md: -------------------------------------------------------------------------------- 1 | ## psql 2 | 3 | - Информация про таблицу 4 | ``` 5 | \d+ pgbench_accounts 6 | Table "public.pgbench_accounts" 7 | Column | Type | Collation | Nullable | Default | Storage | Stats target | Description 8 | -------------+---------------+-----------+----------+---------+----------+--------------+------------- 9 | aid | integer | | not null | | plain | | 10 | bid | integer | | | | plain | | 11 | abalance | integer | | | | plain | | 12 | filler | character(84) | | | | extended | | 13 | test_column | text | | | | extended | | 14 | Indexes: 15 | "pgbench_accounts_pkey" PRIMARY KEY, btree (aid) 16 | Options: fillfactor=100 17 | ``` 18 | 19 | - Информация про индекс 20 | ``` 21 | \di+ pgbench_accounts_pkey 22 | List of relations 23 | Schema | Name | Type | Owner | Table | Size | Description 24 | --------+-----------------------+-------+----------+------------------+--------+------------- 25 | public | pgbench_accounts_pkey | index | postgres | pgbench_accounts | 107 MB | 26 | ``` 27 | 28 | - Информация про представление 29 | ``` 30 | \dv+ pg_user_mappings 31 | List of relations 32 | Schema | Name | Type | Owner | Size | Description 33 | ------------+------------------+------+----------+---------+------------- 34 | pg_catalog | pg_user_mappings | view | postgres | 0 bytes | 35 | ``` 36 | 37 | Дополнительные команды 38 | 39 | | Команда | Описание | 40 | | :------------- | :------------- | 41 | | \dt+ *.* | Список всех таблиц и краткая информация по ним во всех схемах БД | 42 | | \di+ *.* | Список всех индексов и краткая информация по ним во всех схемах БД | 43 | | \dv+ *.* | Список всех представлений и краткая информация по ним во всех схемах БД | 44 | | \dp schema_name.table_name | Список всех прав доступа | 45 | | \df+ *.* | Список всех функций и краткая информация по ним во всех схемах БД | 46 | | \l | Список всех БД кластера | 47 | -------------------------------------------------------------------------------- /run-time parameters/run-time.md: -------------------------------------------------------------------------------- 1 | ## Конфигурационные параметры времени выполнения 2 | 3 | Когда клиент подключён к базе данных, он может воспользоваться двумя командами SQL (и равнозначными функциями), которые предоставляет PostgreSQL для управления параметрами конфигурации: 4 | - Команда `SHOW` позволяет узнать текущее значение всех параметров. Соответствующая ей функция — `current_setting(имя_параметра text)`. 5 | - Команда `SET` позволяет изменить текущее значение параметров, которые действуют локально в рамках сеанса; на другие сеансы она не влияет. Соответствующая ей функция — `set_config(имя_параметра, новое_значение, локально)`. 6 | 7 | Кроме того, просмотреть и изменить значения параметров для текущего сеанса можно в системном представлении `pg_settings`: 8 | 9 | Запрос на чтение представления выдаёт ту же информацию, что и `SHOW ALL`, но более подробно. Этот подход и более гибкий, так как в нём можно указать условия фильтра или связать результат с другими отношениями. 10 | 11 | Выполнение `UPDATE` для этого представления, а именно присвоение значения столбцу, равносильно выполнению команды `SET`. Например, команде 12 | ```sql 13 | SET configuration_parameter TO DEFAULT; 14 | ``` 15 | равнозначен запрос: 16 | ```sql 17 | UPDATE pg_settings SET setting = reset_val WHERE name = 'configuration_parameter'; 18 | ``` 19 | 20 | Проверить, можно ли изменить параметр "на лету" (значение `false`) или нет можно запросом 21 | ```sql 22 | SELECT pending_restart FROM pg_settings WHERE name = 'configuration_parameter'; 23 | ``` 24 | 25 | ### Установка параметров 26 | Команда `SET` изменяет конфигурационные параметры времени выполнения («на лету»). 27 | 28 | Если команда SET (или равнозначная SET SESSION) выполняется внутри транзакции, которая затем прерывается, эффект команды SET пропадает, когда транзакция откатывается. Если же окружающая транзакция фиксируется, этот эффект сохраняется до конца сеанса, если его не переопределит другая команда SET. 29 | 30 | Действие SET LOCAL продолжается только до конца текущей транзакции, независимо от того, фиксируется она или нет. Особый случай представляет использование SET с последующей SET LOCAL в одной транзакции: значение, заданное SET LOCAL, будет сохраняться до конца транзакции, но после этого (если транзакция фиксируется) восстановится значение, заданное командой SET. 31 | 32 | ``` 33 | SET [ SESSION | LOCAL ] параметр_конфигурации { TO | = } { значение | 'значение' | DEFAULT } 34 | SET [ SESSION | LOCAL ] TIME ZONE { часовой_пояс | LOCAL | DEFAULT } 35 | ``` 36 | 37 | Пример 38 | ```sql 39 | SET TIME ZONE 'Europe/Moscow'; 40 | SET search_path = 'my_schema'; 41 | SET search_path TO my_schema; 42 | SET search_path = DEFAULT; 43 | ``` 44 | 45 | Также изменить значение параметра можно с помощью функции set_config. Кроме того, выполнив UPDATE в системном представлении pg_settings, можно произвести то же действие, что выполняет SET. 46 | 47 | ``` 48 | set_config(setting_name, new_value, is_local) 49 | 50 | ``` 51 | 52 | Если параметр is_local равен true, новое значение будет действовать только в рамках текущей транзакции. 53 | 54 | 55 | Пример 56 | ```sql 57 | SELECT set_config('log_statement_stats', 'off', false); 58 | 59 | set_config 60 | ------------ 61 | off 62 | (1 row) 63 | ``` 64 | 65 | ### Просмотр текущих параметров 66 | Чтобы посмотреть текущее значение параметра, можно воспользоваться командой `SHOW` или функцией `current_setting(setting_name [, missing_ok ])`. Если параметра с именем setting_name нет, функция current_setting выдаёт ошибку, если только дополнительно не передан параметр missing_ok со значением true. 67 | 68 | Пример 69 | ```sql 70 | SHOW search_path ; 71 | search_path 72 | ----------------- 73 | "$user", public 74 | 75 | SELECT current_setting('datestyle'); 76 | 77 | current_setting 78 | ----------------- 79 | ISO, MDY 80 | (1 row) 81 | ``` 82 | 83 | ### Источники 84 | - [Документация](https://postgrespro.ru/docs/postgrespro/10/sql-set) postgrespro.ru 85 | -------------------------------------------------------------------------------- /schemas and priveleges/postgresql_schema.md: -------------------------------------------------------------------------------- 1 | ## Схемы и привилегии 2 | ### Схемы 3 | Схемы в некоторым смысле подобны каталогам в операционной системе, но они не могут быть вложенными. 4 | Схема по сути представляет собой пространство имён: она содержит именованные объекты (таблицы, типы данных, функции и операторы), имена которых могут совпадать с именами других объектов, существующих в других схемах. Для обращения к объекту нужно либо «дополнить» его имя именем схемы в виде префикса, либо установить путь поиска, включающий требуемую схему. Команда `CREATE`, в которой указывается неполное имя объекта, создаёт объект в текущей схеме (схеме, стоящей первой в пути поиска; узнать её позволяет функция `current_schema`). 5 | 6 | Есть несколько возможных объяснений, для чего стоит применять схемы: 7 | - Чтобы одну базу данных могли использовать несколько пользователей, независимо друг от друга. 8 | - Чтобы объединить объекты базы данных в логические группы для облегчения управления ими. 9 | - Чтобы в одной базе сосуществовали разные приложения, и при этом не возникало конфликтов имён. 10 | 11 | Более подробно про варианты применения схем будет описано ниже (в главе **Шаблоны использования**). 12 | 13 | #### Синтаксис 14 | ```sql 15 | CREATE SCHEMA имя_схемы [ AUTHORIZATION указание_роли ] [ элемент_схемы [ ... ] ] 16 | CREATE SCHEMA AUTHORIZATION указание_роли [ элемент_схемы [ ... ] ] 17 | CREATE SCHEMA IF NOT EXISTS имя_схемы [ AUTHORIZATION указание_роли ] 18 | CREATE SCHEMA IF NOT EXISTS AUTHORIZATION указание_роли 19 | 20 | Здесь указание_роли: 21 | 22 | имя_пользователя 23 | | CURRENT_USER 24 | | SESSION_USER 25 | ``` 26 | Команда `CREATE SCHEMA` может дополнительно включать подкоманды, создающие объекты в новой схеме. Эти подкоманды по сути воспринимаются как отдельные команды, выполняемые после создания схемы, за исключением того, что с предложением `AUTHORIZATION` все создаваемые объекты будут принадлежать указанному в нём пользователю. 27 | 28 | **Параметр элемент_схемы** 29 | Оператор SQL, определяющий объект, создаваемый в новой схеме. В настоящее время `CREATE SCHEMA` может содержать только подкоманды `CREATE TABLE`, `CREATE VIEW`, `CREATE INDEX`, `CREATE SEQUENCE`, `CREATE TRIGGER` и `GRANT`. Создать объекты других типов можно отдельными командами после создания схемы. 30 | 31 | Чтобы создать схему, пользователь должен иметь право `CREATE` в текущей базе данных. (Разумеется, на суперпользователей это условие не распространяется.) 32 | 33 | Пример 34 | ```sql 35 | CREATE SCHEMA hollywood 36 | CREATE TABLE films (title text, release date, awards text[]) 37 | CREATE VIEW winners AS 38 | SELECT title, release FROM films WHERE awards IS NOT NULL; 39 | ``` 40 | 41 | #### Создание и удаление схемы 42 | Для создания схемы используется команда CREATE SCHEMA. При этом вы определяете имя схемы по своему выбору, например так: 43 | ```sql 44 | CREATE SCHEMA myschema; 45 | ``` 46 | Чтобы удалить пустую схему (не содержащую объектов), выполните: 47 | ```sql 48 | DROP SCHEMA myschema; 49 | ``` 50 | Удалить схему со всеми содержащимися в ней объектами можно так: 51 | ```sql 52 | DROP SCHEMA myschema CASCADE; 53 | ``` 54 | 55 | #### Обращение к объектам в схеме 56 | Чтобы создать объекты в схеме или обратиться к ним, указывайте полное имя, состоящее из имён схемы и объекта, разделённых точкой: `схема.таблица` 57 | 58 | #### Путь поиска схем (search_path) 59 | Чтобы каждый раз не указывать схему явно, можно задать путь поиска (`search_path`). Первая существующая схема в пути поиска называется текущей. Эта схема будет использоваться не только при поиске, но и при создании объектов. 60 | 61 | Если объекты отсутствуют в первой схеме, поиск продолжается во всех остальных схемах из `search_path`, пока объект не будет найден. 62 | 63 | Если объект не существует или находится в схеме, не добавленной в `search_path`, мы получим ошибку. 64 | 65 | *Примечание: Путь поиска можно изменить во время выполнения следующей командой:* 66 | ```sql 67 | SET search_path TO схема [, схема, ...] 68 | ``` 69 | 70 | #### Схема Public 71 | Во всех базах данных по умолчанию есть схема public. Единственное, что отличает схему `public` от других, это то, что она существует по умолчанию, хотя её так же можно удалить. По умолчанию, она идет второй схемой в `search_path`: 72 | ```sql 73 | show search_path; 74 | ┌─────────────────┐ 75 | │ search_path │ 76 | ├─────────────────┤ 77 | │ "$user", public │ 78 | └─────────────────┘ 79 | ``` 80 | Если `search_path` не был изменен и в базе данных нет схемы, совпадающей с именем текущего пользователя, все объекты без явного указания схемы будут создаваться в `public`. 81 | 82 | #### Схема системного каталога 83 | В дополнение к схеме `public` и схемам, создаваемым пользователями, любая база данных содержит схему `pg_catalog`, в которой находятся системные таблицы и все встроенные типы данных, функции и операторы. `pg_catalog` **фактически всегда является частью пути поиска**. Если даже эта схема не добавлена в путь явно, она неявно просматривается до всех схем, указанных в пути. Так обеспечивается доступность встроенных имён при любых условиях. Однако вы можете явным образом поместить pg_catalog в конец пути поиска, если вам нужно, чтобы пользовательские имена переопределяли встроенные. 84 | 85 | Так как имена системных таблиц начинаются с `pg_`, такие имена лучше не использовать во избежание конфликта имён, возможного при появлении в будущем системной таблицы с тем же именем, что и ваша. (С путём поиска по умолчанию неполная ссылка будет воспринята как обращение к системной таблице.) Системные таблицы будут и дальше содержать в имени приставку `pg_`, так что они не будут конфликтовать с неполными именами пользовательских таблиц, если пользователи со своей стороны не будут использовать приставку `pg_`. 86 | 87 | #### Функции получения информации о схеме 88 | - Функция current_schema возвращает имя схемы, которая стоит первой в пути поиска (или NULL, если путь поиска пуст). Эта схема будет задействована при создании таблиц или других именованных объектов, если целевая схема не указана явно. 89 | ```sql 90 | select current_schema; 91 | ┌────────────────┐ 92 | │ current_schema │ 93 | ├────────────────┤ 94 | │ public │ 95 | └────────────────┘ 96 | ``` 97 | - Функция current_schemas(boolean) возвращает массив имён всех схем, находящихся в пути поиска. Её логический параметр определяет, будут ли включаться в результат неявно добавляемые в путь поиска системные схемы, такие как pg_catalog. 98 | ```sql 99 | select current_schemas(true); 100 | ┌─────────────────────┐ 101 | │ current_schemas │ 102 | ├─────────────────────┤ 103 | │ {pg_catalog,public} │ 104 | └─────────────────────┘ 105 | ``` 106 | 107 | ### Привелегии 108 | По умолчанию пользователь не может обращаться к объектам в чужих схемах. Чтобы изменить это, владелец схемы должен дать пользователю право `USAGE` для данной схемы. Чтобы пользователи могли использовать объекты схемы, может понадобиться назначить дополнительные права на уровне объектов. 109 | ```sql 110 | GRANT USAGE ON SCHEMA имя_схемы TO имя_пользователя; 111 | ``` 112 | При этом доступ на уровне объектов также должен быть задан корректно (например, пользователю явно должен быть выдан доступ на чтение конкретной или всех таблиц схемы). 113 | 114 | Пользователю также можно разрешить создавать объекты в схеме, не принадлежащей ему. Для этого ему нужно дать право `CREATE` в требуемой схеме. Заметьте, что по умолчанию все имеют права `CREATE` и `USAGE` в схеме `public`. Благодаря этому все пользователи могут подключаться к заданной базе данных и создавать объекты в её схеме `public`. 115 | 116 | #### Установка владельца схемы 117 | Часто бывает нужно создать схему, владельцем которой будет другой пользователь (это один из способов ограничения пользователей пространствами имён). Сделать это можно так: 118 | ```sql 119 | CREATE SCHEMA имя_схемы AUTHORIZATION имя_пользователя; 120 | ``` 121 | Вы даже можете опустить имя схемы, в этом случае именем схемы станет имя пользователя. 122 | Схемы с именами, начинающимися с pg_, являются системными; пользователям не разрешено использовать такие имена. 123 | 124 | #### Привелегии на уровне объектов схемы 125 | Можно выдать доступ как к одному, так и всем подобным объектам (таблицам, последовательностям, функциям) в рамках схемы: 126 | ```sql 127 | GRANT { { SELECT | INSERT | UPDATE | DELETE | TRUNCATE | REFERENCES | TRIGGER } 128 | [, ...] | ALL [ PRIVILEGES ] } 129 | ON { [ TABLE ] table_name [, ...] 130 | | ALL TABLES IN SCHEMA schema_name [, ...] } 131 | TO role_specification [, ...] [ WITH GRANT OPTION ] 132 | 133 | GRANT { { USAGE | SELECT | UPDATE } 134 | [, ...] | ALL [ PRIVILEGES ] } 135 | ON { SEQUENCE sequence_name [, ...] 136 | | ALL SEQUENCES IN SCHEMA schema_name [, ...] } 137 | TO role_specification [, ...] [ WITH GRANT OPTION ] 138 | 139 | GRANT { EXECUTE | ALL [ PRIVILEGES ] } 140 | ON { FUNCTION function_name [ ( [ [ argmode ] [ arg_name ] arg_type [, ...] ] ) ] [, ...] 141 | | ALL FUNCTIONS IN SCHEMA schema_name [, ...] } 142 | TO role_specification [, ...] [ WITH GRANT OPTION ] 143 | 144 | GRANT { { CREATE | USAGE } [, ...] | ALL [ PRIVILEGES ] } 145 | ON SCHEMA schema_name [, ...] 146 | TO role_specification [, ...] [ WITH GRANT OPTION ] 147 | 148 | where role_specification can be: 149 | 150 | [ GROUP ] role_name 151 | | PUBLIC 152 | | CURRENT_USER 153 | | SESSION_USER 154 | ``` 155 | 156 | ### Шаблоны использования схем 157 | 1. Ограничить обычных пользователей личными схемами. Для реализации этого подхода выполните 158 | `REVOKE CREATE ON SCHEMA public FROM PUBLIC` (первое слово `public` обозначает схему, а второе означает «каждый пользователь». В первом случае это идентификатор, а во втором — ключевое слово) и создайте для каждого пользователя схему с его именем. Если затрагиваемые пользователи подключались к базе ранее, проведите аудит схемы на предмет наличия таких же имён, как в схеме `pg_catalog`. Вспомните, что путь поиска по умолчанию начинается со значения `$user`, которое разрешается в имя пользователя. Таким образом, если у всех пользователей будет отдельная схема, они по умолчанию будут обращаться к собственным схемам. 159 | 160 | 2. Удалить схему `public` из пути поиска по умолчанию для каждого пользователя, с помощью команды `ALTER ROLE пользователь SET search_path = "$user"`. Все сохранят возможность создавать объекты в общедоступной схеме, но обращаться к ним будут только по полным именам. Пользователь, имеющий право `CREATEROLE`, сможет отменить присвоение этого пути и выполнять произвольные запросы от имени пользователей, полагающихся на расширенный путь. Если вы даёте пользователям право `CREATEROLE`, но не хотите, чтобы они стали практически суперпользователями, вам нужно использовать первый шаблон. 161 | 162 | 3. Удалить схему `public` из пути поиска (`search_path`) в `postgresql.conf`. Это будет иметь такое же влияние на пользователей, что и предыдущий шаблон. При этом такое же влияние, как обладатели права `CREATEROLE`, смогут оказывать и владельцы баз данных. Если вы даёте пользователям права `CREATEROLE`, `CREATEDB` или делаете их владельцами отдельных баз данных, но не хотите, чтобы они стали практически суперпользователями, вам нужно использовать первый шаблон. 163 | 164 | 4. Сохранить поведение по умолчанию. Все пользователи неявно обращаются к схеме `public`. Тем самым имитируется ситуация с полным отсутствием схем, что позволяет осуществить плавный переход из среды без схем. Однако при этом любой пользователь может выполнять произвольные запросы от имени любого пользователя, который не позаботится о своей защите специально. Этот шаблон подходит, только если в базе данных имеется всего один или несколько взаимно доверяющих пользователей. 165 | 166 | При любом подходе, устанавливая совместно используемые приложения (таблицы, которые нужны всем, дополнительные функции сторонних разработчиков и т. д.), помещайте их в отдельные схемы. Не забудьте дать другим пользователям права для доступа к этим схемам. Тогда пользователи смогут обращаться к этим дополнительным объектам по полному имени или при желании добавят эти схемы в свои пути поиска. 167 | 168 | ### Продвинутое использование схем 169 | TODO (версионирование схемами Zalando, Avito) 170 | 171 | ### Источники 172 | - Документация postgrespro.ru 173 | -------------------------------------------------------------------------------- /statistics and data analysis/PostgreSQL_анализ_данных.md: -------------------------------------------------------------------------------- 1 | ## Статистика и анализ данных 2 | #### Фактическая информация по [B-tree индексу](https://postgrespro.ru/docs/postgrespro/10/pgstattuple) 3 | Информация собирается чтением всех страниц индекса. 4 | ```sql 5 | SELECT * FROM pgstatindex('schema_name.table_name'); 6 | -[ RECORD 1 ]------+---------- 7 | version | 2 8 | tree_level | 3 9 | index_size | 361717760 10 | root_block_no | 16607 11 | internal_pages | 417 12 | leaf_pages | 43737 13 | empty_pages | 0 14 | deleted_pages | 0 15 | avg_leaf_density | 33.67 16 | leaf_fragmentation | 49.24 17 | ``` 18 | 19 | | Столбец | Описание | 20 | | :------------- | :------------- | 21 | | version | Версия B-tree | 22 | | tree_level | Уровень корневой страницы в дереве | 23 | | index_size | Общий объём индекса в байтах | 24 | | root_block_no | Расположение страницы корня (0, если её нет) | 25 | | internal_pages | Количество «внутренних» страниц (верхнего уровня) | 26 | | leaf_pages | Количество страниц на уровне листьев | 27 | | empty_pages | Количество пустых страниц | 28 | | deleted_pages | Количество удалённых страниц | 29 | | avg_leaf_density | Средняя плотность страниц на уровне листьев (в %. Чем выше тем лучше. В идеале должно соответствовать значению fillfactor) | 30 | | leaf_fragmentation | Фрагментация на уровне листьев (тоже в %, вероятно. Чем выше, тем хуже. Если большие значения, значит много пустого места и стоит сделать reindex.) | 31 | 32 | Листья - это 8KB странички, которые хранят данные из таблицы в логически отсортированном порядке и организованы в виде двусвязных списков. Если при добавлении новой строки в таблицу, в текущем листе не хватает места для новой записи, происходит добавление, т.е. появляется еще одна страничка листа, связанная с предыдущей как двусвязный список. При удалении и обновлении записей в таблице, происходит их удаление и в индексе, в связи с чем страницы индекса начинают содержать пустое место и занимать на диске и в памяти больше места, чем нужно (фрагментация). 33 | 34 | #### Фактическая информация по [таблице](https://postgrespro.ru/docs/postgrespro/10/pgstattuple) 35 | Информация собирается чтением всех страниц таблицы. 36 | ```sql 37 | SELECT * FROM pgstattuple('schema_name.table_name'); 38 | -[ RECORD 1 ]------+---------- 39 | table_len | 999006208 40 | tuple_count | 2261310 41 | tuple_len | 486961920 42 | tuple_percent | 48.74 43 | dead_tuple_count | 18 44 | dead_tuple_len | 3760 45 | dead_tuple_percent | 0 46 | free_space | 490253164 47 | free_percent | 49.07 48 | ``` 49 | 50 | | Столбец | Значение | 51 | | :------------- | :------------- | 52 | | table_len | Физическая длина отношения в байтах | 53 | | tuple_count | Количество «живых» кортежей | 54 | | tuple_len | Общая длина «живых» кортежей в байтах | 55 | | tuple_percent | Процент «живых» кортежей | 56 | | dead_tuple_count | Количество «мёртвых» кортежей | 57 | | dead_tuple_len | Общая длина «мёртвых» кортежей в байтах | 58 | | dead_tuple_percent | Процент «мёртвых» кортежей | 59 | | free_space | Общий объём свободного пространства в байтах | 60 | | free_percent | Процент свободного пространства | 61 | 62 | #### Статистика по [распределению данных в полях таблицы](https://postgrespro.ru/docs/postgrespro/10/view-pg-stats) 63 | Так как расчитывается на основе статистики (analyze), может содержать не совсем корректные сведения. Для увеличения достоверности данных можно увеличить значение собираемой статистики по данному полю (параметр statistics). 64 | ```sql 65 | SELECT * FROM pg_stats 66 | WHERE tablename = 'table_name' 67 | AND schemaname = 'schema_name' 68 | AND attname='attr_name'; 69 | -[ RECORD 1 ]----------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 70 | schemaname | schema_name 71 | tablename | table_name 72 | attname | attr_name 73 | inherited | f 74 | null_frac | 0 75 | avg_width | 8 76 | n_distinct | 1698 77 | most_common_vals | {"2014-09-11 00:00:00","2011-09-14 00:00:00","2014-01-06 00:00:00","2014-01-05 00:00:00","2014-01-11 00:00:00","2017-11-16 00:00:00","2014-01-08 00:00:00","2017-04-05 00:00:00","2 78 | most_common_freqs | {0.1232,0.0550367,0.0406333,0.0273067,0.0258933,0.0235833,0.02057,0.0177333,0.01366,0.0134767,0.0133767,0.01328,0.00983667,0.00938667,0.00779,0.0075,0.00718333,0.00700667,0.00631, 79 | histogram_bounds | {"2011-09-16 00:00:00","2011-09-21 00:00:00","2011-09-23 00:00:00","2011-09-27 00:00:00","2011-09-29 00:00:00","2011-09-30 00:00:00","2011-09-30 00:00:00","2011-10-04 00:00:00","2 80 | correlation | 0.212783 81 | most_common_elems | NULL 82 | most_common_elem_freqs | NULL 83 | elem_count_histogram | NULL 84 | ``` 85 | 86 | | Столбец | Значение | 87 | | :------------- | :------------- | 88 | | inherited | Если true, в данных этой строки учитываются значения в дочерних столбцах, а не только в указанной таблице | 89 | | null_frac | Доля записей, в которых этот столбец содержит NULL | 90 | | avg_width | Средний размер элементов в столбце, в байтах | 91 | | n_distinct | Число, большее нуля, представляет примерное количество различных значений в столбце. Если это число меньше нуля, его модуль представляет количество различных значений, делённое на количество строк. (Отрицательная форма применяется, когда ANALYZE полагает, что число различных значений, скорее всего, будет расти по мере роста таблицы; положительная, когда в столбце, вероятно, будет фиксированное количество возможных значений.) Например, -1 указывает на столбец с уникальным содержимым, в котором количество различных значений совпадает с количеством строк. | 92 | | most_common_vals | Список самых частых значений в столбце. (NULL, если не находятся значения, появляющиеся чаще других.) | 93 | | most_common_freqs | Список частот самых частых значений, то есть число их вхождений, делённое на общее количество строк. (NULL, вместе с most_common_vals.) | 94 | | histogram_bounds | Список значений, разделяющих значения столбца на примерно одинаковые популяции. Значения most_common_vals, если они присутствуют, не рассматриваются при вычислении этой гистограммы. (Этот столбец содержит NULL, если для типа данных столбца не определён оператор <, либо если в most_common_vals перечисляется вся популяция.) | 95 | | correlation | Статистическая корреляция между физическим порядком строк и логическим порядком значений столбца. Допустимые значения лежат в диапазоне -1 .. +1. Когда значение около -1 или +1, сканирование индекса по столбцу будет считаться дешевле, чем когда это значение около нуля, как результат уменьшения случайного доступа к диску. (Этот столбец содержит NULL, если для типа данных столбца не определён оператор <.) | 96 | | most_common_elems | Важно только для нескалярных типов (типа массивов). Список элементов, отличных от NULL, наиболее часто присутствующих в значениях столбца. (NULL для скалярных типов.) | 97 | | most_common_elem_freqs | Важно только для нескалярных типов (типа массивов). Список частот самых частых элементов, то есть доля строк, содержащих минимум один экземпляр данного значения. За частотами по элементам следуют два или три дополнительных значения: минимум и максимум предшествующих частот по элементам и дополнительно частота элементов NULL. (Принимает значение NULL вместе с most_common_elems.) | 98 | | elem_count_histogram | Важно только для нескалярных типов (типа массивов). Гистограмма количеств различных и отличных от NULL элементов в значениях этого столбца, за которой следует среднее количество элементов, отличных от NULL. (Принимает значение NULL для скалярных типов.) | 99 | 100 | Статистика собирается раз в PGSTAT_STAT_INTERVAL (500 ms) миллисекунд. Также статистика обновляется каждым бэкендом в момент, когда он готовится перейти в idle состояние. Статистика в рамках одной транзакции постоянна (до коммита видна будет одна и та же статистика). Транзакция может видеть некоторые изменения, вызванные действиями в этой транзакции. Подробнее https://www.postgresql.org/docs/10/static/monitoring-stats.html. 101 | 102 | #### [pg_statistic](https://postgrespro.ru/docs/postgrespro/9.6/catalog-pg-statistic) 103 | В каталоге pg_statistic хранится статистическая информация о содержимом базы данных. Записи в нём создаются командой ANALYZE, а затем используются планировщиком запросов. Заметьте, что все эти данные по природе своей неточные, даже если предполагается, что они актуальны. 104 | 105 | **В pg_statistic также хранится статистическая информация о значениях выражений функциональных индексов**. Она описывается так же, как если бы это были столбцы данных; в частности, starelid ссылается на индекс. Однако для столбцов, задействуемых в индексе без выражений, дополнительная запись не добавляется, так как она повторяла бы запись для нижележащего столбца таблицы. 106 | 107 | Пример увеличения статистики по функциональному индексу: 108 | ```sql 109 | CREATE TABLE public.test_table(key int, val text); 110 | CREATE INDEX public.idx_test_table_val_lower ON test_table (lower(val)); 111 | 112 | \d public.idx_test_table_val_lower 113 | Index "public.idx_test_table_val_lower" 114 | ┌────────┬──────┬────────────┐ 115 | │ Column │ Type │ Definition │ 116 | ├────────┼──────┼────────────┤ 117 | │ lower │ text │ lower(val) │ 118 | └────────┴──────┴────────────┘ 119 | btree, for table "public.test_table" 120 | 121 | ALTER TABLE public.idx_test_table_val_lower ALTER lower SET STATISTICS 10000; 122 | ANALYZE public.test_table; 123 | 124 | SELECT stanullfrac, stawidth, stadistinct 125 | FROM pg_statistic 126 | WHERE starelid = 'public.idx_test_table_val_lower'::regclass; 127 | ``` 128 | 129 | Над каталогом `pg_statistics` существует представление, называемое `pg_stats`. 130 | 131 | Максимальным числом записей в полях-массивах можно управлять на уровне столбцов, используя команду `ALTER TABLE SET STATISTICS`, или глобально, задав параметр времени выполнения `default_statistics_target`. 132 | 133 | Планировщик запросов должен оценить число строк, возвращаемых запросов, чтобы сделать правильный выбор в отношении плана запроса. В частности, статистика включает общее число записей в каждой таблице и индексе, а также число дисковых блоков, которые они занимают. 134 | 135 | #### ANALYZE 136 | Количество самплов, выбираемое PostgreSQL во время выполнения команды ANALYZE расчитывается по формуле 137 | ``` 138 | (300 * statistics_target) 139 | ``` 140 | 141 | Сами самплы берутся каждый раз рандомно. 142 | 143 | Статистика снимается по полям таблиц и по функциональным индексам. **Статистика не снимается по частичным функциональным индексам (c WHERE условием)!** 144 | 145 | Подробнее можно почитать тут – https://github.com/postgres/postgres/blob/master/src/backend/statistics/README. 146 | -------------------------------------------------------------------------------- /time and timezones/PostgreSQL_timestamp.md: -------------------------------------------------------------------------------- 1 | ## Timestamp 2 | ### Подходы к хранению времени в БД 3 | 1. Если вам нужно хранить время только что произошедшего события, текущее время, по факту определённого действия, храните его в UTC. Это могут быть записи в логах, время регистрации пользователя, совершения заказа или отправки письма; 4 | 2. Если время не привязано к пользователю или его часовому поясу, храните его в UTC. Это может быть, например, время следующего солнечного затмения; 5 | 3. Если вам нужно хранить время в прошлом или в будущем, сохраняйте локальное время пользователя, а рядом сохраняйте его таймзону. А ещё лучше, так, чтобы наверняка, сохраняйте географическое положение пользователя. Если нужно делать выборки по этому времени, сохраняйте рядом время в UTC, и обновляйте это время при изменении информации о часовом поясе; 6 | 4. Если вам нужно совершенно точно знать время для любой даты для заданного географического положения (например, для астрономических расчётов) — храните точные координаты пользователя, но не его часовой пояс. Впрочем, если перед вами стоит такая задача, то вы и так знаете, как делать правильно. 7 | 8 | Подробнее о проблемах хранения времени можно прочитать в статье - [работа с таймзонами](https://habr.com/company/mailru/blog/242645/) 9 | 10 | 11 | ### Timestamp в PostgreSQL 12 | **Timestamp** – содержит дату и время; 13 | **Timestamptz** – содержит дату, время и часовой пояс. Значения в timestamptz указаны в формате UTC. 14 | 15 | Типы timestamp и timestamptz занимают одинаковое место на диске. 16 | 17 | Timestamptz полезно использовать, если с приложением можно работать из разных часовых зон или если в стране есть перевод времени на зимнее/летнее. 18 | 19 | Если часовой пояс в запросе к полю timestamptz не указан, PostgreSQL использует заранее сконфигурированное время. Конфигурировать время можно разными способами: 20 | - параметр timezone в postgresql.conf используется для того, чтобы указать в каком часовом поясе находится сервер. Если другие параметры не заданы, будет использоваться он; 21 | - ```ALTER DATABASE foo SET timezone = 'Europe/Moscow';``` используется на уровне базы данных; 22 | - ```ALTER USER u_foo SET timezone = 'Europe/Moscow';``` используется на уровне пользователя; 23 | - ```SET timezone = 'Europe/Moscow'``` используется на уровне сессии. 24 | 25 | ### Поведение конструкции AT TIME ZONE (или ее аналога функции timezone) 26 | Согласно [документации](https://postgrespro.ru/docs/postgrespro/10/functions-datetime#FUNCTIONS-DATETIME-ZONECONVERT): 27 | 28 | | Выражение | Тип результата | Описание | 29 | | :------------- | :------------- | :------------- | 30 | | timestamp without time zone AT TIME ZONE часовой_пояс | timestamp with time zone | Воспринимает заданное время без указания часового пояса как время в указанном часовом поясе | 31 | | timestamp with time zone AT TIME ZONE часовой_пояс | timestamp without time zone | Переводит данное значение timestamp с часовым поясом в другой часовой пояс, но не сохраняет информацию о нём в результате | 32 | 33 | ### Примеры 34 | В примере ниже показывается изменение вывода функции now() при изменении часового пояса на уровне сессии: 35 | ```sql 36 | show timezone; 37 | ┌──────────┐ 38 | │ TimeZone │ 39 | ├──────────┤ 40 | │ UTC │ 41 | └──────────┘ 42 | SELECT now(); 43 | ┌───────────────────────────────┐ 44 | │ now │ 45 | ├───────────────────────────────┤ 46 | │ 2018-06-02 09:54:00.469394+00 │ 47 | └───────────────────────────────┘ 48 | SET timezone = 'Europe/Moscow'; 49 | SELECT now(); 50 | ┌───────────────────────────────┐ 51 | │ now │ 52 | ├───────────────────────────────┤ 53 | │ 2018-06-02 12:54:17.119815+03 │ 54 | └───────────────────────────────┘ 55 | ``` 56 | 57 | В данном примере отобразится сколько времени было в Москве, когда в LA было 20 часов (Москва опережает LA на 10 часов). Сперва timestamp в часовом поясе LA сконвертировался в UTC, затем сконвертирован в часовой пояс текущей сессии: 58 | ```sql 59 | show timezone; 60 | ┌───────────────┐ 61 | │ TimeZone │ 62 | ├───────────────┤ 63 | │ Europe/Moscow │ 64 | └───────────────┘ 65 | SELECT '2018-06-02 20:00:00-07'::timestamptz; 66 | ┌────────────────────────┐ 67 | │ timestamptz │ 68 | ├────────────────────────┤ 69 | │ 2018-06-03 06:00:00+03 │ 70 | └────────────────────────┘ 71 | ``` 72 | 73 | Можно посмотреть время и по другому: 74 | ```sql 75 | -- Сколько было времени в LA, когда в Москве было 20 вечера 76 | SELECT '2018-06-02 20:00:00'::timestamptz at time zone 'America/Los_Angeles'; 77 | ┌─────────────────────┐ 78 | │ timezone │ 79 | ├─────────────────────┤ 80 | │ 2018-06-02 10:00:00 │ 81 | └─────────────────────┘ 82 | -- OR 83 | SELECT timezone('America/Los_Angeles', '2018-06-02 20:00:00'::timestamptz); 84 | ┌─────────────────────┐ 85 | │ timezone │ 86 | ├─────────────────────┤ 87 | │ 2018-06-02 10:00:00 │ 88 | └─────────────────────┘ 89 | -- Сколько было времени в Москве, когда в LA было 20 вечера 90 | SELECT '2018-06-02 20:00:00'::timestamp at time zone 'America/Los_Angeles'; 91 | ┌────────────────────────┐ 92 | │ timezone │ 93 | ├────────────────────────┤ 94 | │ 2018-06-03 06:00:00+03 │ 95 | └────────────────────────┘ 96 | -- OR 97 | SELECT timezone('America/Los_Angeles', '2018-06-02 20:00:00'::timestamp); 98 | ┌────────────────────────┐ 99 | │ timezone │ 100 | ├────────────────────────┤ 101 | │ 2018-06-03 06:00:00+03 │ 102 | └────────────────────────┘ 103 | ``` 104 | 105 | В данном примере выведем время с точностью до секунд: 106 | ```sql 107 | SELECT now()::timestamptz(0); 108 | ┌────────────────────────┐ 109 | │ now │ 110 | ├────────────────────────┤ 111 | │ 2018-06-02 13:29:03+03 │ 112 | └────────────────────────┘ 113 | -- OR 114 | SELECT to_char(now(), 'YYYY-MM-DD HH24:MI:SS TZ'); 115 | ┌─────────────────────────┐ 116 | │ to_char │ 117 | ├─────────────────────────┤ 118 | │ 2018-06-02 13:30:40 MSK │ 119 | └─────────────────────────┘ 120 | -- OR 121 | SELECT date_trunc('second', now()); 122 | ┌────────────────────────┐ 123 | │ date_trunc │ 124 | ├────────────────────────┤ 125 | │ 2018-06-02 13:31:01+03 │ 126 | └────────────────────────┘ 127 | ``` 128 | -------------------------------------------------------------------------------- /transactions/PostgreSQL_transactions.md: -------------------------------------------------------------------------------- 1 | ## Transaction isolation (I in ACID) 2 | 3 | ### Виды нарушения изоляции 4 | https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Dirty_reads 5 | 6 | - Dirty read – Транзакция 1 делает update данных в таблице. Параллельная транзакция 2 сразу видит изменения в таблице, сделанные транзакцией 1 (даже не смотря на то, что транзакция 1 еще на сделала commit). 7 | - Non-repeatable read – два чтения в транзакции 1 по одной и той же строке могут вернуть два разных результата. Допустим, транзакция 1 читает строку первый раз. Затем, параллельная транзакция 2 изменяет эначения полей в этой строке. При повторном чтении, транзакция 1 уже читает измененное значение строки (получаем non-repeatable чтение). 8 | - Phantom read – В рамках одной транзакции, один и тот же запрос возвращает разный результирующий набор строк. 9 | 10 | Non-repeatable чтение изменяет значение в одной и той же строке при повторном выполнении запроса, в то время как фантомное чтение приводит к тому, что в результате выполнения запроса второй раз, читается другой набор строк (сами значения строк не изменяются, но к предыдущему результату выполнения добавляются новые строки или удаляются старые). 11 | 12 | #### Сопоставление уровней изоляции транзакции и видов нарушения изоляции 13 | | Уровень изоляции | Dirty read | Non-repeatable read | Phantom read | 14 | | :------------- | :------------- | :------------- | :------------- | 15 | | Read uncommitted | Может возникнуть | Может возникнуть | Может возникнуть | 16 | | Read committed | Не возникает | Может возникнуть | Может возникнуть | 17 | | Repeatable read | Не возникает | Не возникает | Может возникнуть | 18 | | Serializable | Не возникает | Не возникает | Не возникает | 19 | 20 | #### Пример 1 21 | ```sql 22 | create table test (id int, city text); 23 | 24 | 25 | insert into test values(1, 'Moscow'); 26 | insert into test values(2, 'Piter'); 27 | ``` 28 | 29 | ```sql 30 | -- Пример Non-repeatable read нарушения изоляции 31 | begin transaction isolation read committed; 32 | select city from test where id = 1; 33 | city 34 | -------- 35 | Moscow 36 | begin transaction isolation read committed; 37 | update test set city = 'Msk' where id = 1; 38 | commit; 39 | select city from test where id = 1; 40 | city 41 | ------ 42 | Msk 43 | commit; 44 | ``` 45 | 46 | ```sql 47 | -- Пример защиты уровня изоляции от Non-repeatable read 48 | begin transaction isolation level repeatable read; 49 | select city from test where id = 1; 50 | city 51 | -------- 52 | Msk 53 | begin transaction isolation level repeatable read; 54 | update test set city = 'Moscow' where id = 1; 55 | commit; 56 | select city from test where id = 1; 57 | city 58 | ------ 59 | Msk 60 | commit; 61 | 62 | select city from test where id = 1; 63 | city 64 | ------ 65 | Moscow 66 | ``` 67 | 68 | #### Пример 2 (phantom read и сериализация) 69 | https://vladmihalcea.com/a-beginners-guide-to-the-phantom-read-anomaly-and-how-it-differs-between-2pl-and-mvcc/ 70 | Допустим, Элис хочет повысить зарплату всем сотрудникам отдела на 10%, в это же время Боб нанимает нового сотрудника в отдел. Бюджет отдела ограничен 370 пиастрами. Текущие сотрудники в сумме получают 300 пиастр. Новый сотрудник должен получать 70 пиастр. 71 | ```sql 72 | create table employee (id int, salary int, department int); 73 | 74 | insert into employee 75 | values (1, 100, 1), (2, 150, 1), (3, 50, 1); 76 | ``` 77 | 78 | Элис и Боб одновременно проверяю, сколько осталось свободных денег в бюджете. Затем Боб подписывает соглашение с новым сотрудником, а через несколько секунд Элис увеличивает зарплату всем старым сотрудникам. В итоге получают перерасход бюджета 30 пиастр. 79 | ```sql 80 | begin transaction isolation level Serializable; 81 | -- Alice хочет увеличить зарплату всех сотрудников на 10% 82 | select sum(salary) 83 | from employee 84 | where department = 1; 85 | -- 86 | 300 87 | begin transaction isolation level Serializable; 88 | -- Bob в это же время нанимает нового сотрудника 89 | select sum(salary) 90 | from employee 91 | where department = 1; 92 | -- 93 | 300 94 | 95 | insert into employee 96 | values (4, 70, 1); 97 | commit; 98 | update employee set salary = salary * 1.1 where department = 1; 99 | -- 100 | UPDATE 3 101 | commit; 102 | select sum(salary) from employee; 103 | sum 104 | -- 105 | 400 106 | ``` 107 | 108 | Для предотвращения такой ситуации можно использовать Serializable уровень изоляции транзакций, при котором транзакции могут успешно завершиться в том и только том случае, если они бы смогли успешно завершиться ровно с тем же итогом, выполняясь последовательно. 109 | ```sql 110 | drop table employee; 111 | create table employee (id int, salary int, department int); 112 | 113 | insert into employee 114 | values (1, 100, 1), (2, 150, 1), (3, 50, 1); 115 | ``` 116 | 117 | Транзакция Элис теперь не сможет завершиться, так как в последовательном выполнении транзакций Элис и Боба друг за другом Боб получил бы остаток бюджета равный 40 пиастрам, а он уже получил остаток равный 70 пиастрам. В этом случае транзакция Элис получит ошибку сериализации и не выполнится. Бюджет останется неперерасходованным. 118 | ```sql 119 | begin transaction isolation level Serializable; 120 | -- Alice хочет увеличить зарплату всех сотрудников на 10% 121 | select sum(salary) 122 | from employee 123 | where department = 1; 124 | -- 125 | 300 126 | begin transaction isolation level Serializable; 127 | -- Bob в это же время нанимает нового сотрудника 128 | select sum(salary) 129 | from employee 130 | where department = 1; 131 | -- 132 | 300 133 | 134 | insert into employee 135 | values (4, 70, 1); 136 | commit; 137 | update employee set salary = salary * 1.1 where department = 1; 138 | -- 139 | ERROR: 40001: could not serialize access due to read/write dependencies among transactions 140 | DETAIL: Reason code: Canceled on identification as a pivot, during write. 141 | HINT: The transaction might succeed if retried. 142 | LOCATION: OnConflict_CheckForSerializationFailure, predicate.c:4677 143 | rollback; 144 | select sum(salary) from employee; 145 | sum 146 | -- 147 | 370 148 | ``` 149 | -------------------------------------------------------------------------------- /vacuum/heap_file_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yum-install-brains/postgresql-spot/1ead800a6aa2c3bf5171871c2bcf5273e927a8a6/vacuum/heap_file_page.png -------------------------------------------------------------------------------- /vacuum/postgresql_vacuum.md: -------------------------------------------------------------------------------- 1 | ## VACUUM 2 | 3 | - [VACUUM](#) 4 | - [Введение](#) 5 | - [VACUUM vs VACUUM FULL](#) 6 | - [Неблокирующий VACUUM](#) 7 | - [Блок 1](#) 8 | - [Блок 2](#) 9 | - [Блок 3](#) 10 | - [Visibility Map](#) 11 | - [Ленивый режим](#) 12 | - [Жадный режим](#) 13 | - [Запрос для получения pg_class.relfrozenxid и pg_database.datfrozenxid](#) 14 | - [AUTOVACUUM](#) 15 | - [Известные баги и их workaround](#) 16 | - [Ссылки](#) 17 | 18 | #### Введение 19 | VACUUM – это процесс обслуживания PostgreSQL, двумя основными задачами коготоро являются **удаление мертвых строк** и **заморозка идентификаторов транзакций**. Процесс можно запустить руками, но также он запускается и автоматически демоном AUTOVACUUM. 20 | 21 | Есть две разновидности VACUUM: 22 | - Неблокирующий VACUUM (или просто VACUUM) 23 | - удаляет мертвые строки на всех страницах файла таблицы, 24 | - удаляет строки индекса, ссылающиеся на мертвые строки таблицы, 25 | - дефрагментирует живые строки в рамках каждой страницы, 26 | - замораживает старые идентификаторы транзакций по необходимости, 27 | - обновляет замороженный идентификатор транзакции (frozen txid), 28 | - обновляет FSM и VM, 29 | - обновляет статистику (pg_stat_all_tables и тд). 30 | 31 | При этом не блокируя другие транзакции к этой таблице. 32 | 33 | - Блокирующий VACUUM (VACUUM FULL): 34 | - удаляет мертвые строки и индексы, ссылающиеся на мертвые строки, 35 | - дефрагментирует живые строки во всем файле (т.е. дефрагментирует сами страницы в файле, убирая пустые или не до конца заполненные страницы и высвобождая память системе. Все дефрагментированные страницы записываются в новый файл таблицы, а старый файл после операции удаляется), 36 | - перестраивает все индексы по таблице, 37 | - обновляет FSM и VM, 38 | - обновляет статистику. 39 | 40 | При этом блокируя все транзакции к этой таблице. 41 | 42 | При работе VACUUM должен просканировать всю таблицу, это довольно дорогая операция. В версии 8.4 была добавлена Visibility Map, которая позволила VACUUM пропускать страницы, не содержащие мертвых строк. В версии 9.6 такая же оптимизация на основе Visibility Map была сделана для процесса заморозки транзакций. 43 | 44 | #### VACUUM vs VACUUM FULL 45 | Псевдокод процесса VACUUM FULL 46 | ```java 47 | (1) FOR each table 48 | (2) Acquire AccessExclusiveLock lock for the table 49 | (3) Create a new table file 50 | (4) FOR each live tuple in the old table 51 | (5) Copy the live tuple to the new table file 52 | (6) Freeze the tuple IF necessary 53 | END FOR 54 | (7) Remove the old table file 55 | (8) Rebuild all indexes 56 | (9) Update FSM and VM 57 | (10) Update statistics 58 | Release AccessExclusiveLock lock 59 | END FOR 60 | (11) Remove unnecessary clog files and pages if possible 61 | ``` 62 | 63 | Псевдокод процесса неблокирующего VACUUM 64 | ```java 65 | (1) FOR each table 66 | (2) Acquire ShareUpdateExclusiveLock lock for the target table 67 | 68 | /* The first block */ 69 | (3) Scan all pages to get all dead tuples, and freeze old tuples if necessary 70 | (4) Remove the index tuples that point to the respective dead tuples if exists 71 | 72 | /* The second block */ 73 | (5) FOR each page of the table 74 | (6) Remove the dead tuples, and Reallocate the live tuples in the page 75 | (7) Update FSM and VM 76 | END FOR 77 | 78 | /* The third block */ 79 | (8) Truncate the last page if possible 80 | (9) Update both the statistics and system catalogs of the target table 81 | Release ShareUpdateExclusiveLock lock 82 | END FOR 83 | 84 | /* Post-processing */ 85 | (10) Update statistics and system catalogs 86 | (11) Remove both unnecessary files and pages of the clog if possible 87 | ``` 88 | #### Неблокирующий VACUUM 89 | ##### Блок 1 90 | Первый блок производит операции заморозки идентификаторов транзакций и удаления строк индексов, ссылающихся на мертвые строки таблицы. 91 | 92 | Сперва, PostgreSQL сканирует таблицу для составления списка мертвых строк и замораживает транзакции старых строк, если возможно. Список хранится в maintenance_work_mem в оперативной памяти. Если maintenance_work_mem переполняется, PostgreSQL переходит к шагам (4)-(7), затем опять возвращается к шагу (3) и возобновляет сканирование. 93 | 94 | После сканирования производится операция удаления строк индексов, ссылающихся на мертвые строки таблицы. 95 | 96 | ##### Блок 2 97 | Во втором блоке происходит постраничное удаление мертвых строк и постраничное обновление FSM, VM. Процесс продолжается, пока не будут обработаны все страницы. 98 | ![alt text](http://www.interdb.jp/pg/img/fig-6-01.png "Page structure") 99 | 100 | ##### Блок 3 101 | После окончания VACUUM процесса, PostgreSQL обновляет статистические таблицы и таблицы системного каталога, связанные с процессом VACUUM. 102 | 103 | ##### Visibility Map 104 | VM есть у каждой таблицы и содержит информацию по видимости страниц таблицы (1 - все строки страницы видимы, 0 - не все строки страницы видимы). В страницы, отмеченные полностью видимыми, VACUUM может не заходить (тк в них нет мертвых строк). 105 | 106 | Начиная с версии 9.6, в VM также добавилась информация о том, заморожены ли строки на странице или нет. 107 | 108 | Процесс заморозки может работать в двух режимах в зависимости от условий – ленивом режиме и жадном режиме. В основном заморозка происходит в ленивом режиме. 109 | 110 | В ленивом режиме процесс заморозки сканирует только страницы, содержащие мертвые строки (используя VM). 111 | 112 | В жадном режиме происходит полное сканирование всех страниц, независимо от наличия мертвых строк. 113 | 114 | ##### Ленивый режим 115 | В начале процесса заморозки, PostgreSQL вычисляет 116 | 117 | freezeLimit_txid = (OldestXmin − vacuum_freeze_min_age) 118 | OldestXmin - самая старая из активных транзакций на момент запуска VACUUM. vacuum_freeze_min_age - конфигурационный параметр. 119 | 120 | Далее, все транзакции, t_xmin которых меньше, чем freezeLimit_txid, замораживаются. 121 | 122 | Если в момент запуска VACUUM больше нет активных транзакций, OldestXmin будет равна транзакции, в которой запустился VACUUM. 123 | ![alt text](http://www.interdb.jp/pg/img/fig-6-03.png "Freeze") 124 | В примере с картинки выше Tuple 1,2,3,7,8 будут заморожены, а tuple 1,7 вдобавок почищены, т.к. они являютя мертвыми строками. Tuple 4,5,6 будут пропущены в связи с VM. 125 | 126 | ##### Жадный режим 127 | Как было показано выше, ленивый режим может пропускать некоторые строки, требующие заморозки. Жадный режим нужен для того, чтобы компенсировать это. Жадный режим сканирует все страницы во всей таблице. 128 | 129 | Жадный режим применяется, если удовлетворяется следующее условие 130 | 131 | pg_database.datfrozenxid < (OldestXmin − vacuum_freeze_table_age) 132 | pg_database.datfrozenxid представляет собой значение из pg_database и хранит самый старый замороженный txid для каждой базы данных. 133 | 134 | После заморозки каждой таблицы, значение pg_class.relfrozenxid обновляется для этой таблицы. Также, после заморозки таблицы по необходимости обновляется значение pg_database.datfrozenxid. 135 | ![alt text](http://www.interdb.jp/pg/img/fig-6-05.png "Freeze database") 136 | 137 | Заморозку конкретной таблицы можно выполнить явно, запустив команду 138 | 139 | VACUUM Freeze 140 | В таком случае будут заморожены все строки, чей t_xmin меньше OldestXmin (а не OldestXmin - vacuum_freeze_min_age в случае автовакуума). 141 | 142 | В версии 9.6 жадный режим был оптимизирован. Теперь в VM содержится информация о страницах, содержащих только замороженные строки (они могли быть уже заморожены в ленивом режиме). Такие страницы пропускаются при заморозке. 143 | 144 | ![alt text](http://www.interdb.jp/pg/img/fig-6-06.png "eager database") 145 | 146 | ##### Запрос для получения pg_class.relfrozenxid и pg_database.datfrozenxid 147 | ```sql 148 | testdb=# VACUUM table_1; 149 | VACUUM 150 | 151 | testdb=# SELECT n.nspname as "Schema", c.relname as "Name", c.relfrozenxid 152 | FROM pg_catalog.pg_class c 153 | LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace 154 | WHERE c.relkind IN ('r','') 155 | AND n.nspname <> 'information_schema' AND n.nspname !~ '^pg_toast' 156 | AND pg_catalog.pg_table_is_visible(c.oid) 157 | ORDER BY c.relfrozenxid::text::bigint DESC; 158 | Schema | Name | relfrozenxid 159 | ------------+-------------------------+-------------- 160 | public | table_1 | 100002000 161 | public | table_2 | 1846 162 | pg_catalog | pg_database | 1827 163 | pg_catalog | pg_user_mapping | 1821 164 | pg_catalog | pg_largeobject | 1821 165 | 166 | ... 167 | 168 | pg_catalog | pg_transform | 1821 169 | (57 rows) 170 | 171 | testdb=# SELECT datname, datfrozenxid FROM pg_database WHERE datname = 'testdb'; 172 | datname | datfrozenxid 173 | ---------+-------------- 174 | testdb | 1821 175 | (1 row) 176 | ``` 177 | 178 | #### AUTOVACUUM 179 | Автовакуум - демон автоматического запуска процедуры VACUUM. Он запускается каждые autovacuum_naptime секунд и порождает autovacuum_max_works число воркеров. Воркеры производят неблокирующий VACUUM. 180 | 181 | #### Известные баги 182 | - [ERROR: found multixact from before relminmxid](http://www.postgresql-archive.org/ERROR-found-multixact-from-before-relminmxid-td6015389.html) и [pgsql: Don't mark pages all-visible spuriously](http://www.postgresql-archive.org/pgsql-Don-t-mark-pages-all-visible-spuriously-td6019854.html). Баг есть в версии 9.6 и выше. Проблема в том, что во время выполнения операции VACUUM, блоки, содержащие строки заблокированные [мультитранзакциями](https://postgrespro.ru/docs/postgrespro/9.6/routine-vacuuming), могут быть ошибочно признаны полностью видимыми. Поэтому вакуум не заморозит старые строки в таких блоках и через какое-то время они станут старше, чем relminmxid. 183 | [Исправлено](https://www.postgresql.org/docs/10/static/release-10-4.html) в версии 10.4. Лечится двумя способами: а) пересоздать таблицу, например с помощью pg_repack б) найти некорректные строки в таблице (`select * from table where xid < relfrozenxid and xid != 0`), удалить и повторно вставить их. 184 | - [found xmin from before relfrozenxid on pg_catalog.pg_authid](https://www.postgresql.org/message-id/20180525203736.crkbg36muzxrjj5e@alap3.anarazel.de) и [http://www.postgresql-archive.org/Error-on-vacuum-xmin-before-relfrozenxid-td6021953.html](Error on vacuum: xmin before relfrozenxid). Проблема может возникать при выполнении VACUUM общих таблиц (из pg_catalog) и приводит к невозможности выполнения VACUUM. Проблема вызвана тем что relfrozenxid в relcache и в каталоге отличаются. Это приводит к некорректной работе VACUUM. Воркэраундом является [удаление файла](https://www.postgresql.org/message-id/20180619165837.wxtitjqkpusjbidv%40alap3.anarazel.de) `global/pg_internal.init`. 185 | 186 | 187 | #### Источники 188 | [VACUUM processing](http://www.interdb.jp/pg/pgsql06.html) 189 | -------------------------------------------------------------------------------- /wal/wal.md: -------------------------------------------------------------------------------- 1 | ## WAL 2 | 3 | WAL – it is a history log of all changes and actions in a database system so as to ensure that no data has been lost due to failures, such as a power failure, or some other server failure that causes the server crash. As the log contains sufficient information about each transaction executed already, the database server should be able to recover the database cluster by replaying changes and actions in the transaction log in case of the server crash. WAL files also used in Point-in-Time Recovery (PITR) and Streaming Replication (SR). 4 | 5 | Without WAL all data in shared_buffers would be lost in case of server failure. PostgreSQL writes all modifications as history data into a persistent storage, to prepare for failures. In PostgreSQL, the history data are known as WAL data. 6 | 7 | WAL records are written into the in-memory WAL buffer by change operations such as insertion, deletion, or commit action. They are immediately written into a WAL segment file on the storage when a transaction commits/aborts. LSN (Log Sequence Number) of WAL record represents the location where its record is written on the transaction log. LSN of record is used as the unique id of WAL record. 8 | 9 | Query to find WAL file, which contains specified LSN: 10 | ```sql 11 | SELECT pg_walfile_name('1/00002D3E'); # In version 10 or later, "SELECT pg_walfile_name('1/00002D3E');" 12 | pg_xlogfile_name 13 | -------------------------- 14 | 000000010000000100000000 15 | (1 row) 16 | ``` 17 | 18 | ### REDO 19 | When we consider how database system recovers, there may be one question; what point does PostgreSQL start to recover from? The answer is REDO point; that is, the location to write the WAL record at the moment when the latest checkpoint is started. In fact, the database recovery processing is strongly linked to the checkpoint processing and both of these processing are inseparable. 20 | 21 | ### Full-Page Writes 22 | Suppose that the TABLE_A's page-data on the storage is corrupted, because the operating system has failed while the background-writer process has been writing the dirty pages. As XLOG records cannot be replayed on the corrupted page, we would need an additional feature. 23 | 24 | PostgreSQL supports a feature referred to as full-page writes to deal with such failures. If it is enabled, PostgreSQL writes a pair of the header-data and the entire page as XLOG record during the first change of each page after every checkpoint; default is enabled. In PostgreSQL, such a XLOG record containing the entire page is referred to as backup block (or full-page image). 25 | 26 | ### WAL segments 27 | A WAL segment is a 16 MB file, by default, and it is internally divided into pages of 8192 bytes (8 KB). The WAL segment filename is in hexadecimal 24-digit number. 28 | 29 | ### WAL buffers flush triggers 30 | Writing WAL from buffer to disk may be caused when any one of the following occurs: 31 | - One running transaction has committed or has aborted. 32 | - The WAL buffer has been filled up with many tuples have been written. (The WAL buffer size is set to the parameter wal_buffers.) 33 | - A WAL writer process writes periodically. 34 | 35 | If one of above occurs, all WAL records on the WAL buffer are written into a WAL segment file regardless of whether their transactions have been committed or not. 36 | 37 | It is taken for granted that DML (Data Manipulation Language) operations write XLOG records, but so do non-DML operations. As described above, a commit action writes a XLOG record that contains the id of committed transaction. Another example may be a checkpoint action to write a XLOG record that contains general information of this checkpoint. Furthermore, SELECT statement creates XLOG records in special cases, though it does not usually create them. For example, if deletion of unnecessary tuples and defragmentation of the necessary tuples in pages occur by HOT(Heap Only Tuple) during a SELECT statement processing, the XLOG records of modified pages are written into the WAL buffer. 38 | 39 | ### WAL writer 40 | WAL writer is a background process to check the WAL buffer periodically and write all unwritten XLOG records into the WAL segments. The purpose of this process is to avoid burst of writing of XLOG records. If this process has not been enabled, the writing of XLOG records might have been bottlenecked when a large amount of data committed at one time. 41 | 42 | WAL writer is working by default and cannot be disabled. Check interval is set to the configuration parameter wal_writer_delay, default value is 200 milliseconds. 43 | 44 | ### Checkpoints and database recovery 45 | In PostgreSQL, the checkpointer (background) process performs checkpoints; its process starts when one of the following occurs: 46 | - The interval time set for checkpoint_timeout from the previous checkpoint has been gone over (the default interval is 300 seconds (5 minutes)). 47 | - In version 9.4 or earlier, the number of WAL segment files set for checkpoint_segments has been consumed since the previous checkpoint (the default number is 3). 48 | - In version 9.5 or later, the total size of the WAL segment files in the pg_xlog (in version 10 or later, pg_wal) has exceeded the value of the parameter max_wal_size (the default value is 1GB (64 files)). 49 | - PostgreSQL server stops in smart or fast mode. 50 | 51 | 52 | Its process also does it when a superuser issues CHECKPOINT command manually. 53 | 54 | Checkpoint not only flush shared buffers to disk, but also prepares database recovery (for more info read about pg_control file). 55 | 56 | Checkpointing creates the checkpoint record which contains the REDO point, and stores the checkpoint location and more into the pg_control file. Therefore, PostgreSQL enables to recover itself by replaying WAL data from the REDO point (obtained from the checkpoint record) provided by the pg_control file. When PostgreSQL starts up, it reads the pg_control file at first. 57 | 58 | ### Database recovery 59 | (1) PostgreSQL reads all items of the pg_control file when it starts. If the state item is in 'in production', PostgreSQL will go into recovery-mode because it means that the database was not stopped normally; if 'shut down', it will go into normal startup-mode. 60 | (2) PostgreSQL reads the latest checkpoint record, which location is written in the pg_control file, from the appropriate WAL segment file, and gets the REDO point from the record. If the latest checkpoint record is invalid, PostgreSQL reads the one prior to it. If both records are unreadable, it gives up recovering by itself. (Note that the prior checkpoint is not stored from PostgreSQL 11.) 61 | (3) Proper resource managers read and replay XLOG records in sequence from the REDO point until they come to the last point of the latest WAL segment. When a XLOG record is replayed and if it is a backup block, it will be overwritten on the corresponding table's page regardless of its LSN. Otherwise, a (non-backup block's) XLOG record will be replayed only if the LSN of this record is larger than the pd_lsn of a corresponding page. 62 | 63 | ### WAL files management 64 | PostgreSQL writes XLOG records to one of the WAL segment files stored in the pg_xlog subdirectory (in version 10 or later, pg_wal subdirectory), and switches for a new one if the old one has been filled up. The number of the WAL files will vary depending on several configuration parameters, as well as server activity. 65 | 66 | WAL segment switches occur when one of the following occurs: 67 | - WAL segment has been filled up. 68 | - The function pg_switch_xlog has been issued. 69 | - archive_mode is enabled and the time set to archive_timeout has been exceeded. 70 | 71 | Switched file is usually recycled (renamed and reused) for future use but it may be removed later if not necessary. 72 | 73 | The number of WAL files adaptively changes depending on the server activity. If the amount of WAL data writing has constantly increased, the estimated number of the WAL segment files as well as the total size of WAL files also gradually increase. In the opposite case (i.e. the amount of WAL data writing has decreased), these decrease. 74 | 75 | If the total size of the WAL files exceeds max_wal_size, a checkpoint will be started. 76 | 77 | ### WAL archiving 78 | Continuous Archiving is a feature that copies WAL segment files to archival area at the time when WAL segment switches, and is performed by the archiver (background) process. The copied file is called an archive log. This feature is usually used for hot physical backup and PITR (Point-in-Time Recovery) described in Chapter 10. 79 | 80 | The path of archival area is set to the configuration parameter archive_command. For example, using the following parameter, WAL segment files are copied to the directory '/home/postgres/archives/' every time when each segment switches: 81 | ``` 82 | archive_command = 'cp %p /home/postgres/archives/%f' 83 | ``` 84 | where, placeholder %p is copied WAL segment, and %f is archive log. 85 | 86 | ### Sources 87 | - [interdb.jp blog](http://www.interdb.jp/pg/pgsql09.html) 88 | - [DBA2](https://www.youtube.com/watch?v=GghMySWRH48&t=1s&index=8&list=PLaFqU3KCWw6JgufXBiW4dEB2-tDpmOXPH) 89 | --------------------------------------------------------------------------------