Как использовать строку в качестве ключа для консультативной блокировки PostgreSQL?

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

Функция, которая позволяет нам получить такую ​​блокировку, принимает большой целочисленный аргумент: pg_advisory_lock(key bigint) или два целых ключа: pg_advisory_lock(key1 int, key2 int) (вторая форма).

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

Возможно ли реализовать это только в PostgreSQL без необходимости приведения строки к целому числу на уровне приложения?

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

Ответы

Ответ 1

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

Вы можете использовать таблицу oid (идентификатор объекта) из pg_class, чтобы указать таблицу. Удобство, наложенное на псевдо-тип regclass, выглядит для вас, или вы можете select c.oid from pg_class c inner join pg_namespace n where n.nspname = 'public' and c.relname = 'mytable' получить его по-схеме.

Есть небольшая проблема, потому что oid внутренне является неподписанным 32-битным целым числом, но форма с двумя аргументами pg_advisory_lock принимает целое число со знаком. На практике это вряд ли будет проблемой, так как вам нужно пройти через множество OID, прежде чем проблема.

например.

SELECT pg_advisory_lock('mytable'::regclass::integer, 42);

Однако, если вы собираетесь это сделать, вы в основном эмулируете блокировку строк с помощью консультативных блокировок. Так почему бы просто не использовать блокировку строк?

SELECT 1
FROM mytable
WHERE id = 42
FOR UPDATE OF mytable;

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

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

Они на самом деле хеш для int4, и вы бы предпочли int8, так что вы столкнулись с еще большим риском столкновений. Альтернативой является использование медленного криптографического хэша, такого как md5, и его усечение, и это просто уродливо.

Итак, если вы действительно чувствуете себя действительно, вы можете сделать что-то вроде:

select pg_advisory_lock( hashtext('fredfred') );

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