Вопросы о Postgres track_commit_timestamp (pg_xact_commit_timestamp) - PullRequest
0 голосов
/ 10 июля 2019

Я работаю над дизайном безопасной параллельной системы с наращиванием агрегирования, и track_commit_timestamp (pg_xact_commit_timestamp) звучит идеально. Но я нашел очень мало комментариев к нему в целом, и не мог понять, как это работает подробно из исходного кода.

Надеюсь, кто-то знает ответы на один или несколько моих вопросов:

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

  • Сколько байтов добавлено к каждой строке в окончательной реализации? Дискуссии, которые я видел, казались в диапазоне 12-24 байта. Было обсуждение добавления дополнительных байтов для «на всякий случай». Это до 9,5, так что мир назад.

  • Временные метки индексируются внутренне? С B-деревом? Я прошу по причинам планирования мощности.

  • Я видел в StackOverflow и обсуждениях дизайна, что временные метки не хранятся бесконечно, но не могу найти точную информацию о том, как долго они хранятся.

  • Есть ли практические правила, влияющие на производительность включения track_commit_timestamp? Мне не нужны данные по всем таблицам, но там, где я это делаю, звучит так, будто они могут отлично работать.

  • Есть ошибки? Я попытался запустить VACUUM FULL на тестовой таблице, и ни один из параметров pg_xact_commit_timestamp не изменился. Кажется, что физическая операция, такая как VACUUM, не должна ничего менять, но может быть что-то, о чем я даже не думал. И, честно говоря, мой быстрый тест ВАКУУМ может даже ничего не значить.

Большое спасибо за любую помощь!


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

select max(pg_xact_commit_timestamp(xmin)) from scan;--   2019-07-07 20:46:14.694288+10

update scan set quantity = 5 where quantity = 1; --       Change some data.

select max(pg_xact_commit_timestamp(xmin)) from scan; --  2019-07-10 09:38:17.920294+10

-- Find the changed row(s):
select * 
  from scan 
 where pg_xact_commit_timestamp(xmin) > '2019-07-07 20:46:14.694288+10'; 

Идея состоит в том, чтобы делать постепенный и регулярный свертывание строк. Итак,

- Отслеживать последнюю свернутую метку времени. - Подождите 5 минут (или что-то еще.) - Найти текущую максимальную отметку времени коммита. - Поиск строк, где отметка времени фиксации находится между последней обработанной отметкой времени и макс. - Сверните их.

Одни только идентификаторы транзакций не могут работать, потому что они могут очень легко совершить нарушение порядка. И эта система отметок времени не должна быть совершенной на 100%, но я стремлюсь к чему-то очень близкому к идеальному. Таким образом, немного покачивания часов и даже небольшая путаница из-за перекрывающегося времени начала / конца, вероятно, допустимы.

Есть ли явный недостаток в этом плане?

Ответы [ 3 ]

1 голос
/ 10 июля 2019

Много вопросов.

Для справки, исходный код находится в src/backend/access/transam/commit_ts.c.

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

  2. Отметка времени вообще не сохраняется в строке, но в подкаталоге pg_commit_tsкаталога данных.Каждая запись занимает 10 байтов:

    /*
     * We need 8+2 bytes per xact.  Note that enlarging this struct might mean
     * the largest possible file name is more than 5 chars long; see
     * SlruScanDirectory.
     */
    typedef struct CommitTimestampEntry
    {
        TimestampTz time;
        RepOriginId nodeid;
    } CommitTimestampEntry;
    

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

  3. Индекс не требуется, посколькуместоположение метки времени определяется номером транзакции (каждая транзакция имеет фиксированное местоположение для метки времени фиксации).См. TransactionIdToCTsPage.

  4. Отметки времени сохраняются до номера транзакции, если я правильно понимаю код.

  5. Не могу сказатьчто такое издержки, но, вероятно, они невелики.

  6. Почему VACUUM или VACUUM (FULL) должны изменить метку времени фиксации?Это было бы ошибкой.

Теперь, когда я понимаю, чего вы хотите достичь с помощью отметок времени коммита, слово к этому (я хотел бы, чтобы люди задавали вопрос real прямо сейчас):

Отметки времени коммита не - правильный инструмент для вас.Вы не могли индексировать выражение, потому что pg_xact_commit_timestamp не является неизменным.

Выберите простое и очевидное решение и добавьте дополнительный столбец timestamp with time zone с триггером BEFORE, который устанавливает его на current_timestamp on INSERT и UPDATE.Это можно проиндексировать.

Известный человек сказал, что преждевременная оптимизация - корень всего зла.

0 голосов
/ 14 июля 2019

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

  • Метки времени фиксации назначаются, когда работа транзакции завершена , но это не то же самое, что когда * совершено 1007 *.Модуль записи WAL не обновляет штампы, чтобы сохранять их в хронологическом порядке.
  • Следовательно, отметки времени коммита определенно не являются надежным механизмом поиска строк изменений по порядку.
  • Несколько часов.Саморегулирующиеся часы.О, человечество!
  • Если вам нужна последовательность в порядке изменения порядка, логическое декодирование или репликация являются опциями.(Я попробовал логическую репликацию пару недель назад экспериментально. Самое крутое. Вещи. Когда-либо.)
  • Стоимость отслеживания меток времени составляет 12 байтов на транзакцию , а не на строку.Так что не все так плохо.(Временные метки составляют 8 байтов, идентификаторы транзакций - 4 байта.)
  • Все это является частью существующей системы транзакций, поэтому в этом случае также применимы реалии развертывания идентификатора транзакции.(Не страшно в моем случае.) См .:

    https://www.postgresql.org/docs/current/routine-vacuuming.html

  • Для записи, вы можете включить эту опцию в RDS через настройку группы параметров.Просто установите track_commit_timestamp в 1 и перезапустите.(Настройка включена в postgres.conf.)
