F.24. in_memory

Расширение in_memory даёт возможность размещать данные в общей памяти Postgres Pro, используя таблицы в оперативной памяти, реализованные через обёртку сторонних данных.

Примечание

Это расширение нельзя использовать с автономными транзакциями.

Таблицы в оперативной памяти, или оперативные таблицы, организованы по индексу — строки таблиц хранятся в страницах листьев индекса-B-дерева, построенного по первичному ключу таблицы. Это решение даёт следующие плюсы:

  • Быстрый произвольный доступ по первичному ключу. Это может дать значительный выигрыш по скорости при работе с данными, когда требуется очень быстрый доступ на чтение и запись, особенно в многоядерных системах.

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

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

  • WAL, сохранение и репликация данных в оперативных таблицах в настоящее время не поддерживается.

  • Вторичные индексы не поддерживаются.

  • Поддерживаются уровни изоляции транзакции до REPEATABLE READ. Уровень SERIALIZABLE не поддерживается, вместо него используется REPEATABLE READ.

  • Оперативные таблицы не поддерживают TOAST и другие механизмы хранения больших кортежей. Так как размер страницы в памяти составляет один 1 КБ, а индекс-B-дерево должен содержать на странице минимум три кортежа, максимальная длина строки ограничена 304 байтами.

  • Когда строка удаляется из оперативной таблицы, соответствующая страница данных не освобождается. За подробностями обратитесь к Подразделу F.24.2.3.

F.24.1. Установка и подготовка

Чтобы включить оперативные таблицы в вашем кластере:

  1. Убедитесь, что включён модуль postgres_fdw.

  2. Добавьте in_memory в переменную shared_preload_libraries в файле postgresql.conf:

    shared_preload_libraries = 'in_memory'
  3. Создайте расширение in_memory, выполнив следующую команду:

    CREATE EXTENSION in_memory;

В результате будет создан сторонний сервер in_memory, для оперативных таблиц будет выделен отдельный сегмент в разделяемой памяти, и в нём будут подготовлены страницы для данных. Чтобы доступ к данным памяти был эффективным, они должны быть более компактными, по сравнению с данными на жёстком диске или SSD, так что размер страницы в памяти составляет всего 1 КБ. Сразу после создания расширения вы можете начать использовать оперативные таблицы, как рассказывается в Подразделе F.24.2.

Подсказка

Если требуется, вы можете увеличить объём памяти, выделяемый для оперативных таблиц. За подробностями обращайтесь к Подразделу F.24.2.6.

F.24.2. Использование

F.24.2.1. Создание оперативных таблиц

Чтобы добавить оперативную таблицу в вашу базу данных, создайте стороннюю таблицу на сервере in_memory, используя обычный синтаксис CREATE FOREIGN TABLE. По умолчанию в ней будет создан уникальный индекс-B-дерево по первому столбцу, с сортировкой по возрастанию. Если требуется, вы можете воспользоваться указанием INDICES в предложении OPTIONS, чтобы определить другую структуру индекса-B-дерева, следующим образом:

OPTIONS ( INDICES '[ UNIQUE ] {столбец [ COLLATE правило_сортировки ] [ASC | DESC] } [, ... ]' )

Здесь столбец — столбец, включаемый в индекс-B-дерево, а правило_сортировки — имя правила сортировки для этого столбца. Вы можете задействовать в индексе до восьми произвольных столбцов, через запятую, при этом параметры сортировки (COLLATE, ASC/DESC) для этих столбцов могут задаваться независимо. Указание UNIQUE показывает, что создаваемый индекс будет уникальным, подчёркивая поведение по умолчанию.

Все столбцы, используемые в индексе-B-дереве, должны иметь тип, для которого имеется класс операторов B-дерева по умолчанию. Подробнее о классах операторов можно узнать в Разделе 11.9.

Примеры

Создание оперативной таблицы blog_views со статистикой про просмотру блогов, включающей идентификаторы записей блога, с уникальным индексом-B-деревом по первому столбцу в порядке возрастания:

CREATE FOREIGN TABLE blog_views
(
    id int8 NOT NULL,
    author text,
    views bigint NOT NULL
) SERVER in_memory
OPTIONS (INDICES 'UNIQUE (id)');

Определение индекса-B-дерева по столбцам id и author, со значениями author, сортируемыми по возрастанию в соответствии с правилом сортировки "ru_RU":

CREATE FOREIGN TABLE blog_views
(
    id int8 NOT NULL,
    author text,
    views bigint NOT NULL
) SERVER in_memory
OPTIONS (INDICES '(id, author COLLATE "ru_RU" ASC)');

F.24.2.2. Выполнение запросов с оперативными таблицами

Когда оперативная таблица создана, с ней можно выполнять все основные операции DML: SELECT, INSERT, UPDATE, DELETE.

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

Примеры

Заполнение таблицы blog_views начальными нулевыми значения для первых десяти записей блога:

postgres=# INSERT INTO blog_views (SELECT id, 0 FROM generate_series(1, 10) AS id);

Увеличение количества просмотров для пары записей и отображение результата:

