Короткая версия
- Postgres 11.4 развернут на RDS.
- Существует ли встроенный или простой способ разделения строк в таблице для пакетного обновления?
- Как только у вас есть схема сегментов, как вы запускаете цикл в SQL для обработки каждого сегмента с небольшой паузой, чтобы сервер мог перевести дыхание?
- Есть ли необходимость в групповой работе или я беспокоюсь без веской причины?
Подробная версия:
Мы собираем данные для некоторыхвремя, и используя поля timestamptz. Я сделал ошибку, я должен был использовать метку времени. Мы собираем много данных из разных мест, а затем сами вычисляем UTC , прежде чем отправлять данные в Postgres. Насколько я понимаю, данные timestamp и timestamptz - это одни и те же 8 байтов в любом случае, что дает timestamptz магическое (и невидимое) преобразование AT TIME ZONE
. Это означает, что данные не отличаются, это то, как Postgres обрабатывает эти данные, что отличается. В нашем случае это означает, что мы облажаемся, помещая данные в Postgres как UTC, а затем снова выводим их на локальный уровень. Данные нашего сервера не имеют единого часового пояса, поэтому мы устанавливаем его в UTC, как это делает Postgres. Чтобы упростить отчетность, аналитические таблицы обычно имеют избыточный столбец для local_dts и utc_dts. Таким образом, мы можем запускать отчеты, в которых сравниваются «утра понедельника с 8 до 11» для разных учреждений в разных часовых поясах. Различные объекты имеют разные часовые пояса, поэтому мы используем «локальное» значение, которое их локальное для таких запросов. Но если нам нужна единая временная шкала, тогда мы используем UTC. Проще говоря: строки в одной и той же таблице могут быть из источников с разными часовыми поясами.
Хорошо, это фон, у меня теперь есть 10 с миллионов строк, которые я собираюсь обновить. Изменения структуры выглядят просто:
-- Change the data type, this is instantaneous.
ALTER TABLE assembly
ALTER COLUMN created_dts
SET DATA TYPE timestamp;
-- Reset the default, it's probably not necessary, but the ::timestamptz is misleading/confusing here otherwise.
ALTER TABLE assembly
ALTER COLUMN created_dts
SET DEFAULT '-infinity'::timestamp
Мне придется отбросить и воссоздать несколько представлений, но это всего лишь вопрос запуска некоторых сценариев резервного копирования.
Мой вопрос заключается в том, как это сделатьобновление эффективно без перетаскивания сервера? Я представляю, как можно группировать вещи по 5 тысяч строк за раз или тому подобное. Для простоты предположим, что все наши серверы настроены на работу в США и Центральной. Когда мы изначально выдавали данные как UTC, они снова были преобразованы Postgres, поэтому теперь данные отключены из-за смещения времени нашего сервера и UTC. (Я думаю.) Если это так, простейшее обновление может выглядеть так:
SET TIME ZONE 'UTC'; -- Tell Postgres we're in UTC to line up the data with the UTC clock it's set to.
UPDATE analytic_scan
SET created_dts = created_dts at time zone 'US/Central' -- Tell Postgres to convert the value back to where we started.
Это похоже на работу (?), Исключая очевидное упущение в работе с летним временем. Я мог бы добавить предложение WHERE
, чтобы справиться с этим, но это не меняет моего вопроса. И теперь вопрос, у меня есть количество записей, таких как:
analytic_productivity 728,708
analytic_scan 4,296,273
analytic_sterilizer_load 136,926
analytic_sterilizer_loadinv 327,700
record_changes_log 17,949,132
Итак, не массово, но не ничего. Есть ли способ разумно нарезать данные в SQL, чтобы
- Каждая строка обновлялась один раз
- Ни одна строка не обновлялась более одного раза
- Не слишком много строкобновляются за один раз
Во всех таблицах есть поле PK UUID ID, у пары есть сгенерированный столбец идентификаторов, подобный фрагменту из этой таблицы отчетов:
CREATE TABLE IF NOT EXISTS "data"."analytic_productivity" (
"id" uuid NOT NULL DEFAULT NULL,
"pg_con_id" integer GENERATED BY DEFAULT AS IDENTITY UNIQUE,
"data_file_id" uuid NOT NULL DEFAULT NULL,
"start_utc" timestamptz NOT NULL DEFAULT '-infinity',
"start_local" timestamptz NOT NULL DEFAULT '-infinity',
"end_utc" timestamptz NOT NULL DEFAULT '-infinity',
"end_local" timestamptz NOT NULL DEFAULT '-infinity')
У меня была идея использовать подстроку или хеш UUID::text
для создания меньших партий:
select * from analytic_sterilizer_loadinv
where left(id::text,1) = 'a'
Это кажется медленным и ужасным. Хеш выглядит немного лучше:
select abs(hashtext(id::text)) % 64,
count(*)
from analytic_sterilizer_loadinv
Размеры блоков не такие уж четные, но они могут быть достаточно хорошими, и я могу увеличить количество блоков, если это необходимо. К сожалению, я не знаю, как запустить мой код в цикле в SQL с использованием сегментов. Если кто-то должен указать, как, я был бы благодарен. И, если есть простая встроенная функция разбиения на блоки, я хотел бы знать об этом.
Я не продумал четкую проблему того, как обращаться с входящими данными, которые могут попасть вмодификации, если не считать блокировки всей таблицы. Что я мог бы сделать.