0 голосов
/ 10 июля 2019

Лоренц, во-первых, ты чемпион за то, что копался и помогал мне. Спасибо. Для справки, я задал этот вопрос более подробно в нескольких списках рассылки PG, и получил ноль ответов.Я думаю, что это потому, что мой полный вопрос был слишком длинным.

Я попытался быть здесь короче и, к сожалению, не объяснил важную часть ясно.Физическая оптимизация - это , а не .На самом деле, система commit_timestamp будет стоить мне места, так как это глобальная настройка для всех таблиц.Мои реальные таблицы будут иметь полные timestamptz (установлены в UTC) поля, по которым я буду индексировать и агрегировать.То, что я сейчас пытаюсь разобраться (фаза разработки) - это точность подхода.А именно, собираю ли я все события один раз и только один раз?

Мне нужен надежный последовательный номер или временная линия , чтобы отметить самую высокую / последнюю строку, которую я обработал, и текущую самую высокую/ последний ряд.Это позволяет мне захватывать любые строки, которые не были обработаны, без повторного выбора уже обработанных строк или блокировки таблицы при добавлении новых строк.Эта идея называется «идентификатором параллелизма» в некоторых контекстах.Вот эскиз, адаптированный из другой части нашего проекта, в котором было целесообразно использовать числа вместо временных меток (но временные шкалы - это тип числовой линии):

Д'оо!Я не могу публиковать изображения.Это здесь:

https://imgur.com/iD9bn5Q

Показывает номерную строку для отслеживания записей, которые делятся на три части [Готово] [Захватить их] [Хвосты]

«Готово»это все из самого высокого / последнего обработанного счетчика.

«Захватить это» - это все, что позже «Готово» и меньше текущего максимального счетчика в таблице.

«Хвост» - любой новыйболее высокие счетчики добавляются другими входными данными во время обработки строк «захвата этих».

На снимке это легче увидеть.

Итак, у меня есть небольшая служебная таблица, такая какэто:

CREATE TABLE "rollup_status" (
    "id" uuid NOT NULL DEFAULT extensions.gen_random_uuid(), -- We use UUIDs, not necessary here, but it's what we use. 
    "rollup_name" text NOT NULL DEFAULT false,               
    "last_processed_dts" timestamptz NOT NULL DEFAULT NULL); -- Marks the last timestamp processed.

А теперь представьте одну запись:

rollup_name         last_processed_dts
error_name_counts   2018-09-26 02:23:00

Итак, моя числовая строка (временная шкала, в случае фиксации времени) обрабатывается с любой даты 0до 2018-09-26 02:23:00В следующий раз я получу текущий максимум из интересующей меня таблицы 'scan':

select max(pg_xact_commit_timestamp(xmin)) from scan; -- Pretend that it's 2019-07-07 25:00:00.0000000+10

Это значение становится верхней границей моего поиска, и новое значение rollup_status.last_processed_dts.

-- Find the changed row(s):
select * 
  from scan 
 where pg_xact_commit_timestamp(xmin) >  '2019-07-07 20:46:14.694288+10' and
       pg_xact_commit_timestamp(xmin) <= '2019-07-07 25:00:00.0000000+10

Это сегмент "захватить эти" моей числовой линии.Это также единственное использование, которое я запланировал для данных о времени фиксации.Мы загружаем данные из различных источников и хотим, чтобы их метки времени (с учетом UTC), а не метки времени сервера.(Серверные временные метки могут иметь смысл, в случае наших данных они просто не случаются.) Таким образом, единственная цель временной метки фиксации - создать надежную числовую линию.

Если вы посмотрите на график, он показывает три разные числовые линии для одной и той же базовой таблицы.Сама таблица имеет только один номер или временную шкалу, есть три различных использования этого номера / временного ряда.Итак, три строки rollup_status, идущие с моей таблицей эскизов из ранее.Таблица «scan» должна ничего не знать о том, как она используется.Это огромное преимущество этой стратегии.Вы можете добавлять, удалять и повторять операции без необходимости изменять основную таблицу или ее строки.

Я также рассматриваю триггер выбора ON AFTER INSERT / UPDATE с таблицей переходов для заполнения timestamptz (установлен на UTC), как row_commmitted_dts.Это может быть мой план B, но он требует добавления триггеров, и кажется, что он может быть только немного менее точным, чем фактическое время транзакции.Вероятно, небольшая разница, но с параллелизмом, небольшие проблемы могут быстро перерасти в большие ошибки.

Итак, вопрос в том, могу ли я рассчитывать на систему фиксации времени для получения точных результатов, которые не появятся «в прошлом». Вот почему я не могу использовать идентификаторы транзакций. Они назначаются в начале транзакции, но могут быть зафиксированы в любом порядке. (Насколько я понимаю.) Поэтому мои границы диапазона «последний обработанный» и «текущий максимум в файле» не могут работать. Я мог получить этот диапазон, и ожидающая транзакция могла зафиксировать тысячи записей с отметкой времени ранее , чем мое ранее записанное «максимальное значение». Вот почему я за марками коммитов.

Опять же, спасибо за любую помощь или предложения. Я очень благодарен.

P.S Единственная дискуссия, с которой я столкнулся в мире Postgres с чем-то вроде этого, здесь:

Масштабируемое добавочное агрегирование данных на Postgres и Citus. https://www.citusdata.com/blog/2018/06/14/scalable-incremental-data-aggregation/

Они используют счетчики больших серий таким образом, но, насколько я понимаю, это работает только для INSERT, а не UPDATE. И, честно говоря, я недостаточно знаю о транзакциях и сериалах Postgres, чтобы продумать поведение параллелизма.

...