Есть ли лучший способ восстановить данные из сотен миллионов записей, распределенных за длительный период времени? - PullRequest
1 голос
/ 03 августа 2020

(в первую очередь - извиняюсь за название, но лучше не придумал)

Вот моя проблема - у меня есть таблица с 4 столбцами - entity::INT, entry::TEXT , state::INT и day::INT.

Может быть от 50 до 1000 entities. Каждый entity может иметь более 100 миллионов entries. Каждый entry может иметь один или несколько states, который изменяется, если данные, хранящиеся в записи, изменились, но только один state может быть записан для любого конкретного day. day начинается с единицы и увеличивается каждый день.

Пример:

entity | entry  | state     | day
-------------------------------------
1      | ABC123 | 1         | 1
1      | ABC124 | 2         | 1
1      | ABC125 | 3         | 1
...
1      | ABC999 | 999       | 1
2      | BCD123 | 1000      | 1
...
1      | ABC123 | 1001      | 2
2      | BCD123 | 1002      | 3

Индекс установлен на (entity, day, state).

Чего я хочу достичь заключается в эффективном выборе наиболее актуального состояния каждой записи в день N.

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

Мне нужно прекратить писать эту «полную» версию еженедельно и вместо этого иметь достаточно быструю настройку для этого. Я решил использовать DISTINCT ON с другим индексом, установленным на (entity, entry, day DESC, state), чтобы я мог:

SELECT DISTINCT ON (entity, entry) entry, state
FROM table
WHERE entity = <entity> AND day <= <day>
ORDER BY entity, entry, day DESC, state;

Будет ли это наиболее эффективным способом сделать это или есть способы лучше? Или entry, возможно, имеющий сотни миллионов уникальных значений, делает его плохим выбором для второго столбца в индексе, и производительность в конечном итоге остановится?

Ответы [ 2 ]

0 голосов
/ 03 августа 2020

DISTINCT ON прост, а производительность отличная - для нескольких строк на запись. См .:

Не для многие строк на запись.

Каждая сущность может иметь более 100 миллионов записей

См .:

Предполагая, что таблица entry содержит одну строку для каждой существующей записи (каждая соответствующая отдельная комбинация (entity, entry)), это запрос очень эффективен для получения последнего состояния за данный день:

SELECT e.entity, e.entry, t.day, t.state
FROM   entry e
LEFT   JOIN LATERAL (
   SELECT day, state
   FROM   tbl
   WHERE  (entity, entry) = (e.entity, e.entry)
   AND    day <= <day>  -- given day
   ORDER  BY day DESC
   LIMIT  1
   ) t ON true;
ORDER  BY e.entity, e.entry; -- optional

Используйте CROSS JOIN LATERAL вместо LEFT JOIN, если вам нужны только записи, содержащие хотя бы одну строку в tbl.

Идеальный индекс для этого - на (entity, entry, day) INCLUDE (state).

Если у вас нет таблицы entry, подумайте о ее создании. (Как правило, он должен быть.) Методы rCTE, описанные в связанном ответе выше , также могут быть использованы для создания такой таблицы.

0 голосов
/ 03 августа 2020

Вы хотите ранжировать записи по времени и брать самую последнюю. Это то же самое, что ранжировать их в обратном порядке и брать первое. И ROW_NUMBER() - один из способов сделать это.

WITH
    ranked AS
(
    SELECT
        *, 
        ROW_NUMBER()
            OVER (
                PARTITION BY entity, entry
                    ORDER BY day DESC
            )
              AS entity_entry_rank
    FROM
        yourTable
)
SELECT
    *
FROM
     ranked
WHERE
    entity_entry_rank = 1

Столбец дня может стать отметкой времени, и вам не нужно хранить новую копию каждый день.

соответствующий индекс будет (entity, entry, timestamp)

Кроме того, обычно используются две таблицы. Один с историей, другой с последним значением. Это позволяет быстрее использовать текущее значение при незначительных накладных расходах на диск.

(Приносим извинения за ошибки или форматирование, я разговариваю по телефону.)

...