2016-09-09 2 views
1

Существует таблица с этой структурой:Postgres, сортировочные

    Table "public.all_emails" 
│ Column | Type | Modifiers 
│ ----------- + -------- + ----------- 
│ email | text | 
│ frequency | bigint | 
│Indexes: 
│ "all_emails_email_idx" UNIQUE, btree (email) 

Я хочу, чтобы переместить все записи из этой таблицы в другую базу данных, делая еще несколько операций с ними. Чтобы ускорить работу, я написал многопроцессорное приложение, которое занимает несколько раз конкретную часть таблицы. Для того, чтобы знать, какие из следующего процесса начинается, сортировать таблицу следующим образом:

Select email from all_emails order by email limit # {PULL_SIZE} offset # {offset} 

С большим количеством записей в таблице, эта операция является весьма дорогостоящей и не является оптимальной. Как я могу сделать это лучше?

ответ

1

Вы можете CLUSTER таблицу для этой цели:

CLUSTER all_emails USING all_emails_email_idx; 
ANALYZE all_emails; 

кластеризация физически переупорядочивают строки в таблице в соответствии с индексом указанным. Таким образом, адреса электронной почты упорядочиваются в соответствии с адресом электронной почты, а затем запрос, который обрабатывает, как и любой другой запрос, найдет все строки из запрошенного подмножества на ограниченном количестве страниц на диске, а также сократит ввод-вывод как любой порядок (поскольку планировщик запросов распознает, что таблица кластеризована по определенному индексу). Команда ANALYZE обновляет статистику таблицы после кластеризации, чтобы помочь планировщику запросов сделать оптимальный выбор.

Это действительно работает только в таблице, которая доступна только для чтения или не очень часто обновляется или добавляются новые строки, поскольку кластеризация не поддерживается: это одноразовый процесс. Кластеризация также является довольно «дорогостоящим» процессом, потому что вся таблица переписана и требуется эксклюзивная блокировка таблиц. Вы можете периодически перегруппировать таблицу, используя тот же индекс, с сокращенной формой CLUSTER all_emails.

1

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

Было бы заманчиво разбить таблицу на ctid, физическое расположение кортежа в таблице, но PostgreSQL не оптимизирует доступ по ctid для операторов, отличных от =:

test=> EXPLAIN SELECT * FROM large WHERE ctid BETWEEN '(390, 0)' AND '(400,0)'; 
┌───────────────────────────────────────────────────────────────────┐ 
│       QUERY PLAN        │ 
├───────────────────────────────────────────────────────────────────┤ 
│ Seq Scan on large (cost=0.00..1943.00 rows=500 width=8)   │ 
│ Filter: ((ctid >= '(390,0)'::tid) AND (ctid <= '(400,0)'::tid)) │ 
└───────────────────────────────────────────────────────────────────┘ 
(2 rows) 

То же самое для вставок: не имея возможности показывать числа, я уверен, что один процесс INSERT ing или COPY в одну таблицу не будет медленнее, чем несколько процессов, загружающих данные в одну таблицу.

Так как кажется, что узким местом является обработка строк между SELECT в начале координат и INSERT в пункте назначения, я хотел бы предложить следующее:

  1. Иметь один поток, который выполняет одну SELECT * FROM all_emails.

  2. Создайте несколько потоков, которые могут выполнять дорогостоящую обработку параллельно.

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

  4. Еще одна нить собирает результаты параллельных рабочих и записывает их в ввод для оператора COPY tablename FROM STDIN, который он выполняет.

+0

Как это устраняет проблему, которую OP хочет прочитать рядами в партиях, отсортированных по электронной почте? – Patrick

+0

Он/она этого не хочет. «ORDER BY» просто дает смысл комбинации «OFFSET»/«LIMIT». Цель состоит в том, чтобы как можно быстрее переместить таблицу из одной базы данных в другую, и вопрос в том, есть ли способ сделать ее быстрее с распараллеливанием. –

+0

@LaurenzAlbe, Возможно, вы не совсем меня правильно поняли. Мой скрипт имеет несколько параллельных процессов, чтобы взять часть таблицы для выполнения некоторых операций над строками и вставить ее в другую таблицу. Проблема была в неоптимальном образце, требуемом для каждой части процесса. –

Смежные вопросы