Длительные транзакции

big-trot
Дата: 13.04.2015 12:42:15
Добрый день.

Есть таблица (planet_osm_line) - 87 млн записей. Из этой таблицы путем типового запроса SELECT ... INTO TABLE ..., выполняется формирование новой таблицы. Запрос выполняется больше суток. Вопрос - как понять, что запрос выполняется, а не завис? Куда и как смотреть, что бы понять что вставка данных идет и нужно просто набраться терпения и дождаться результата?
Спасибо.
Alexius
Дата: 13.04.2015 13:14:29
big-trot,

смотреть в pg_stat_activity поле waiting у строки с запросом, если оно false и status=active - значит запрос выполняется (но не значит что за разумное время завершится). понять, завершится ли можно по explain запроса.
по top/iotop/iostat можно понять во что запрос упирается: в cpu или диски.
Gold_
Дата: 13.04.2015 13:47:57
big-trot,

Если есть в новой таблице поле, заполняемое последовательностью (напрbмер - serial), то можно смотреть значение последовательности.
grufos
Дата: 13.04.2015 15:51:41
big-trot,

В книге "PostgreSQL 9 Administration Cookbook" Simon Riggs, Hannu Krosing
есть функция pg_relation_size_nolock которая показывает как узнать физический размер который занимает таблица на диске.
CREATE OR REPLACE FUNCTION pg_relation_size_nolock(tablename regclass)
RETURNS BIGINT
LANGUAGE plpgsql
AS $$
DECLARE
    classoutput RECORD;
    tsid INTEGER;
    rid INTEGER;
    dbid INTEGER;
    filepath TEXT;
    filename TEXT;
    datadir TEXT;
    i INTEGER := 0;
    tablesize BIGINT;
BEGIN
    --
    -- получаем директорию с данными
    --
    EXECUTE 'SHOW data_directory' INTO datadir;
    -- получаем relfilenode и reltablespace
    SELECT reltablespace as tsid,relfilenode as rid INTO classoutput
    FROM pg_class
    WHERE oid = tablename
    AND relkind = 'r';
    --
    -- генерируем ошибку если не можем найти указанную таблицу
    --
    IF NOT FOUND THEN
        RAISE EXCEPTION 'tablename % not found', tablename;
    END IF;
    tsid := classoutput.tsid;
    rid := classoutput.rid;
    --
    -- получаем внутренний идентификатор объекта самй БД - oid
    --
    SELECT oid INTO dbid
    FROM pg_database
    WHERE datname = current_database();
    -- 
    -- используем внутренее представление о формировании полного имени файла
    --
    IF tsid = 0 THEN
        filepath := datadir || '/base/' || dbid || '/' || rid;
    ELSE
        filepath := datadir || '/pg_tblspc/' || tsid || '/' || dbid || '/' || rid;
    END IF;
    --
    -- получаем размер полученного файла
    --
    SELECT (pg_stat_file(filepath)).size INTO tablesize;
    --
    -- суммируем размеры всех возможных дополнительных файлов, если они есть
    --
    WHILE FOUND LOOP
        i := i + 1;
        filename := filepath || '.' || i;
        --
        -- pg_stat_file возвращает ERROR если не может найти файл
        -- таким образом мы знаем, что больше нечего искать
        --
        BEGIN
            SELECT tablesize + (pg_stat_file(filename)).size INTO tablesize;
        EXCEPTION
            WHEN OTHERS THEN EXIT;
        END;
    END LOOP;
    RETURN tablesize;
END;
$$;

далее используете следующее
-- создаем таблицу нужной структуры и обеспечиваем существование таблицы вне транзакции копирования данных
select * into table_dest from table_source where 1=2; 
-- начинаем копирование
insert into table_dest select * from table_source;


в другом соединении выполняем команду
select round(pg_relation_size_nolock('table_dest'::regclass)*100.0/pg_relation_size_nolock('table_source'::regclass),2);

теперь мы можем видеть в % как много еще осталось скопировать.
Если же разделить полученный размер в байтах (pg_relation_size_nolock('table_dest'::regclass)) на среднюю ширину строки, то можно получить приблизительный объем вставленных строк.
big-trot
Дата: 13.04.2015 16:58:37
Ситуация несколько иная. Выражение SELECT ... INTO TABLE ... похоже не создает сразу таблицу в явном виде, потому что её не видно ни в одном из инструментов. Можно предположить, что таблица появится после выполнения всей операции в целом. В настоящий момент нет возможности обратиться к этой таблице и отследить как идет вставка. В pg_stat_activity процесс находится в активном состоянии. С помощью запроса select pg_database_size(current_database()) видно, что размер базы данных увеличивается. Не ясно, как понять когда процесс завершится.
Maxim Boguk
Дата: 13.04.2015 17:07:39
big-trot,

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

--
Maxim Boguk
www.postgresql-consulting.ru
big-trot
Дата: 13.04.2015 17:12:00
На разумном объеме это все работает, потому что проверялось на меньшей геометрии (данные вставлялись для одного города).
grufos
Дата: 13.04.2015 17:55:41
big-trot
Выражение SELECT ... INTO TABLE ... похоже не создает сразу таблицу в явном виде,

именно поэтому я написал вам, что таблицу источник нужно создать заранее, отдельной командой в отдельном запросе.
grufos
Дата: 13.04.2015 17:57:03
big-trot
Не ясно, как понять когда процесс завершится.

далее вы можете смотреть на время потраченное на запись определенного объема данных и зная каков общий объем примерно понять как долго еще будет идти процесс.
big-trot
Дата: 14.04.2015 18:31:09
Опытным путем пришел к следующей схеме работы с данными больших таблиц.
1. Если необходимо выполнить выборку данных из большой таблицы и результат направить в другую таблицу, то целесообразно результат запросы выгружать сначала в файл с помощью команды COPY, и далее той же командой COPY загрузить данные в предварительно созданную под эти данные таблицу.
2.Не рекомендуется использовать команду типа SELECT ... INTO TABLE. Данной выражение работает на несколько порядков медленнее.
3.Правда не пробовал вариант INSERT INTO ... SELECT... Осмелюсь предположить, что этот вариант равносилен SELECT ... INTO TABLE.

На основании чего были сделаны выводы:
Исходная таблица - 86 млн. записей.
В результате выполнения запроса получено - 4 млн. записей.
Время выборки и копирование результата в файл заняло чуть больше 4-х часов. Время вставки данных из файла в таблицу - секунды.
Время выполнения команды SELECT ... INTO TABLE - запрос еще выполняется. Но уже время выполнения запроса в настоящий меряется сутками.