Оптимизация производительности

На производительность запросов влияет множество факторов. Часть из них может контролироваться пользователем или администратором, на другие наибольшее влияние оказывает базовая архитектуры системы. В этой статье представлены несколько рекомендаций по настройке производительности PostgreSQL.

Оптимизация потребления ресурсов

В этом разделе содержатся общие рекомендации по значениям конфигурационных параметров, которые можно использовать в качестве отправной точки. Оптимальные значения параметров зависят от нагрузки конкретного кластера и используемого оборудования. Обратите внимание, что существуют конфигураторы с открытым исходным кодом, которые рассчитывают значения основных настроек PostgreSQL на основе указанной конфигурации оборудования. Некоторые из них доступны онлайн.

Настройки памяти

В таблице ниже приведены базовые рекомендации по значениям настроек памяти в зависимости от размера памяти сервера.

Название параметра Описание Рекомендуемое значение

effective_cache_size

Определяет представление планировщика об эффективном размере дискового кeша, доступном для одного запроса. Это значение учитывается при оценке стоимости использования индекса. Более высокое значение увеличивает вероятность использования сканирования индекса, более низкое значение повышает вероятность применения последовательного сканирования. При настройке этого параметра следует учитывать как разделяемые буферы PostgreSQL (shared buffers), так и часть дискового кеша ядра, которая будет использоваться для файлов данных PostgreSQL, хотя некоторые данные могут существовать и там, и там. Также необходимо учитывать ожидаемое количество одновременных запросов к разным таблицам, поскольку им придется делить доступное пространство. Этот параметр не влияет на размер общей памяти, выделяемой PostgreSQL, и не резервирует дисковый кеш ядра. Он используется только в целях оценки. Система также не предполагает, что данные остаются в дисковом кеше между запросами. Если значение effective_cache_size указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байт, обычно это 8 КБ

2/3 RAM

shared_buffers

Устанавливает объем памяти, который сервер базы данных использует для буферов в разделяемой памяти. Если у вас есть выделенный сервер базы данных с 1 ГБ или более оперативной памяти, разумное начальное значение для shared_buffers составляет 25% памяти системы. Для некоторых типов рабочих нагрузок эффективны даже более высокие настройки для shared_buffers, но поскольку PostgreSQL также полагается на кеш операционной системы, маловероятно, что выделение более 40% RAM для shared_buffers будет работать лучше, чем меньший объем. Большие настройки для shared_buffers обычно требуют соответствующего увеличения параметра max_wal_size, чтобы распределить процесс записи большого количества новых или измененных данных на более длительный период времени. Если значение параметра указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байт, обычно это 8 КБ

RAM/4

temp_buffers

Устанавливает максимальный объем памяти, используемый для временных буферов в каждой сессии. Эти существующие только в пределах сессии буферы используются для доступа к временным таблицам. Если значение параметра указано без единиц измерения, оно считается заданным в блоках, размер которых равен BLCKSZ байт, обычно это 8 КБ

256 МБ

work_mem

Устанавливает базовый максимальный объем памяти, который будет использоваться операцией запроса (например, сортировкой или хеш-таблицей) перед записью во временные файлы на диске. Обратите внимание, что для сложного запроса несколько операций сортировки или хеширования могут выполняться параллельно. Каждой операции будет разрешено использовать столько памяти, сколько указано в этом значении, прежде чем начнется запись во временные файлы. Несколько запущенных сессий также могут выполнять такие операции одновременно. Следовательно, общий объем используемой памяти может во много раз превышать значение work_mem. Если это значение указано без единиц измерения, оно считается заданным в килобайтах

RAM/32

maintenance_work_mem

Указывает максимальный объем памяти, который будет использоваться операциями обслуживания базы данных, такими как VACUUM, CREATE INDEX и ALTER TABLE ADD FOREIGN KEY. Если это значение указано без единиц измерения, оно считается заданным в килобайтах. Поскольку в сессии одновременно может выполняться только одна из этих операций, а в кластере они обычно не выполняются параллельно, можно безопасно установить это значение значительно больше, чем work_mem. Более высокое значение может повысить производительность операций вакуумирования и восстановления базы данных из резервных копий .