postgres=# UPDATE blog_views SET views = views + 1 WHERE id = 1;
UPDATE 1
postgres=# UPDATE blog_views SET views = views + 1 WHERE id = 1;
UPDATE 2
postgres=# UPDATE blog_views SET views = views + 1 WHERE id = 2;
UPDATE 1
postgres=# SELECT * FROM blog_views WHERE id = 1 OR id = 2;
 id | views
----+-------
  1 |  2
  2 |  1
(2 rows)

Проверка стоимости планирования и выполнения запроса, для которого нужно выполнить только поиск по первичному ключу:

postgres=# EXPLAIN ANALYZE SELECT * FROM blog_views WHERE id = 1;
                       QUERY PLAN
-------------------------------------------------------------------
Foreign Scan on blog_views  (cost=0.02..0.03 rows=1 width=16)
(actual time=0.013..0.014 rows=1 loops=1)
  Pk conds: (id = 1)
Planning time: 0.060 ms
Execution time: 0.035 ms
(4 rows)

Проверка стоимости вычисления общего количества просмотров, для которого требуется полное сканирование индекса:

postgres=# EXPLAIN ANALYZE SELECT SUM(views) FROM blog_views;
                         QUERY PLAN
--------------------------------------------------------------------
Aggregate  (cost=1.62..1.63 rows=1 width=32)
(actual time=0.323..0.323 rows=1 loops=1)
Foreign Scan on blog_views  (cost=0.02..1.30 rows=128 width=8)
(actual time=0.005..0.168 rows=1000 loops=1)
Planning time: 0.113 ms
Execution time: 0.353 ms
(4 rows)

F.24.2.3. Удаление данных из оперативных таблиц

Когда строка удаляется из оперативной таблицы, страницы данных не освобождаются. Чтобы освободить страницы, занимаемые оперативными таблицами, вы можете:

  • Удалить таблицу с помощью команды DROP FOREIGN TABLE.

  • Опустошить таблицу с помощью команды TRUNCATE.

Указания RESTART IDENTITY, CONTINUE IDENTITY, CASCADE и RESTRICT команды TRUNCATE с оперативными таблицами не поддерживаются.

F.24.2.4. Запись данных в оперативные таблицы на сервере горячего резерва

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

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

Важно

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

F.24.2.5. Получение статистики по оперативным таблицам

Чтобы получить статистику по страницам оперативных таблиц в вашем кластере, воспользуйтесь функцией in_memory_page_stats, которая возвращает количество всех использованных и свободных страниц, а также общее число страниц, выделенных для оперативных таблиц. Например:

postgres=# SELECT * FROM in_memory.in_memory_page_stats();
 used_pages | free_pages | all_pages
------------+------------+-----------
        576 |       7616 |      8192
(1 row)

F.24.2.6. Тонкая настройка параметров памяти

F.24.2.6.1. Увеличение объёма разделяемой памяти, выделяемого для оперативных таблиц

Оперативные таблицы размещаются в отдельном сегменте общей памяти. Его размер определяется параметром in_memory.shared_pool_size. По умолчанию этот размер ограничен 8 мегабайтами.

Если объём данных, помещаемых в оперативные таблицы, превышает объём выделенного сегмента памяти, происходит следующая ошибка:

ERROR: failed to get a new page: shared pool size is exceeded

Во избежание подобных проблем вы можете увеличить значение in_memory.shared_pool_size или ограничить объём хранимых данных. Для изменения размера разделяемого сегмента требуется перезапустить сервер.

F.24.2.6.2. Управление журналом отмены

Для реализации многоверсионного управления конкурентным доступом (MVCC) модуль in_memory использует журнал отмены — кольцевой буфер в разделяемой памяти, в котором хранятся предыдущие версии записей данных и страниц. Размер журнала отмены определяется параметром in_memory.undo_size и по умолчанию ограничивается 1 мегабайтом. Если до завершения транзакции происходит переполнение буфера, выдаётся следующая ошибка:

ERROR: failed to add undo record: undo size is exceeded

Чтобы избежать этой проблемы, вы можете увеличить значение in_memory.undo_size или разделить транзакции на меньшие.

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

ERROR: snapshot is outdated

В этом случае вы можете:

  • Увеличить значение in_memory.undo_size. Изменение этого параметра требует перезапуска сервера.

  • Сделать так, чтобы журнал отмены не терялся на протяжении времени использования снимка. Для этого можно использовать уровень изоляции транзакций READ COMMITTED или разделить сложный запрос на несколько меньших.

F.24.3. Справка

F.24.3.1. Конфигурационные переменные

in_memory.shared_pool_size (integer)

Определяет размер сегмента в общей памяти, выделенного для оперативных таблиц.

По умолчанию: 8 МБ

in_memory.undo_size (integer)

Определяет размер журнала отмены.

По умолчанию: 1 МБ

F.24.3.2. Функции

in_memory.in_memory_page_stats()

Выводит статистику по страницам оперативных таблиц:

  • used_pages — страницы оперативных таблиц, содержащие какие-либо данные.

  • free_pages — пустые страницы. В это число входят все изначально выделенные страницы, в которые ещё не записывались данные, а также страницы, из которых данные были удалены.

  • all_pages — общее число страниц для оперативных таблиц, выделенное на этом сервере.

F.24.4. Авторы

Postgres Professional, Москва, Россия