У меня есть исправительный вопрос относительно последовательностей. Я немного их использовал и ознакомился с документами, и надеюсь, что это легкий вопрос для группы. Сейчас мы на Postgres 11.4 и перейдем к PG 12, когда это будет доступно в RDS.
Цель состоит в том, чтобы набор чисел увеличивался при каждом добавлении или обновлении строки. Мы используем имя поля "con_id" (идентификатор параллелизма) для этого типа счетчика. Таким образом, первый раз, когда строка вставляется в пустую таблицу, значение равно 1, вторая строка получает 2 и т. Д. Звучит как SEQUENCE
. У меня была стандартная последовательность в этой роли, затем я переключился на AS IDENTITY
... но теперь понимаю, что это, вероятно, ошибка.
При обновлении счетчик должен продолжать работать. Таким образом, если обновляется первая строка, con_id изменяется с 1 на 3, current max()+1
. К сведению, во всех наших обновлениях используется ON CONFLICT(id) SET
, а не просто прямая UPDATE
.
Смысл числового ряда состоит в том, чтобы определить границы начала-остановки для различных операций:
operation last_number_processed
sync_to_domo 124556
rollup_day 123516
rollup_week 103456
Затем, когда пришло время выполнить одну из этих операций, все, что вам нужно сделать, чтобы найти правильный блок записей, это выбрать con_id от last_number_processed + 1 до max (con_id). Вы должны обновить трекер операций с этим максимумом (con_id) после завершения операции.
select max(con_id) from source_data; -- Get the current highest number.
В этом случае диапазон будет примерно таким же, как 124557-128923 для "sync_to_domo".
Uniqueness isn 'Т требуется здесь, хотя это желательно. Пробелы вообще не имеют значения. Хранение чисел в последовательности имеет важное значение.
Этот тип операции обновления является той вещью, которая могла бы стать ужасным узким местом, если бы я ее испортил. Кто-нибудь может предложить лучшую надежную стратегию с низким уровнем конкуренции для поддержания счетчика, который получает максимальное значение из таблицы +1 при каждой вставке или обновлении?
И, да, для этой цели можно использовать временную метку,это просто другой вид числовой линии. Причиной целого числа является совпадение с тем, как мы кодируем другие системы. Проще всего объяснить и рассуждать об этом, когда тип данных остается неизменным на разных платформах. Или, похоже, в данном случае.
Тестовый код
Я добавляю некоторый тестовый код и результаты к своему первоначальному вопросу. Это работает, но я сомневаюсь, что это супер эффективно. Тест, приведенный ниже, является минимальной версией и не полезен ... Я просто пытаюсь определить, могу ли я получить постоянно увеличивающуюся число строк вставок и ревизий. Эта попытка выглядит хорошо при быстрой проверке, но я так сильно не усвоил подход Postgres к спорам, что я включаю его главным образом, чтобы люди могли сказать мне, почему это ужасно. Поэтому, пожалуйста, сделайте; -)
Настройка должна иметь последовательность, которая назначается автоматически на INSERT
и с помощью каждого ROW
триггера на UPDATE
. Звучит меньше, чем идея, есть ли лучший способ? Он работает в тесте с одним соединением, но увеличивает счетчики в два раза на UPDATE
. Для меня это не проблема, но я не понимаю, почему это происходит.
Вот автономный тестовый код:
DROP TABLE IF EXISTS data.test_con_ids;
DROP SEQUENCE IF EXISTS test_con_ids_sequence;
DROP FUNCTION IF EXISTS data.test_con_ids_update;
BEGIN;
CREATE SEQUENCE data.test_con_ids_sequence
AS bigint;
CREATE TABLE data.test_con_ids (
id integer NOT NULL DEFAULT NULL PRIMARY KEY,
con_id bigint NOT NULL DEFAULT NEXTVAL ('test_con_ids_sequence'),
dts timestamptz default NOW()
);
CREATE OR REPLACE FUNCTION data.test_con_ids_update()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
NEW.con_id := NEXTVAL ('test_con_ids_sequence');
RETURN NEW;
END
$function$;
-- It's late here, not sure if I could use a FOR EACH STATEMENT trigger here, which should be faster. If it would work.
CREATE TRIGGER test_con_ids_update_trigger BEFORE UPDATE ON data.test_con_ids
FOR EACH ROW EXECUTE PROCEDURE test_con_ids_update();
-- Add ten records, IDs 1-10, con_ids 1-10.
INSERT INTO data.test_con_ids (id)
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
-- Update rows 1-5 to their current values. The trigger should increment the con_id.
INSERT INTO data.test_con_ids (id)
VALUES (1),(2),(3),(4),(5)
ON CONFLICT(id) DO UPDATE set id = EXCLUDED.id; -- Completely pointless, obviously...just a test. We always UPSERT with ON CONFLICT DO UPDATE.
COMMIT;
И вот результаты:
id con_id dts
1 12 2019-11-02 21:52:34.333926+11
2 14 2019-11-02 21:52:34.333926+11
3 16 2019-11-02 21:52:34.333926+11
4 18 2019-11-02 21:52:34.333926+11
5 20 2019-11-02 21:52:34.333926+11
6 6 2019-11-02 21:52:34.333926+11
7 7 2019-11-02 21:52:34.333926+11
8 8 2019-11-02 21:52:34.333926+11
9 9 2019-11-02 21:52:34.333926+11
10 10 2019-11-02 21:52:34.333926+11
Это работает, 1-10 созданы, 1-5 обновлены и получают приращение значений счетчика con_id. На 2 почему-то (?), Но, по крайней мере, они находятся в полезном порядке, и это то, что нам нужно.
Может кто-нибудь предложить предложения о том, как добиться такого поведения более эффективно? Цель - постоянно увеличивающаяся числовая строка для записей, отражающих последние действия INSERT
и UPDATE
. И поскольку мы используем целые числа для этого везде, мы пытаемся придерживаться целых чисел вместо временных меток. Но, честно говоря, во многих отношениях это косметика. Другая причина, по которой я смотрю на SEQUENCE
, заключается в том, что, если я не понял неправильно, это не связано с транзакцией. Это идеально подходит для этого ... нам не нужен ряд без пробелов, только последовательный.
Тест Postgres 12
Следуя предложению Белайера, я создал экспериментальную базу данных PG 12. Я пошел с настройками по умолчанию, так что все в public
. (В реальном мире я убираю public
.) Да, сгенерированный столбец, кажется, работает, пока у вас есть неизменяемая функция. Я прочитал о IMMUTABLE
в Postgres несколько раз .... и я не понимаю. Поэтому я не могу сказать, что эта функция безопасна. Похоже, так и должно быть. Я следовал шаблонам, используемым в этой достойной статье:
https://www.2ndquadrant.com/en/blog/generated-columns-in-postgresql-12/
CREATE OR REPLACE FUNCTION public.generate_concurrency_id() RETURNS bigint
AS $$
SELECT EXTRACT(EPOCH FROM clock_timestamp())::bigint;
$$
LANGUAGE sql IMMUTABLE;
COMMENT ON FUNCTION public.generate_concurrency_id() IS 'Generate a bigint to act as a progressive change counter for a table.';
DROP TABLE IF EXISTS public.test_con_ids;
CREATE TABLE test_con_ids (
id integer NOT NULL DEFAULT NULL PRIMARY KEY,
con_id bigint GENERATED ALWAYS AS (generate_concurrency_id()) STORED,
dts timestamptz default NOW()
);
-- Add ten records, IDs 1-10, con_ids 1-10.
INSERT INTO public.test_con_ids (id)
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
-- Unless you wait here, the con_ids all look the same...this operation is way too short to tick over to another second, unless you get lucky.
-- Update rows 1-5 to their current values. The trigger should increment the con_id.
INSERT INTO public.test_con_ids (id)
VALUES (1),(2),(3),(4),(5)
ON CONFLICT(id) DO UPDATE set id = EXCLUDED.id; -- Completely pointless, obviously...just a test. We always UPSERT with ON CONFLICT DO UPDATE.
Пример выше работает с сгенерированным столбцом, но я не знаю о характеристиках производительности PG12 вычисленных столбцов против триггеров.
На более грустной ноте, я думаю, что это может не сработать для меня вообще. Мне действительно нужна метка времени фиксации, которая доступна только через логическое декодирование. Вот почему, на примере.
- Я суммирую данные и хочу получить необработанные обновленные или вставленные строки.
- Последнее число, которое я получил от записи, добавленной в 01:00:00Теперь я хочу получить все старшие числа.
- Хорошо ... Я делаю это.
- Ой, подождите, незавершенная транзакция приходит и фиксирует временные метки / производные числа от более ранних.
A SEQUENCE
здесь тоже не работает, потому что, опять же, строка / кортеж не видна моему процессу сборщика, пока транзакция не зафиксирована.
Так что я думаю, что это логическое декодирование, сводная таблица обновлений или перебор.