Обратите внимание, что при запуске автовакуума эта память может быть выделена до autovacuum_max_workers раз, поэтому будьте осторожны и не устанавливайте слишком высокое значение. Может быть полезно использовать конфигурационный параметр autovacuum_work_mem для управления автовакуумом

RAM/16

Все описанные параметры, кроме autovacuum_work_mem и temp_buffers, можно установить в разделе ADPG configurations дерева Configuration на странице Clusters → Кластер ADPG → Services → ADPG → Primary configuration в пользовательском интерфейсе ADCM.

После изменения описанных настроек выполните действие Reconfigure & Restart сервиса ADPG, чтобы применить изменения.

Настройки, связанные с операциями ввода-вывода

В таблице ниже перечислены параметры, связанные с операциями ввода-вывода, изменение значений которых может значительно влиять на производительность.

Название параметра Описание

fsync

Если этот параметр включен, сервер PostgreSQL пытается убедиться, что обновления физически записываются на диск, выполняя системные вызовы fsync() или различные эквивалентные методы (см. wal_sync_method). Это гарантирует, что кластер базы данных сможет восстановиться до согласованного состояния после сбоя операционной системы или оборудования. Хотя выключение fsync часто повышает производительность, это может привести к невосстановимому повреждению данных в случае сбоя питания или сбоя системы. Отключать fsync рекомендуется только в том случае, если вы можете легко воссоздать всю базу данных из внешних источников.

Примеры, когда отключение fsync неопасно, включают первоначальное наполнение нового кластера базы данных из файла резервной копии, обработку массива данных, после которой базу данных можно удалить и создать заново, или для копии базы данных, доступной только для чтения, которая часто пересоздается и не используется для отработки сбоя. Высококачественное оборудование само по себе не является достаточным основанием для отключения fsync.

Во многих ситуациях отключение synchronous_commit для некритических транзакций может обеспечить значительно больший выигрыш в производительности, чем отключение fsync без сопутствующего риска повреждения данных.

Параметр fsync можно установить в разделе ADPG configurations на вкладке Clusters → Кластер ADPG → Services → ADPG → Primary configuration (см. Конфигурационные параметры)

synchronous_commit

Определяет, после завершения какого уровня обработки WAL сервер базы данных вернет клиенту сообщение об успехе. См. synchronous_commit.

Установка для этого параметра значения off не создает риска возникновения несогласованности базы данных: сбой операционной системы или базы данных может привести к потере последних зафиксированных транзакций, но состояние базы данных будет таким же, как если бы эти транзакции были прерваны. Таким образом, отключение synchronous_commit может быть полезной альтернативой, когда производительность важнее, чем гарантия сохранности каждой транзакции. За дополнительной информацией обратитесь к статье Asynchronous commit.

Поведение каждой транзакции определяется значением, действующим в момент её фиксирования. Таким образом, есть возможность фиксировать некоторые транзакции синхронно, а другие — асинхронно. Например, чтобы зафиксировать одну транзакцию из нескольких команд асинхронно, когда по умолчанию выбрано другое значение, выполните в этой транзакции SET LOCAL synchronous_commit TO OFF

checkpoint_completion_target

Указывает целевое время завершения контрольной точки как долю общего времени между контрольными точками. Значение по умолчанию — 0.9, что распределяет создание контрольной точки по большей части доступного интервала, обеспечивая достаточно стабильную нагрузку ввода-вывода, а также оставляет некоторое время на завершение контрольной точки. Уменьшать этот параметр не рекомендуется, поскольку это приводит к более быстрому завершению создания контрольной точки, и как результат к более высокой скорости ввода-вывода во время создания контрольной точки, за которой следует период меньшего количества операций ввода-вывода между завершением контрольной точки и следующей запланированной контрольной точкой

effective_io_concurrency

Устанавливает количество дисковых операций ввода-вывода, которые могут выполняться одновременно. Увеличение этого значения увеличит количество операций ввода-вывода, которые сессия PostgreSQL пытается инициировать параллельно. Допустимый диапазон — от 1 до 1000 или 0, чтобы отключить выполнение асинхронных запросов ввода-вывода. В настоящее время этот параметр влияет только на сканирование по битовой карте.

