Обновите таблицу данных VERY LARGE PostgreSQL эффективно
У меня очень большая таблица базы данных в PostgresQL и столбец вроде "скопирован". Каждая новая строка начинается без загрузки и позже будет воспроизведена на другую. В этой таблице есть частичный индекс "btree (ID), WHERE replicated = 0". Фоновая программа делает выбор для не более 2000 записей (LIMIT 2000), работает над ними, а затем фиксирует изменения в одной транзакции с использованием 2000 подготовленных sql-команд.
Теперь проблема заключается в том, что я хочу предоставить пользователю опцию reset для этого реплицированного значения, снова сделайте ее равной нулю.
Набор таблиц обновления, реплицированный = 0;
невозможно:
- Требуется очень много времени.
- Он дублирует размер столбца из-за MVCC
- Это делается за одну транзакцию: она либо терпит неудачу, либо проходит через.
Мне действительно не нужны транзакционные функции для этого случая: если система опускается, она обрабатывает только ее части.
Несколько других проблем:
Выполнение
update set replicated=0 where id >10000 and id<20000
также плохо: он выполняет последовательное сканирование по всей таблице, которое происходит слишком медленно.
Если бы это не было сделано, это все равно было бы медленным, потому что было бы слишком много запросов.
Мне действительно нужно пройти через все строки, изменить их и не привязываться к гигантской транзакции.
Странно,
UPDATE table
SET replicated=0
WHERE ID in (SELECT id from table WHERE replicated= LIMIT 10000)
также медленный, хотя это должно быть хорошо: пройдите по таблице в порядке DISK...
(Обратите внимание, что в этом случае был также индекс, который охватывал это)
(Обновление LIMIT, как Mysql, недоступно для PostgresQL)
BTW: Реальная проблема сложнее, и мы говорим о встроенной системе, которая уже развернута, поэтому удаленные изменения схемы сложны, но возможны
К сожалению, PostgresQL 7.4.
Количество строк, о которых я говорю, равно, например, 90000000. Размер базы данных может составлять несколько десятков гигабайт.
Сама база данных содержит только 5 таблиц, одна очень большая.
Но это неплохой дизайн, потому что эти встроенные ящики работают только с одним типом сущности, это не ERP-система или что-то в этом роде!
Любые идеи?
Ответы
Ответ 1
Как добавить новую таблицу для хранения этого реплицированного значения (и первичный ключ для привязки каждой записи к основной таблице). Затем вы просто добавляете запись для каждого реплицированного элемента и удаляете записи для удаления реплицированного флага. (Или, может быть, наоборот - запись для каждой не реплицированной записи, в зависимости от которой является общим случаем).
Это также упростит случай, когда вы хотите вернуть все обратно в 0, поскольку вы можете просто обрезать таблицу (которая обнуляет размер таблицы на диске, вам даже не нужно вакуумировать, чтобы освободить пространство)
Ответ 2
Если вы пытаетесь выполнить reset всю таблицу, а не только несколько строк, она обычно быстрее (на очень больших наборах данных - не на обычных таблицах) - просто CREATE TABLE bar AS SELECT everything, but, copied, 0 FROM foo
, а затем меняет местами таблицы и отбрасывает Старый. Очевидно, вам нужно будет убедиться, что ничего не будет вставлено в исходную таблицу, пока вы это делаете. Вам также нужно будет воссоздать этот индекс.
Изменить: простое улучшение во избежание блокировки таблицы при копировании 14 гигабайт:
lock ;
create a new table, bar;
swap tables so that all writes go to bar;
unlock;
create table baz as select from foo;
drop foo;
create the index on baz;
lock;
insert into baz from bar;
swap tables;
unlock;
drop bar;
(пусть записи происходят, когда вы делаете копию, и вставляете их post-factum).
Ответ 3
В то время как вы не можете решить проблему использования пространства (это временно, только до вакуума), вы, вероятно, действительно можете ускорить процесс с точки зрения времени. Тот факт, что PostgreSQL использует MVCC, означает, что вы сможете это сделать без каких-либо проблем, связанных с недавно вставленными строками. При создании таблицы в качестве выбора будут затронуты некоторые проблемы с производительностью, но она не позволит продолжать использовать таблицу и занимает столько же места. Просто опустите индекс и перестройте его, затем сделайте вакуум.
drop index replication_flag;
update big_table set replicated=0;
create index replication_flag on big_table btree(ID) WHERE replicated=0;
vacuum full analyze big_table;
Ответ 4
Это псевдокод. Вам понадобится 400MB (для ints) или 800MB (для bigints) временный файл (вы можете сжать его с помощью zlib, если это проблема). Для вакуума потребуется около 100 сканов таблицы. Но он не будет раздувать таблицу более 1% (не более 1000000 мертвых строк в любое время). Вы также можете торговать меньшим количеством сканирований для большего раздувания таблицы.
// write all ids to temporary file in disk order
// no where clause will ensure disk order
$file = tmpfile();
for $id, $replicated in query("select id, replicated from table") {
if ( $replicated<>0 ) {
write($file,&$id,sizeof($id));
}
}
// prepare an update query
query("prepare set_replicated_0(bigint) as
update table set replicated=0 where id=?");
// reread this file, launch prepared query and every 1000000 updates commit
// and vacuum a table
rewind($file);
$counter = 0;
query("start transaction");
while read($file,&$id,sizeof($id)) {
query("execute set_replicated_0($id)");
$counter++;
if ( $counter % 1000000 == 0 ) {
query("commit");
query("vacuum table");
query("start transaction");
}
}
query("commit");
query("vacuum table");
close($file);
Ответ 5
Я думаю, что лучше изменить свои postgres до версии 8.X. вероятно, причиной является низкая версия Postgres. Также попробуйте этот запрос ниже. Надеюсь, это поможет.
UPDATE table1 SET name = table2.value
FROM table2
WHERE table1.id = table2.id;
Ответ 6
Я предполагаю, что вам нужно сделать
а. скопируйте значение PK 2000 записей во временную таблицу с тем же стандартным лимитом и т.д.
б. выберите те же записи 2000 и выполните необходимые операции в курсоре, как есть.
с. В случае успеха запустите один запрос на обновление записей в таблице temp. Очистите таблицу темпа и выполните шаг a еще раз.
д. Если не удалось, очистите таблицу temp без запуска запроса на обновление.
Простой, эффективный и надежный.
С Уважением,
KT