68.1. Введение

BRIN расшифровывается как «Block Range Index» (Индекс зон блоков). BRIN предназначается для обработки очень больших таблиц, в которых определённые столбцы некоторым естественным образом коррелируют с их физическим расположением в таблице.

В BRIN используется понятие зоны блоков (или «зоны страниц»). Зоной блоков называется группа страниц, физически расположенных в таблице рядом; для каждой зоны в индексе сохраняется некоторая сводная информация. Например, в таблице заказов магазина может содержаться поле с датой добавления заказа, и практически всегда записи более ранних заказов и в таблице будут размещены ближе к началу; в таблице, содержащей столбец с почтовым индексом, также естественным образом могут группироваться записи по городам.

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

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

Размер зоны блоков определяется в момент создания индекса параметром хранения pages_per_range. Число записей в индексе будет равняться размеру отношения в страницах, делённому на установленное значение pages_per_range. Таким образом, чем меньше это число, тем больше становится индекс (так как в нём требуется хранить больше элементов), но в то же время сводные данные могут быть более точными и большее число блоков данных может быть пропущено при сканировании индекса.

68.1.1. Обслуживание индекса

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

Начальный расчёт сводки для зоны страниц может производиться несколькими способами. Во-первых, сводка рассчитывается в ходе процедуры VACUUM, запущенной вручную или из процесса autovacuum, для всех существующих зон. Во-вторых, если включён параметр autosummarize для индекса (по умолчанию он отключён), при автоочистке этой базы данных производится расчёт сводки для всех зон без сводной информации, охватывающих недавно заполненные страницы таблицы, независимо от того, выполняется ли автоочистка именно этой таблицы; подробнее см. ниже.

Наконец, можно использовать следующие функции:

brin_summarize_new_values(regclass), рассчитывающую сводку для зон без сводных данных;
brin_summarize_range(regclass, bigint), рассчитывающую сводку для зоны без сводных данных, содержащей указанную страницу.

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

LOG:  request for BRIN range summarization for index "brin_wi_idx" page 128 was not recorded

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

И наоборот, можно удалить сводное значение для зоны, вызвав функцию brin_desummarize_range(regclass, bigint), что может быть полезно, когда этот кортеж в индексе становится не очень хорошим представлением соответствующих данных, так как они изменились. За дополнительной информацией обратитесь к Подразделу 9.27.8.

6.2. Updating Data

The modification of data that is already in the database is referred to as updating. You can update individual rows, all the rows in a table, or a subset of all rows. Each column can be updated separately; the other columns are not affected.

To update existing rows, use the UPDATE command. This requires three pieces of information:

  1. The name of the table and column to update

  2. The new value of the column

  3. Which row(s) to update

Recall from Chapter 5 that SQL does not, in general, provide a unique identifier for rows. Therefore it is not always possible to directly specify which row to update. Instead, you specify which conditions a row must meet in order to be updated. Only if you have a primary key in the table (independent of whether you declared it or not) can you reliably address individual rows by choosing a condition that matches the primary key. Graphical database access tools rely on this fact to allow you to update rows individually.

For example, this command updates all products that have a price of 5 to have a price of 10:

UPDATE products SET price = 10 WHERE price = 5;

This might cause zero, one, or many rows to be updated. It is not an error to attempt an update that does not match any rows.

Let's look at that command in detail. First is the key word UPDATE followed by the table name. As usual, the table name can be schema-qualified, otherwise it is looked up in the path. Next is the key word SET followed by the column name, an equal sign, and the new column value. The new column value can be any scalar expression, not just a constant. For example, if you want to raise the price of all products by 10% you could use:

UPDATE products SET price = price * 1.10;

As you see, the expression for the new value can refer to the existing value(s) in the row. We also left out the WHERE clause. If it is omitted, it means that all rows in the table are updated. If it is present, only those rows that match the WHERE condition are updated. Note that the equals sign in the SET clause is an assignment while the one in the WHERE clause is a comparison, but this does not create any ambiguity. Of course, the WHERE condition does not have to be an equality test. Many other operators are available (see Chapter 9). But the expression needs to evaluate to a Boolean result.

You can update more than one column in an UPDATE command by listing more than one assignment in the SET clause. For example:

UPDATE mytable SET a = 5, b = 3, c = 1 WHERE a > 0;