Для магнитных дисков хорошей отправной точкой для этого параметра является количество отдельных дисков, составляющих массив RAID 0 или RAID 1, в котором размещена базы данных. Для RAID 5 следует исключить диск четности. Если база данных часто перегружена несколькими запросами, выполняемыми в одновременных сессиях, меньших значений может быть достаточно, чтобы дисковый массив был занят. Значение, превышающее необходимое для занятости дисков, приведет только к дополнительной нагрузке на процессор. SSD-накопители часто могут обрабатывать множество одновременных запросов, поэтому оптимальное значение может исчисляться сотнями.

Это значение можно переопределить для таблиц в определенном табличном пространстве, задав одноименный параметр табличного пространства (см. ALTER TABLESPACE )

random_page_cost

Устанавливает приблизительную стоимость чтения одной страницы с диска, извлекаемой непоследовательно, для планировщика. Значение по умолчанию — 4.0.

Уменьшение random_page_cost относительно seq_page_cost приведет к тому, что система отдаст предпочтение сканированию индекса, его повышение сделает сканирование индекса относительно более дорогим. Вы можете повысить или понизить оба значения одновременно, чтобы изменить стоимость операций ввода-вывода по отношению к стоимости операций процессора.

Произвольный доступ к механическому дисковому хранилищу обычно обходится намного дороже последовательного доступа, более чем в четыре раза. Однако используется более низкое значение по умолчанию (4.0), поскольку предполагается, что большая часть произвольного доступа к диску, например индексированного чтения, выполняется из кеша. Таким образом, можно считать, что значение по умолчанию моделирует ситуацию, когда произвольный доступ в 40 раз медленнее последовательного, при этом ожидается, что 90% операций произвольного чтения выполняются из кеша.

Если вы считаете, что доля операций, выполняемых из кеша, равная 90 %, является неправильным предположением для вашей рабочей нагрузки, вы можете увеличить random_page_cost, чтобы лучше отразить истинную стоимость произвольного чтения. Если ваши данные, скорее всего, будут полностью находиться в кеше, например, когда размер базы данных меньше, чем общая память сервера, уменьшение random_page_cost может быть целесообразным. Для хранилища, имеющего низкую стоимость произвольного чтения по сравнению с последовательным, например SSD, лучше выбрать более низкое значение random_page_cost, например, 1.1.

Это значение можно переопределить для таблиц и индексов в определенном табличном пространстве, задав одноименный параметр табличного пространства (см. ALTER TABLESPACE)

Мониторинг загрузки диска

В ADPG/PostgreSQL для каждой таблицы создается первичный дисковый heap-файл, в котором хранится большая часть данных. Если в таблице есть столбцы с потенциально большими значениями, с ней также может быть связан TOAST-файл, который используется для хранения этих больших значений. Для каждой TOAST-таблицы на диске будет храниться один валидный индекс. Также могут быть индексы, связанные с базовой таблицей. PostgreSQL хранит каждую таблицу и индекс в отдельном дисковом файле. Если размер файла превышает 1 ГБ, будут созданы дополнительные файлы. Соглашения об именах для этих файлов описаны в статье Database file layout.

Вы можете осуществлять мониторинг дискового пространства тремя способами:

Использование SQL-функции для определения размеров объектов базы данных

Функции SQL наиболее просты в использовании и обычно рекомендуется применять их. Они перечислены в таблице Database object size functions.

Например, функция pg_relation_size() возвращает указанный размер таблицы (в данном примере — pgbench_accounts) в байтах. Вы можете использовать psql для выполнения следующего запроса:

SELECT pg_relation_size('pgbench_accounts');

Результат:

 pg_relation_size
------------------
          671481856

Функция pg_size_pretty() преобразует размер в байтах в более удобный для чтения формат с единицами измерения: байты, КБ, МБ, ГБ или ТБ:

SELECT pg_size_pretty(pg_relation_size('pgbench_accounts'));

Результат:

 pg_size_pretty
----------------
 640 MB

Можно использовать функцию pg_database_size() для вычисления общего дискового пространства, занимаемого указанной базой данных (в данном примере example):

SELECT pg_size_pretty (pg_database_size('example'));

Результат:

 pg_size_pretty
----------------
 756 MB

Просмотр системных каталогов

Можно выполнить запросы в psql, чтобы увидеть использование диска таблицей. Для достоверности результатов над таблицей предварительно должна быть выполнена операция VACUUM или ANALYZE. Следующий запрос определяет количество страниц (столбец relpages), принадлежащих таблице pgbench_accounts:

