Эффективное хранение 7.300.000.000 строк - PullRequest
23 голосов
/ 20 марта 2009

Как бы вы решили следующую проблему хранения и поиска?

Примерно 2.000.000 строк будут добавляться каждый день (365 дней / год) со следующей информацией в строке:

  • id (уникальный идентификатор строки)
  • entity_id (принимает значения от 1 до 2.000.000 включительно)
  • date_id (увеличивается на единицу каждый день - принимает значения от 1 до 3,650 (десять лет: 1 * 365 * 10))
  • значение_1 (принимает значения от 1 до 1.000.000 включительно)
  • значение_2 (принимает значения от 1 до 1.000.000 включительно)

entity_id в сочетании с date_id является уникальным. Следовательно, в таблицу можно добавить не более одной строки для каждой сущности и даты. База данных должна содержать данные за 10 лет (7.300.000.000 строк (3.650 * 2.000.000)).

То, что описано выше, это шаблоны записи. Шаблон чтения прост: все запросы будут выполняться по определенному entity_id. То есть получить все строки, описывающие entity_id = 12345.

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

Теперь - как бы вы решили описанную проблему?

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

Ответы [ 7 ]

28 голосов
/ 20 марта 2009

«Теперь - как бы вы решили описанную проблему?»

С простыми плоскими файлами.

Вот почему

"все запросы будут сделаны на конкретный entity_id. То есть восстановить все строки, описывающие entity_id = 12345. "

У вас есть 2.000.000 сущностей. Раздел на основе номера объекта:

level1= entity/10000
level2= (entity/100)%100
level3= entity%100

Каждый файл данных level1/level2/level3/batch_of_data

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

Если кому-то нужна реляционная база данных, загрузите файлы для данного entity_id в базу данных для их использования.


Редактировать На номера дня.

  1. Правило уникальности date_id / entity_id - это , а не , то, что должно быть обработано. Это (а) тривиально накладывается на имена файлов и (б) не имеет отношения к запросу.

  2. * "Ролловер" date_id ничего не значит - нет запроса, поэтому не нужно ничего переименовывать. date_id должен просто расти без привязки к дате эпохи. Если вы хотите удалить старые данные, удалите старые файлы.

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

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


Редактировать при открытии / закрытии

Для записи вы должны оставить файлы открытыми. Вы делаете периодические сбросы (или закрываете / открываете снова), чтобы убедиться, что данные действительно отправляются на диск.

У вас есть два варианта архитектуры вашего писателя.

  1. Иметь единый процесс записи, который объединяет данные из различных источников. Это полезно, если запросы относительно часты. Вы платите за объединение данных во время записи.

  2. Несколько файлов открываются одновременно для записи. При запросе объедините эти файлы в один результат. Это полезно, если запросы относительно редки. Вы платите за объединение данных во время запроса.

13 голосов
/ 20 марта 2009

Использовать разбиение . С вашим шаблоном чтения вы хотите разделить на entity_id хеш.

5 голосов
/ 20 марта 2009

Возможно, вы захотите взглянуть на эти вопросы:

Большой первичный ключ: 1+ миллиардов строк MySQL + InnoDB?

Большие таблицы MySQL

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

НТН.,

S

4 голосов
/ 21 марта 2009

Ваше приложение имеет те же характеристики, что и мое. Я написал собственный движок хранения MySQL для эффективного решения проблемы. Описано здесь

Представьте, что ваши данные размещены на диске в виде массива из 2M записей фиксированной длины (по одной на объект), каждая из которых содержит 3650 строк (по одной в день) по 20 байт (строка для одного объекта в день).

Ваш шаблон чтения читает одну сущность. Он непрерывен на диске, поэтому требуется 1 поиск (около 8 миллисекунд) и чтение 3650x20 = около 80 КБ со скоростью, возможно, 100 МБ / с ... так что это делается за доли секунды, легко удовлетворяя вашему чтению 1 запрос в секунду шаблон.

Обновление должно записать 20 байтов в 2M разных местах на диске. В простейшем случае это потребует 2M запросов, каждый из которых занимает около 8 миллисекунд, так что это займет 2M * 8ms = 4,5 часа. Если вы распределите данные по 4 дискам raid0, это может занять 1,125 часа.

Однако места находятся всего в 80К друг от друга. Это означает, что в блоке 16 МБ есть 200 таких мест (типичный размер дискового кэша), поэтому он может работать с чем угодно, до 200 раз быстрее. (1 минута) Реальность находится где-то посередине.

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

Вы можете написать именно то, что я описал. Помещение кода в подключаемый модуль хранения MySQL означает, что вы можете использовать MySQL для запроса данных с различными генераторами отчетов и т. Д.

