Как часто ротируются идентификаторы транзакций в PostgreSQL?

Не так давно, на одном из внутренних докладов о PG разгорелся спор о том, достаточно ли  бит для хранения идентификаторов транзакций. С одной стороны это не так уж мало, а с другой стороны некоторые вспомнили возмущения на форумах, в которых говорилось, что на дворе 21 век и пора уж расширить айдишники до современных размеров и убирать эти "костыли" с ротацией. Посему, ниже коротенечко покажу на сколько хватает текущего размера айдишника, зачем может понадобиться его увеличивать и что такое "прошлое" и "будущее" в контексте транзакции.

tl;dr

  • Идентификаторы цикличны.
  • Ротация идёт не реже чем каждые 2 миллиарда строк (=2 147 483 648).
  • При 100 запросах в секунду ротация должна проходить не реже чем раз в ~250 дней.
  • Подробности вот здесь: на русском и на английском.
  • Случай из практики серьёзных дядей, связанный с высокой нагрузкой здесь.

Какие есть кандидаты на замену?

Логично будет посмотреть на типы данных в PG и выбрать подходящий из существующих. То есть подойти могут bigint или decimal/numeric. В первом случае - это 8 байт (в 2 раза больше байт чем текущий transaction id), во втором - "user-specified precision up to 131072 digits before the decimal point". Иными словами, текущие 4 294 967 296 можно заменить либо на 18 446 744 073 709 551 616, либо на что-то космическое с 131072 знаками до запятой.

Что плохого  в увеличении размера идентификатора транзакции?

  • Как минимум то, что он хранится в каждой строке таблицы в двух экземплярах в полях xmin и xmax. Следовательно, увеличение размера идентификатора транзакции повлечёт увеличение размера служебной информации, хранящейся в строке минимум на  4 байта, в случае замены на вышеупомянутый bigint.
  • Так же, если менять его на decimal/numeric, то получим неприятную особенность: служебная информация в каждой строке БД будет занимать не фиксированное, а переменное количество бит. Это повлечёт необходимость либо в указании размера xmin/xmax где-нибудь в конфиге БД, либо хранить эту же информацию о размере где-либо внутри строки/страницы. Любой из способов создаст значительную головную боль при выборке данных и в большей или меньшей степени понизит быстродействие БД.

Как сравниваются идентификаторы транзакций?

Всё пространство из  значений, условно, разбивается пополам. Вот хорошая иллюстрация, взятая отсюда:


Более подробно принцип работы можно проиллюстрировать следующим примером. Чтобы не путаться в больших цифрах представим, что у нам доступно  уникальных значения для идентификаторов транзакций. Тогда предыдущие транзакции - это прошлое и столько же следующих транзакций - это будущее. Далее представим, что текущая транзакция имеет номер 42. Тогда диапазон от  и до 42 - это прошлое, а от 43 и до - это будущее. Иначе говоря: всё что принадлежит отрезку - это прошлое, а всё что принадлежит - это будущее. Тонкий момент с чётким указанием границ "прошлого" и "будущего" пока оставим в стороне.

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

Как часто происходит ротация номеров транзакций?

Конечно же всё зависит от нагрузки. Но нужно помнить, что для запросов на чтение реальный tid не выделяется, учитываются только транзакции, изменяющие данные. Итого: ниже приведено несколько графиков зависимости времени до ротации от числа реальных транзакций в секунду.

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

А вот что получается для более нагруженных случаев:

Если у вас 10 000 запросов в секунду, то ротация будет не реже раза в 2.5 дня. И тут уж могут быть различные проблемы. Но, с другой стороны, если у вас 10 000 запросов в БД и вы ещё не упали - вы знаете как работать с БД и в курсе о большинстве возможных проблем :)

Вывод

PG, как и любую другую приличную БД, пишут умные дяди и тёти и 4 байта для tid вполне достаточно для современных условий эксплуатации.

PostgreSQL transaction tid rotation

comments powered by Disqus