SELECT pg_relation_filepath(oid), relpages FROM pg_class WHERE relname = 'pgbench_accounts';

Результат:

 pg_relation_filepath | relpages
----------------------+----------
 base/19191/19244     |    81968

Каждая страница обычно имеет размер 8 КБ. Запрос вернул тот же результат, который возвращает функция pg_relation_size(): КБ. Обратите внимание, что relpages обновляется только с помощью VACUUM, ANALYZE и нескольких команд DDL, таких как CREATE INDEX. Путь к файлу (столбец pg_relation_filepath) представляет интерес, если вы хотите напрямую просмотреть файл таблицы на диске.

Чтобы определить пространство, занимаемое таблицами TOAST, связанными с таблицей my_table, используйте следующий запрос:

SELECT relname, relpages
FROM pg_class,
(SELECT reltoastrelid
FROM pg_class
WHERE relname = 'my_table') AS ss
WHERE oid = ss.reltoastrelid OR
oid = (SELECT indexrelid
FROM pg_index
WHERE indrelid = ss.reltoastrelid)
ORDER BY relname;

Результат:

        relname        | relpages
-----------------------+----------
 pg_toast_295925       |        0
 pg_toast_295925_index |        1

Запрос ниже отображает размер индекса для таблицы pgbench_branches:

SELECT c2.relname, c2.relpages
FROM pg_class c, pg_class c2, pg_index i
WHERE c.relname = 'pgbench_branches' AND
      c.oid = i.indrelid AND
      c2.oid = i.indexrelid
ORDER BY c2.relname;

Результат:

        relname        | relpages
-----------------------+----------
 ind_bbalance          |        2
 pgbench_branches_pkey |        2

Чтобы найти самые большие таблицы и индексы, используйте следующий запрос:

SELECT relname, relpages
FROM pg_class
ORDER BY relpages DESC LIMIT 5;

Результат:

       relname         | relpages
-----------------------+----------
 pgbench_accounts      |    81968
 pgbench_accounts_pkey |    13713
 pg_proc               |       94
 pg_depend             |       65
 pg_toast_2618         |       63

Использование модуля oid2name

Утилита oid2name помогает администраторам проверять файловую структуру, используемую ADPG/PostgreSQL.

oid2name подключается к целевой базе данных и извлекает OID, файловый узел (filenode — уникальный числовой идентификатор, используемый для именования файлов на диске, принадлежащих отношению) и/или информацию об имени таблицы:

$ oid2name

Результат:

All databases:
     Oid  Database Name  Tablespace
-----------------------------------
  19191        example  pg_default
  14486       postgres  pg_default
  14485      template0  pg_default
      1      template1  pg_default

Например, можно рассмотреть базу данных example из списка, приведенного выше. Для этого измените текущий каталог на каталог базы данных:

$ cd /pg_data1/adpg16/base/19191

где 19191 — Oid базы данных example из вывода команды, приведенного выше.

Отобразите первые 5 объектов базы данных в табличном пространстве по умолчанию, отсортированные по размеру (самые большие):

$ ls -lS * | head -5

Результат:

-rw------- 1 postgres postgres 671481856 Jul 31 10:54 19244
-rw------- 1 postgres postgres 112336896 Jul 31 11:03 19261
-rw------- 1 postgres postgres    770048 Jul 31 10:43 1255
-rw------- 1 postgres postgres    532480 Aug  1 08:11 2608
-rw------- 1 postgres postgres    516096 Jul 31 10:43 2838

Определите, что за файл 19244:

$ oid2name -d example -f 19244

Результат:

  Filenode        Table Name
----------------------------
     19244  pgbench_accounts

Для утилиты oid2name требуется работающий сервер базы данных с неповрежденными системными каталогами. В ситуациях со значительными повреждением базы данных её использование ограничено.

Наполнение базы данных

Этот раздел содержит рекомендации о том, как эффективно добавлять большие объемы данных при первом заполнении базы или в процессе её использования.

Отключение автофиксации транзакций

При использовании нескольких операторов INSERT отключите автофиксацию и выполните одну фиксацию в конце транзакции. В обычном SQL это означает выполнение BEGIN в начале и COMMIT в конце. Некоторые клиентские библиотеки могут делать это автоматически, в этом случае нужно проверить, что это действительно так. Если вы разрешите фиксацию каждого оператора INSERT отдельно, PostgreSQL проделает большую работу для каждой добавленной строки. Дополнительным преимуществом выполнения всех вставок за одну транзакцию является то, что в случае сбоя добавления одной строки, вставка всех строк будет отменена. Таким образом, вы не получите базу с частично загруженными данными.

