56.5. Блокировка строк в обертках внешних данных#

56.5. Блокировка строк в обертках внешних данных

56.5. Блокировка строк в обертках внешних данных #

Если у механизма хранения FDW есть концепция блокировки отдельных строк для предотвращения одновременных обновлений этих строк, то обычно имеет смысл, чтобы FDW выполнял блокировку на уровне строк с наиболее близким приближением к семантике, используемой в обычных таблицах Tantor BE. При этом следует учитывать несколько моментов.

Одним из ключевых решений, которое необходимо принять, является выбор между ранней блокировкой и поздней блокировкой. При ранней блокировке строка блокируется сразу после ее извлечения из базы данных, в то время как при поздней блокировке строка блокируется только тогда, когда известно, что она должна быть заблокирована. (Разница возникает из-за того, что некоторые строки могут быть отброшены из-за локальных ограничений или условий соединения). Ранняя блокировка намного проще и позволяет избежать дополнительных обращений к удаленному хранилищу, но она может привести к блокировке строк, которые не нужно было блокировать, что приводит к снижению параллелизма или даже неожиданным блокировкам. Кроме того, поздняя блокировка возможна только в том случае, если строка, которую нужно заблокировать, может быть уникально идентифицирована позже. Желательно, чтобы идентификатор строки идентифицировал конкретную версию строки, как это делают TID в Tantor BE.

По умолчанию, Tantor BE игнорирует соображения блокировки при взаимодействии с FDW, но FDW может выполнять раннюю блокировку без какой-либо явной поддержки от основного кода. Функции API, описанные в Раздел 56.2.6, которые были добавлены в PostgreSQL 9.5, позволяют FDW использовать позднюю блокировку, если она желает.

Дополнительное соображение заключается в том, что в режиме изоляции READ COMMITTED Tantor BE может потребоваться повторная проверка ограничений и условий соединения по обновленной версии некоторой целевой кортежи. Повторная проверка условий соединения требует повторного получения копий строк, не являющихся целевыми, которые ранее были объединены с целевым кортежем. При работе со стандартными таблицами Tantor BE это делается путем включения TID нецелевых таблиц в список столбцов, проецируемых через соединение, а затем повторного получения строк нецелевых таблиц при необходимости. Такой подход позволяет сохранить компактность набора данных соединения, но требует недорогой возможности повторного получения, а также TID, который может однозначно идентифицировать версию строки для повторного получения. По умолчанию, поэтому, подход, используемый с внешними таблицами, заключается в том, чтобы включить копию всей строки, полученной из внешней таблицы, в список столбцов, проецируемых через соединение. Это не предъявляет особых требований к FDW, но может привести к снижению производительности соединения слиянием и по хешу. FDW, способный удовлетворить требованиям повторного получения, может выбрать первый способ.

Для операций UPDATE или DELETE на внешней таблице рекомендуется, чтобы операция ForeignScan на целевой таблице выполняла раннюю блокировку строк, которые она извлекает, возможно, с помощью эквивалента SELECT FOR UPDATE. Внешний поставщик данных (FDW) может определить, является ли таблица целью для UPDATE/DELETE на этапе планирования, сравнивая ее идентификатор с root->parse->resultRelation, или на этапе выполнения, используя функцию ExecRelationIsTargetRelation(). Альтернативной возможностью является выполнение поздней блокировки в обратных вызовах ExecForeignUpdate или ExecForeignDelete, но для этого не предусмотрена специальная поддержка.

Для внешних таблиц, которые указаны для блокировки командой SELECT FOR UPDATE/SHARE, операция ForeignScan может снова выполнять раннюю блокировку путем извлечения кортежей с эквивалентом SELECT FOR UPDATE/SHARE. Чтобы вместо этого выполнять позднюю блокировку, предоставьте функции обратного вызова, определенные в Раздел 56.2.6. В функции GetForeignRowMarkType выберите опцию rowmark ROW_MARK_EXCLUSIVE, ROW_MARK_NOKEYEXCLUSIVE, ROW_MARK_SHARE или ROW_MARK_KEYSHARE в зависимости от требуемой силы блокировки. (Основной код будет работать одинаково независимо от выбранной из этих четырех опций). В других местах вы можете определить, была ли внешняя таблица указана для блокировки этого типа команды, используя функцию get_plan_rowmark на этапе планирования или функцию ExecFindRowMark во время выполнения; вы должны проверить не только то, что возвращается ненулевая структура rowmark, но и то, что ее поле strength не равно LCS_NONE.

В конце концов, для внешних таблиц, которые используются в командах UPDATE, DELETE или SELECT FOR UPDATE/SHARE, но не указаны для блокировки по строкам, можно переопределить по умолчанию выбор копирования целых строк, путем выбора опции ROW_MARK_REFERENCE функцией GetForeignRowMarkType, когда она видит уровень блокировки LCS_NONE. Это приведет к вызову функции RefetchForeignRow с этим значением для поля markType; затем она должна повторно извлечь строку без получения новой блокировки. (Если у вас есть функция GetForeignRowMarkType, но вы не хотите повторно извлекать незаблокированные строки, выберите опцию ROW_MARK_COPY для LCS_NONE).

См. src/include/nodes/lockoptions.h для комментариев к RowMarkType и PlanRowMark в src/include/nodes/plannodes.h, а также комментарии к ExecRowMark в src/include/nodes/execnodes.h для получения дополнительной информации.