Удаление множества строк без их блокировки - PullRequest
7 голосов
/ 06 августа 2010

В PostgreSQL у меня есть запрос, подобный следующему, который удалит 250 тысяч строк из таблицы строк длиной 1 метр:

DELETE FROM table WHERE key = 'needle';

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

DELETE FROM table WHERE key = 'needle' LIMIT 10000;

Этот запрос будет работать хорошо, но, увы, его нет в postgres.

Ответы [ 3 ]

23 голосов
/ 06 августа 2010

Попробуйте выполнить подвыбор и используйте уникальное условие:

DELETE FROM 
  table 
WHERE 
  id IN (SELECT id FROM table WHERE key = 'needle' LIMIT 10000);
2 голосов
/ 06 августа 2010

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

http://www.postgresql.org/docs/current/static/sql-lock.html

http://www.postgresql.org/docs/current/static/explicit-locking.html

0 голосов
/ 06 августа 2010

Ответ Фрака хорошо, но это может быть быстрее, но требует 8.4 из-за поддержки оконных функций (псевдокод):

result = query('select
    id from (
        select id, row_number(*) over (order by id) as row_number
        from mytable where key=?
    ) as _
    where row_number%8192=0 order by id', 'needle');
// result contains ids of every 8192nd row which key='needle'
last_id = 0;
result.append(MAX_INT); // guard
for (row in result) {
    query('delete from mytable
        where id<=? and id>? and key=?', row.id, last_id, 'needle');
    // last_id is used to hint query planner,
    // that there will be no rows with smaller id
    // so it is less likely to use full table scan
    last_id = row.id;
}

Это преждевременная оптимизация & mdash; злая вещь. Осторожно.

...