Использование COPY

Используйте COPY, чтобы загрузить все строки одной командой вместо выполнения серии команд INSERT. Команда COPY оптимизирована для загрузки большого количества строк. Она менее гибкая, чем INSERT, но имеет значительно меньшие издержки при больших загрузках данных. Поскольку COPY — это одна команда, нет необходимости отключать автофиксацию, если используется этот метод для заполнения таблицы.

Если невозможно использовать COPY, будет полезно применить PREPARE для создания подготовленного оператора INSERT, а затем использовать EXECUTE столько раз, сколько необходимо. Это позволит избежать накладных расходов на повторный анализ и планирование INSERT.

Обратите внимание, что загрузка большого количества строк с помощью COPY почти всегда происходит быстрее, чем с помощью INSERT, даже если используется PREPARE и несколько вставок объединяются в одну транзакцию.

Если параметр wal_level имеет значение minimal, команда COPY работает быстрее всего, когда выполняется в той же транзакции, что и команды CREATE TABLE или TRUNCATE. В таких случаях писать WAL не нужно, поскольку в случае ошибки файлы, содержащие вновь загруженные данные, будут удалены.

Удаление индексов

Если вы загружаете данные в только что созданную таблицу, самый быстрый способ — добавить данные с помощью COPY, а затем создать индексы. Создание индекса для существующих данных занимает меньше времени, чем его последовательное обновление при добавлении каждой строки.

Если вам нужно добавить большие объемы данных в существующую таблицу, возможно, имеет смысл удалить индексы, загрузить таблицу, а затем воссоздать индексы. Обратите внимание, что производительность базы данных для других пользователей может снизиться, пока индексы отсутствуют. Кроме того, следует дважды подумать, прежде чем удалять уникальные индексы, так как без них не будут выполняться соответствующие проверки ключей.

Удаление ограничений внешних ключей

Как и в случае с индексами, ограничение внешнего ключа можно более эффективно проверять массово, чем построчно. Может быть полезно удалить ограничения внешнего ключа, загрузить данные и заново создать ограничения. В этом случае приходится выбирать между скоростью загрузки данных и потерей проверки ошибок при отсутствии ограничения.

Более того, когда вы загружаете данные в таблицу с существующими ограничениями внешнего ключа, каждая новая строка требует записи в списке ожидающих событий триггера сервера, поскольку именно срабатывающий триггер проверяет ограничение внешнего ключа строки. Загрузка многих миллионов строк может привести к тому, что очередь событий триггера переполнит доступную память, что приведет к недопустимой нагрузке на файл подкачки или даже сбою команды. Поэтому может быть необходимо, а не просто желательно, удалять и восстанавливать внешние ключи при загрузке больших объемов данных. Если временное удаление ограничения неприемлемо, единственным возможным решением может быть разделение операции загрузки на более мелкие транзакции.

Увеличение maintenance_work_mem

Временное увеличение параметра конфигурации maintenance_work_mem при загрузке больших объемов данных может улучшить ситуацию с производительностью. Это помогает ускорить выполнение команд CREATE INDEX и ALTER TABLE ADD FOREIGN KEY, но не влияет на скорость выполнения команды COPY, поэтому данный совет полезен только в том случае, если вы используете один или оба описанных выше метода. maintenance_work_mem находится в разделе ADPG configurations дерева Configuration на странице Clusters → Кластер ADPG → Services → ADPG → Primary configuration в пользовательском интерфейсе ADCM. См. Конфигурационные параметры.

Увеличение max_wal_size

Временное увеличение параметра конфигурации max_wal_size также может ускорить загрузку больших данных. Загрузка большого объема данных приводит к тому, что контрольные точки возникают чаще, чем обычно (задается параметром конфигурации checkpoint_timeout). При возникновении контрольной точки все "грязные" страницы должны быть сброшены на диск. Количество необходимых контрольных точек можно уменьшить, увеличив max_wal_size во время массовой загрузки данных. max_wal_size также находится в разделе ADPG configurations дерева Configuration на странице Clusters → Кластер ADPG → Services → ADPG → Primary configuration в пользовательском интерфейсе ADCM.