Кстати, вы можете исключить дату и идентификатор объекта из сохраненной строки (потому что они являются индексами массива) и могут быть уникальным идентификатором - если он вам действительно не нужен, поскольку (идентификатор объекта, дата) уникальный, и сохраните 2 значения как 3-байтовый int. Тогда ваша сохраненная строка будет 6 байтов, и у вас будет 700 обновлений на 16 МБ, и, следовательно, более быстрые вставки и меньший файл.

Редактировать Сравнить с плоскими файлами

Я заметил, что комментарии в целом одобряют плоские файлы. Не забывайте, что каталоги - это просто индексы, реализованные файловой системой, и они обычно оптимизированы для относительно небольшого количества относительно больших элементов. Доступ к файлам, как правило, оптимизирован, так что он ожидает открытия относительно небольшого числа файлов и имеет относительно большие накладные расходы для открытия и закрытия, а также для каждого открытого файла. Все эти «относительно» относятся к типичному использованию базы данных.

Использование имен файловых систем в качестве индекса для идентификатора сущности, который я считаю непрямым целым числом от 1 до 2 миллионов, противоречит интуиции. В программировании вы будете использовать массив, а не хеш-таблицу, например, и вы неизбежно столкнетесь с большими накладными расходами на дорогой путь доступа, который может быть просто операцией освобождения массива.

Поэтому, если вы используете плоские файлы, почему бы не использовать только один плоский файл и индексировать его?

Редактировать при исполнении

Производительность этого приложения будет зависеть от времени поиска диска. Вычисления, которые я сделал выше, определяют лучшее, что вы можете сделать (хотя вы можете сделать INSERT быстрее, замедляя SELECT - вы не можете сделать их оба лучше). Неважно, используете ли вы базу данных, простые файлы или один простой файл, , за исключением , вы можете добавить дополнительные запросы, которые вам на самом деле не нужны, и еще больше замедлить их. Например, индексирование (будь то индекс файловой системы или индекс базы данных) вызывает дополнительные операции ввода-вывода по сравнению с «поиском в массиве», и это замедляет работу.

Редактировать в контрольных измерениях

У меня есть таблица, которая очень похожа на вашу (или почти как на один из ваших разделов). Это были 64 тыс. Сущностей, а не 2 млн (1/32 от вашего) и 2788 «дней». Таблица была создана в том же порядке INSERT, что и у вас, и имеет тот же индекс (entity_id, day). SELECT для одного объекта требует 20,3 секунды для проверки 2788 дней, что составляет около 130 запросов в секунду, как и ожидалось (на дисках со средним временем поиска 8 миллисекунд). Время SELECT будет пропорционально количеству дней и мало зависит от количества объектов. (Это будет быстрее на дисках с меньшим временем поиска. Я использую пару SATA2 в RAID0, но это не имеет большого значения).

Если вы переупорядочите таблицу в порядке сущностей ALTER TABLE x ЗАКАЗАТЬ (ENTITY, DAY) Затем тот же самый SELECT занимает 198 миллисекунд (потому что он читает объект заказа в одном доступе к диску). Однако операция ALTER TABLE заняла 13,98 дней (для 182M строк).

Есть еще несколько вещей, о которых говорят измерения 1. Ваш индексный файл будет таким же большим, как ваш файл данных. Это 3 ГБ для этой таблицы. Это означает (в моей системе) весь индекс на скорости диска, а не на скорости памяти.

2. Ваша ставка INSERT будет снижаться логарифмически. INSERT в файл данных является линейным, но вставка ключа в индекс - log. На 180M записях я получал 153 INSERT в секунду, что также очень близко к скорости поиска. Это показывает, что MySQL обновляет листовой индексный блок почти для каждой INSERT (как и следовало ожидать, поскольку он индексируется на сущности, но вставляется в порядке дня). Таким образом, вы смотрите на 2M / 153 секунды = 3,6 часа, чтобы выполнить ежедневную вставку 2M строк. (Разделенный на любой эффект, который вы можете получить по разделам между системами или дисками).

2 голосов
/ 20 марта 2009

У меня была похожая проблема (хотя в гораздо большем масштабе - о вашем ежегодном использовании каждый день)

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

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

1 голос
/ 20 марта 2009

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

Это позволит вам рассмотреть возможность сжатия некоторых столбцов.

Также рассмотрите возможность архивирования и создания разделов.

0 голосов
/ 24 октября 2014

Если вы хотите обрабатывать огромные данные с миллионами строк, это может рассматриваться как база данных временных рядов, которая регистрирует время и сохраняет данные в базе данных. Некоторые из способов хранения данных используют InfluxDB и MongoDB.

...