F.29. in_memory — размещение данных в общей памяти с использованием таблиц, реализованных через обёртку сторонних данных #
Расширение in_memory даёт возможность размещать данные в общей памяти Postgres Pro, используя таблицы в оперативной памяти, реализованные через обёртку сторонних данных.
Примечание
Это расширение нельзя использовать с подготовленными транзакциями и при включённом пуле соединений.
Таблицы в оперативной памяти, или оперативные таблицы, организованы по индексу — строки таблиц хранятся в страницах листьев индекса-B-дерева, построенного по первичному ключу таблицы. Это решение даёт следующие плюсы:
Быстрый произвольный доступ по первичному ключу. Это может дать значительный выигрыш по скорости при работе с данными, когда требуется очень быстрый доступ на чтение и запись, особенно в многоядерных системах.
Эффективное использование памяти. Первичный ключ не дублируется, так как все данные хранятся непосредственно в индексе.
Оперативные таблицы поддерживают транзакции, включая точки сохранения. Однако данные в таких таблицах сохраняются только пока сервер работает. Когда сервер отключается, все данные в оперативной памяти пропадают. Используя оперативные таблицы, вы должны также принимать во внимание следующие ограничения:
WAL, сохранение и репликация данных в оперативных таблицах в настоящее время не поддерживается.
Вторичные индексы не поддерживаются.
Поддерживаются уровни изоляции транзакции до
REPEATABLE READ. УровеньSERIALIZABLEне поддерживается, вместо него используетсяREPEATABLE READ.Оперативные таблицы не поддерживают TOAST и другие механизмы хранения больших кортежей. Так как размер страницы в памяти составляет один 1 КБ, а индекс-B-дерево должен содержать на странице минимум три кортежа, максимальная длина строки ограничена 304 байтами.
Когда строка удаляется из оперативной таблицы, соответствующая страница данных не освобождается. За подробностями обратитесь к Подразделу F.29.2.3.
F.29.1. Установка и настройка #
Чтобы включить оперативные таблицы в вашем кластере:
Убедитесь, что включён модуль postgres_fdw.
Добавьте
in_memoryв переменнуюshared_preload_librariesв файлеpostgresql.conf:shared_preload_libraries = 'in_memory'
Создайте расширение
in_memory, выполнив следующую команду:CREATE EXTENSION in_memory;
В результате будет создан сторонний сервер in_memory, для оперативных таблиц будет выделен отдельный сегмент в разделяемой памяти, и в нём будут подготовлены страницы для данных. Чтобы доступ к данным памяти был эффективным, они должны быть более компактными, по сравнению с данными на жёстком диске или SSD, так что размер страницы в памяти составляет всего 1 КБ. Сразу после создания расширения вы можете начать использовать оперативные таблицы, как рассказывается в Подразделе F.29.2.
Подсказка
Если требуется, вы можете увеличить объём памяти, выделяемый для оперативных таблиц. За подробностями обращайтесь к Подразделу F.29.2.6.
F.29.2. Использование #
F.29.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.10.
Примеры
Создание оперативной таблицы 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 NOT NULL,
views bigint NOT NULL
) SERVER in_memory
OPTIONS (INDICES 'UNIQUE (id, author COLLATE "ru_RU" ASC)');F.29.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.29.2.3. Удаление данных из оперативных таблиц #
Когда строка удаляется из оперативной таблицы, страницы данных не освобождаются. Чтобы освободить страницы, занимаемые оперативными таблицами, вы можете:
Удалить таблицу с помощью команды
DROP FOREIGN TABLE.Опустошить таблицу с помощью команды
TRUNCATE.
Указания RESTART IDENTITY, CONTINUE IDENTITY, CASCADE и RESTRICT команды TRUNCATE с оперативными таблицами не поддерживаются.
F.29.2.4. Запись данных в оперативные таблицы на сервере горячего резерва #
В некоторых случаях может быть полезно выполнять операции записи на серверах горячего резерва. Например, предположим, что вам нужно собирать статистику в запросах на ведомом сервере. Так как оперативные таблицы допускают запись, вы можете использовать их для таких целей
Чтобы настроить оперативные таблицы для пишущих запросов на ведомом сервере, создайте требуемые оперативные таблицы на ведущем сервере, как рассказывается в Подразделе F.29.2.1. После завершения репликации вы можете начать записывать данные в эти таблицы на сервере горячего резерва.
Важно
Если сервер горячего резерва перезапускается, все данные, хранящиеся в его оперативных таблицах, сбрасываются. Вы можете продолжить записывать данные в те же оперативные таблицы, но все ранее сохранённые данные будут потеряны.
F.29.2.5. Получение статистики по оперативным таблицам #
Чтобы получить статистику по страницам оперативных таблиц в вашем кластере, воспользуйтесь функцией in_memory_page_stats, которая возвращает количество всех использованных и свободных страниц, а также общее число страниц, выделенных для оперативных таблиц. Например:
postgres=# SELECT * FROM in_memory.in_memory_page_stats();
busy_pages | free_pages | all_pages
------------+------------+-----------
576 | 7616 | 8192
(1 row)
F.29.2.6. Тонкая настройка параметров памяти #
F.29.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.29.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.29.3. Справка #
F.29.3.1. Конфигурационные переменные #
F.29.3.2. Функции #
-
in_memory.in_memory_page_stats()# Выводит статистику по страницам оперативных таблиц:
busy_pages— страницы оперативных таблиц, содержащие какие-либо данные.free_pages— пустые страницы. В это число входят все изначально выделенные страницы, в которые ещё не записывались данные, а также страницы, из которых данные были удалены.all_pages— общее число страниц для оперативных таблиц, выделенное на этом сервере.
F.29.4. Авторы #
Postgres Professional, Москва, Россия
F.29. in_memory — store data in shared memory using tables implemented via FDW #
The in_memory extension enables you to store data in Postgres Pro shared memory using in-memory tables implemented via foreign data wrappers (FDW).
Note
This extension cannot be used together with prepared transactions or while built-in connection pooling is enabled.
In-memory tables are index-organized — table rows are stored in leaf pages of the B-tree index defined on the primary key for the table. This solution offers the following benefits:
Fast random access on the primary key. This can result in significant performance benefits when working with data that requires a very high read and write access rate, especially on multi-core systems.
Effective space usage. There is no primary key duplication as all data is stored directly in the index.
In-memory tables support transactions, including savepoints. However, the data in such tables is stored only while the server is running. Once the server is shut down, all in-memory data gets truncated. When using in-memory tables, you should also take into account the following restrictions:
Persistence, WAL, and data replication are currently not supported for in-memory tables.
Secondary indexes are not supported.
Isolation levels are supported up to
REPEATABLE READ.SERIALIZABLEisolation level is not supported.REPEATABLE READlevel is used instead.In-memory tables do not support TOAST or any other mechanism for storing big tuples. Since the in-memory page size is 1 kB, and the B-tree index requires at least three tuples in a page, the maximum row length is limited to 304 bytes.
When a row is deleted from an in-memory table, the corresponding data page is not freed. See Section F.29.2.3 for details.
F.29.1. Installation and Setup #
To enable in-memory tables for your cluster:
Make sure that the postgres_fdw module is enabled.
Add the
in_memoryvalue to theshared_preload_librariesvariable in thepostgresql.conffile:shared_preload_libraries = 'in_memory'
Create the
in_memoryextension using the following statement:CREATE EXTENSION in_memory;
As a result, the in_memory foreign server is created, and a separate shared memory pool is allocated for in-memory tables, with pre-created pages for storing in-memory data. For in-memory tables, smaller locality is required for effective memory access, as compared to hard drives or SSD, so in-memory page size is 1 kB only. Once the extension is created, you can start using in-memory tables as explained in Section F.29.2.
Tip
If required, you can increase the memory size allocated for in-memory tables. For details, see Section F.29.2.6.
F.29.2. Usage #
F.29.2.1. Creating In-Memory Tables #
To add an in-memory table to your database, create a foreign table on the in_memory server, using the regular CREATE FOREIGN TABLE syntax. By default, a unique B-tree index is built upon the first column, in the ascending order. If required, you can use the INDICES option in the OPTIONS clause to define a different B-tree index structure, as follows:
OPTIONS ( INDICES 'UNIQUE {column [ COLLATE collation ] [ASC | DESC] } [, ... ]' )
where column is the column to include into the B-tree index, and collation is the name of the collation to use for this column. You can specify up to eight arbitrary columns separated by commas, with sorting options defined for each of these columns (COLLATE, ASC/DESC). The mandatory UNIQUE option declares the created index unique, reiterating the default behavior.
All columns to be used in the B-tree index must be of a type for which the default B-tree operator class is available. For details on operator classes, see Section 11.10.
Examples
Create an in-memory table blog_views to store statistics on blog views based on blog post IDs, with the unique B-tree index built upon the first column, in the ascending order:
CREATE FOREIGN TABLE blog_views
(
id int8 NOT NULL,
author text,
views bigint NOT NULL
) SERVER in_memory
OPTIONS (INDICES 'UNIQUE (id)');
Define the B-tree index on the id and author columns, with the author values sorted in the ascending order using "ru_RU" collation:
CREATE FOREIGN TABLE blog_views
(
id int8 NOT NULL,
author text NOT NULL,
views bigint NOT NULL
) SERVER in_memory
OPTIONS (INDICES 'UNIQUE (id, author COLLATE "ru_RU" ASC)');
F.29.2.2. Running Queries on In-Memory Tables #
Once an in-memory table is created, you can run all the main DML operations on this table: SELECT, INSERT, UPDATE, DELETE.
If you use the primary key as the scan qualifier when running queries, a key lookup or range scan is performed. Otherwise, a full index scan is required.
Examples
Fill the blog_views table with initial zero values for initial ten blog posts:
postgres=# INSERT INTO blog_views (SELECT id, 0 FROM generate_series(1, 10) AS id);
Increment the view count for a couple of posts and display the result:
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)
Check planning and execution costs for a query that only requires a primary key lookup:
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)
Check the costs of calculating the sum of all views, which requires a full index scan:
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.29.2.3. Deleting In-Memory Data #
When a row is deleted from an in-memory table, data pages are not freed. To free the pages occupied by in-memory tables, you can:
Delete the table using the
DROP FOREIGN TABLEcommand.Truncate the table using the
TRUNCATEcommand.
RESTART IDENTITY, CONTINUE IDENTITY, CASCADE, and RESTRICT options of the TRUNCATE command are not supported by in-memory tables.
F.29.2.4. Writing Data to In-Memory Tables on Hot Standby #
In some cases, it can be useful to perform write operations on hot standby servers. For example, suppose you need to collect statistics on queries run on hot standby. Since in-memory tables are writable, you can use them for such purposes.
To set up in-memory tables for write queries on standby, create the required in-memory tables on the primary server as explained in Section F.29.2.1. Once the replication is complete, you can start writing data to these tables on hot standby.
Important
If the hot standby server is restarted, all the data stored in its in-memory tables gets truncated. You can continue writing data to the same in-memory tables, but all the previously stored data will be lost.
F.29.2.5. Getting Statistics on In-Memory Tables #
To get statistics on in-memory pages available in your cluster, run the in_memory_page_stats function, which returns the number of all used and free in-memory pages, as well as the total number of pages allocated for in-memory tables. For example:
postgres=# SELECT * FROM in_memory.in_memory_page_stats();
busy_pages | free_pages | all_pages
------------+------------+-----------
576 | 7616 | 8192
(1 row)
F.29.2.6. Fine-Tuning Memory Settings #
F.29.2.6.1. Increasing Shared Memory Pool for In-Memory Tables #
In-memory tables are stored in a separate shared memory segment. Its size is defined by the in_memory.shared_pool_size parameter. By default, this memory segment is limited to 8 MB.
If the data to be stored in in-memory tables exceeds the size of the allocated memory segment, the following error occurs:
ERROR: failed to get a new page: shared pool size is exceeded
To avoid such issues, you can increase the in_memory.shared_pool_size value, or limit the size of the stored data. Changing the shared pool size requires a server restart.
F.29.2.6.2. Managing the Undo Log #
To enable multi-version concurrency control (MVCC), the in_memory module uses the undo log — a shared-memory ring buffer that stores the previous versions of data entries and pages. The size of the undo log is defined by the in_memory.undo_size parameter and is limited to 1 MB by default. If a buffer overflow occurs before a transaction is complete, the following error is returned:
ERROR: failed to add undo record: undo size is exceeded
To avoid this issue, you can increase the in_memory.undo_size value, or split the transactions into smaller ones.
If the required version of the entry or page has already been overwritten in the undo log when it is accessed for read, the following error occurs:
ERROR: snapshot is outdated
In this case, you can:
Increase the
in_memory.undo_sizevalue. Changing this parameter requires a server restart.Ensure that the undo log is not truncated while the snapshot is in use. To achieve this, you can use the
READ COMMITTEDisolation level, or split a complex query into several smaller ones.
F.29.3. Reference #
F.29.3.1. Configuration Variables #
F.29.3.2. Functions #
-
in_memory.in_memory_page_stats()# Displays statistics on pages of in-memory tables:
busy_pages— in-memory pages containing any data.free_pages— empty in-memory pages. This number includes all the initially allocated pages to which no data has been written yet, as well as the pages from which all data has been deleted.all_pages— the total number of in-memory pages allocated on this server.
F.29.4. Authors #
Postgres Professional, Moscow, Russia