Отключение архивации WAL и создания бэкапов

При загрузке больших объемов данных в кластер, использующий архивацию WAL и потоковую репликацию, может быть быстрее создать новую базовую резервную копию после завершения загрузки, чем обрабатывать большой объем инкрементных данных WAL.

Чтобы отключить архивирование WAL, установите следующие параметры конфигурации:

Чтобы задать эти параметры, используйте раздел ADPG configurations на вкладке Clusters → Кластер ADPG → Services → ADPG → Primary configuration в пользовательском интерфейсе ADCM. См. Конфигурационные параметры.

Чтобы отключить создание бэкапов, выключите переключатель Enable backups, отображаемый на вкладке Primary Configuration сервиса ADPG (Clusters → Кластер ADPG → Services → ADPG → Primary configuration).

Для применения изменений этих параметров, выполните действие Reconfigure & Restart.

ВАЖНО
Отключение архивирования WAL делает резервные копии, сделанные ранее, непригодными для восстановления из архива и использования резервным сервером, и может привести к потере данных.

Эти настройки также ускоряют выполнение некоторых команд, поскольку команды не записывают WAL, если для параметра wal_level установлено значение minimal.

Выполнение ANALYZE по завершении

Если вы существенно изменили распределение данных в таблице, настоятельно рекомендуется запустить команду ANALYZE. Выполнение ANALYZE (или VACUUM ANALYZE) гарантирует, что у планировщика будет актуальная статистика о таблице. При отсутствии статистики или устаревшей статистике планировщик может принять неправильные решения во время планирования запроса, что приведет к снижению производительности. Обратите внимание, что если autovacuum включен, он может автоматически запустить ANALYZE.

Улучшение производительности pg_dump

Скрипты дамп-файлов, созданные с помощью pg_dump, автоматически применяют некоторые, но не все, из приведенных выше рекомендаций. Чтобы восстановить дамп как можно быстрее, нужно сделать несколько дополнительных действий вручную. Обратите внимание, что эти рекомендации следует применять при восстановлении дампа, а не при его создании.

По умолчанию pg_dump использует COPY. Кроме того, когда генерируется полный дамп схемы и данных, данные загружаются перед созданием индексов и внешних ключей. Таким образом, некоторые рекомендации применяются автоматически. Все, что вам нужно учитывать, это следующее:

  • Установите соответствующие (большие, чем обычно) значения для maintenance_work_mem и max_wal_size.

  • Если вы используете архивацию или потоковую репликацию WAL, отключите их во время восстановления, как описано в разделе Отключение архивации WAL и создания бэкапов.

  • Поэкспериментируйте с режимами параллельного создания дампа и восстановления утилит pg_dump и pg_restore и найдите оптимальное количество одновременных процессов. Параллельное копирование и восстановление данных, управляемое опцией -j, должно дать значительно более высокую производительность по сравнению с последовательным режимом.

  • В некоторых случаях может быть предпочтительно восстановить дамп за одну транзакцию. Для этого передайте параметр командной строки -1 или --single-transaction в psql или pg_restore. Команды COPY также будут выполняться быстрее, если вы ограничитесь одной транзакцией и отключите архивирование WAL. Обратите внимание, что при использовании этого режима даже малейшие ошибки откатят процесс восстановления полностью, что может привести к потере многих часов обработки.

  • Если на сервере базы данных доступно несколько процессоров (CPU), рассмотрите возможность использования опции --jobs утилиты pg_restore. Это позволяет одновременно загружать данные и создавать индексы.

  • Запустите ANALYZE после восстановления.

Дамп данных (data-only dump) также использует COPY, но он не удаляет и не воссоздает индексы и по умолчанию не работает с внешними ключами. Таким образом, при загрузке дампа, содержащего только данные, вы несете ответственность за удаление и воссоздание индексов и внешних ключей. По-прежнему полезно увеличивать max_wal_size при загрузке данных, но не maintenance_work_mem. maintenance_work_mem стоит менять только в том случае, если вы вручную воссоздаете индексы и внешние ключи. Также рекомендуется запустить ANALYZE в конце восстановления.

Нашли ошибку? Выделите текст и нажмите Ctrl+Enter чтобы сообщить о ней