Коммит миграций кусками с помощью psql

Я ещё не так давно работаю с «Постгресом» и раз в месяц натыкаюсь на что-то неожиданное.

Недавняя ситуация — дано: миграция, которая обновляет более половины полей таблицы с более чем полумиллиардом записей и содержит в себе довольно затратный по процессорному времени запрос.

В «Оракле» такую миграцию наши ребята, насколько я знаю, разбивали хранимой процедурой на части и коммитили внутри кусками по миллиону, в «Постгресе» так нельзя — блок на языке PL/pgSQL выполняется в собственной транзакции и она прервётся при попытке сделать COMMIT. Автономных транзакций нет.

Испытания на тестовом стенде, приближенном к промышленному, показали, что единым куском обновить не получится — на мастере и слейве первой очереди кончилось место и стенд развалился. Надо было найти какой-то эквивалент сбросу миграции в цикле, по типу того как это делается в «Оракле».

Гугление рецептов принесло способ, который заключается в том, что мы создаём соединение из базы в в себя же и шлём запросы в это соединение. Говорят работает, но способ мне показался муторным, пришлось придумать свой.
DO $$
  DECLARE
    max_id table.id % TYPE;
    query TEXT;
BEGIN
  -- делим таблицу на куски по миллиону, теперь в max_id у нас номер последнего куска
  SELECT CEIL(MAX(id)::float/1000000) INTO max_id FROM mytable;

  -- очищаем файл /tmp/sqlquery
  COPY (SELECT '') TO '/tmp/sqlquery';

  -- айдишники в моём конкретном случае идут с единицы, но более правильно идти с минимума до максимума с шагом
  FOR i IN 0..max_id LOOP
        query = FORMAT(
            'UPDATE mytable '
            'SET something=value '
            'WHERE id BETWEEN ' || i*1000000+1 || ' AND ' || (i+1)*1000000 || ';'
        );

        -- запуск /bin/cat нужен, чтобы дописывать в файл
        EXECUTE  'COPY (SELECT ' || quote_literal(query) || ') TO PROGRAM ''/bin/cat >> /tmp/sqlquery''';
  END LOOP;
END$$;

-- запуск запроса
\i /tmp/sqlquery
Я воспользовался тем, что в накатке миграции у нас участвует утилита psql — официальный консольный клиент к «Постгресу», помимо команд СУБД она умеет выполнять свои собственные команды, чем я и пользуюсь — собираю нужные запросы в файл и запускаю его на выполнение в последней строке.

Обратите внимание на вызов /bin/cat — это важно, так как команда COPY сама не умеет добавлять что-то в файл, она его перезаписывает.
2 комментария
24 марта 2017 08:48

[Без заголовка]

Интересную статью обнаружил на просторах сети: "VRAM Storage Device - How to use the memory on GFX board in a different way". В статье показано как использовать лишнюю видеопамять под swap или как RAM-диск - я, буквально вчера, видел на Linux-сервере без GUI видеокарту с памятью 256Мб.

В статье показано как "откусить" у видеокарты память, которую она всё равно не использует. Я, для интереса, попробовал на нашем development-сервере (стоит S3 ViRGE/DX, 2Mb) откусить половину видеопамяти, по результатам опытов появилась несколько рекомендаций.

Во-первых, нельзя огульно верить всему, что написано в lscpi (см. статью). Если в результатах вывода написано о наличии региона некого размера, это ещё не значит, что на видеокарте именно столько памяти. Лучше посмотреть в лог X Window. Пример:
[root@hulk ~]# lspci -vv
...
02:01.0 VGA compatible controller: S3 Inc. ViRGE/DX or /GX (rev 01) (prog-if 00 [VGA])
        Subsystem: S3 Inc. ViRGE/DX
        ...
        Region 0: Memory at f4000000 (32-bit, non-prefetchable) [size=64M]
...
[root@hulk ~]# grep -i videoram /var/log/Xorg.0.log  
(--) S3VIRGE(0): videoram:  2048k

Во-вторых, лучше сделать modprobe и для остальных модулей, необходимых MTD для работы: mtdcore, mtdchar, mtdblock. Без этого, у нас на сервере, получить доступ к созданному устройству не получилось.

В остальном, всё работает замечательно:
[root@hulk ~]# cat /proc/swaps 
Filename                                Type            Size    Used    Priority
/dev/sda2                               partition       2096472 0       -1
/dev/mtdblock0                          partition       1016    0       0
1 комментарий
24 ноября 2006 19:00

[Без заголовка]

Оказывается, в HTML 3.0 должен был войти тег INPUT TYPE="RANGE", но его, по каким-то причинам, не включили в HTML 3.2, который стали поддерживать браузеры. Но тег поддерживается современными браузерами Opera и Safari, насколько я понимаю, как часть стандарта WebForms. FireFox даже последних версий (не говоря уже об IE) этот тег не понимает.
Комментировать
24 ноября 2006 19:00