Эффективная схема MySQL с разделением для огромного набора данных (7.300.000.000 строк и примерно 80 ГБ данных) - PullRequest
3 голосов
/ 20 марта 2009

Это продолжение моего вопроса "Эффективное хранение 7.300.000.000 строк" ( Эффективное хранение 7.300.000.000 строк ).

Я решил использовать MySQL с разметкой, и предварительная схема выглядит следующим образом:

CREATE TABLE entity_values (
  entity_id MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL, # 3 bytes = [0 .. 16.777.215]
  date_id SMALLINT UNSIGNED DEFAULT 0 NOT NULL, # 2 bytes = [0 .. 65.535]
  value_1 MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL, # 3 bytes = [0 .. 16.777.215]
  value_2 MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL, # 3 bytes = [0 .. 16.777.215]
  UNIQUE KEY (entity_id, date_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 PARTITION BY HASH(entity_id) PARTITIONS 25;

Это дает:

  • Строк = 7.300.000.000 строк (согласно требованиям, изложенным в предыдущем посте)
  • Размер / строка = 11 байтов (3 + 2 + 3 + 3)
  • Общий размер = 7.300.000.000 строк * 11 байтов = 80.300.000.000 байтов = 80,3 ГБ
  • Разделы = 25 (3,2 ГБ / раздел, размер раздела несколько произвольный)

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

Теперь к моему вопросу - учитывая требования, изложенные в моем предыдущем посте, и схему выше, есть ли у вас какие-либо предложения по дальнейшей оптимизации / настройкам, которые можно сделать? Или приведенная выше схема "оптимальна", учитывая, что я решил использовать MySQL?

Обновление: Я попытался загрузить текущий набор данных в схему, приведенную выше, и строки 8.570.532 заняли 212.000.000 байтов дискового пространства, что дает примерно 24.7 байта на строку.

Обновление: Обратите внимание, что индекс, охватывающий entity_id + date_id, будет использоваться также для запросов, направленных только на entity_id.

Ответы [ 3 ]

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

Если вы обычно извлекаете все (или большинство) данных для одного идентификатора сущности, вам следует рассмотреть возможность сделать индекс просто идентификатором сущности, а не (entity_id, date_id) - если только вам не нужна база данных для уникальных проверок.

Эффект заключается в уменьшении индекса, чтобы вы могли получить больше его в памяти. Ваша цель должна состоять в том, чтобы индекс был в памяти. Даже если вам нужно выполнить SELECT..ORDER BY DATE, вы обнаружите, что MySQL может упорядочивать 3650 значений за доли секунды на лету (без индекса). Эта проблема - время чтения строк с диска.

Однако ваша основная проблема с производительностью заключается в том, что INSERT приводит к тому, что данные для одного объекта распределяются по диску, что требует доступа к диску для каждого (объект, дата), что заставит ваш запрос выполняться со скоростью несколько сотен строк в секунду. Ваше разбиение не поможет в этом, потому что каждый объект находится в одном разделе, а строки распределены по его диску. (RAID0 на дисках немного поможет).

Чтобы получить эффективный поиск, вам нужно получить данные для смежного объекта на диске, что означает переупорядочение данных из порядка INSERT. Вы можете сделать это с помощью MySQL ALTER TABLE .. ORDER BY ... но это займет вечность. У меня была таблица строк размером 182M, которая выполняла команду ALTER TABLE .. ORDER BY в течение последних 2 недель, и она еще не завершена.

Вот почему я написал собственный движок хранения!

Кстати, я не уверен, что вы вообще что-то получаете, разбивая на разделы, если только вы не разбиваете на несколько серверов или хотя бы на несколько дисков. Тяжелая работа, которую должен выполнить MySQL, не упрощается путем разбиения. Это все о времени доступа к диску.

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

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

Вы получаете некоторый параллелизм обработки (при условии, что у вас есть несколько процессоров) с секционированием, но ваша система будет связана с вводом / выводом, а не с процессором. Если вы используете процессор более 2%, вы, вероятно, делаете то, что вам не нужно (или что-то, что не является вашим приложением).

Я писал, оптимизировал и управлял этим видом приложений в течение девяти лет, используя MySQL ... и у меня есть все шрамы, которые вы могли ожидать от опыта. Когда ваши данные значительно превышают размер вашей памяти (что я и определяю как «огромный»), проблема с производительностью будет Дисковый ввод / вывод , что означает первичное число поиск дисков . Удачи !!

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

Одна вещь, которую я не совсем понимаю, это то, как вы планируете обрезать свои данные. У вас есть 2 миллиона строк в день, но вы не указали, сколько данных вы планируете хранить. В какой-то момент вы захотите просрочить данные по возрасту (по всей вероятности).

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

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

MyISAM имеет функцию под названием «одновременная вставка», которую вам почти наверняка придется использовать постоянно для достижения параллелизма и производительности; это требует правила «не удаляет», что означает, что вы можете удалять только удалив разделы.

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

Сказав все это, 80G не так уж велик, и у меня, возможно, возник соблазн сохранить все это в одной таблице и использовать InnoDB для обеспечения одновременного доступа.

О да, и если бы вы использовали InnoDB, у вас мог бы быть первичный ключ entity_id, date_id, что означает, что он будет кластеризовать строки с одинаковым entity_id. Вы, вероятно, хотели бы, чтобы вторичный индекс для date_id включал эффективное сокращение.

Пожалуйста, проверьте это с вашими размерами производственных данных и сообщите нам, что вы найдете!

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

Вы указали в своем предыдущем вопросе, что получите все строки для entity_id; однако, если вы планируете получать диапазоны дат для определенных объектов, вы можете использовать Подразделение (также называемое составным разделением) . В зависимости от вашего использования, основным разделом может быть entity_id, а под разделом - год или другой диапазон дат. Вы также можете изменить это, если это имеет смысл в вашей системе.

...