Дизайн базы данных для обработки 1 миллиарда строк и подсчета

10

Мы получаем данные GPS в режиме реального времени со скоростью около 5000 pr. минута (с 4-х TCP-серверов). Каждый сервер использует одно соединение для вставки данных и буферизует данные между вставками. Приблизительно каждые 15 минут служба извлекает эти данные и обрабатывает их в командировках. После того, как сгенерированы поездки, фактические данные GPS обычно не так важны, только если пользователь хочет видеть маршрут на карте.

Проблема в том, что, похоже, база данных пытается справиться со скоростью вставляемых данных. Иногда, когда нагрузка увеличивается, время вставки внезапно резко увеличивается (> 30 секунд), что, в свою очередь, позволяет буферизовать больше данных, что, в свою очередь, приводит к увеличению числа вставок и увеличению продолжительности вставки.

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

Текущий дизайн

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

Дизайн стола

  • Идентификатор (ПК, уникальный идентификатор)
  • DeviceId (FK, int)
  • PersonId (FK, int)
  • VehicleId (FK, int)
  • TokenId (FK, int)
  • UtcTime (PK, datetime2 (3))
  • Широта (плавание)
  • Долгота (с плавающей точкой)
  • Скорость (smallint)
  • Заголовок (smallint)
  • Сателлиты (tinyint)
  • IOData (varbinary (100))
  • IgnitionState (tinyint)
  • UserInput (tinyint)
  • CreateTimeUtc (datetime2 (3))

индексы

  • DeviceId_CreateTimeUtc_Desc
  • DeviceId_UtcTime_Desc (кластеризованный)
  • PersonId_UtcTime_Desc
  • TokenId_UtcTime_Desc
  • VehicleId_UtcTime_Desc

Каждая неделя в настоящее время занимает около 10 ГБ, включая индексы, и в настоящее время в основной базе данных находится около 300 ГБ данных.

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

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

Машина представляет собой 8-ядерный HP с 12 ГБ памяти, а диск с основной базой данных работает под управлением RAID 10.

идеи

  • Ограничьте объем данных, хранящихся в первичной базе данных, например, максимум 1 месяц. По крайней мере, это сделает базу данных более управляемой для резервного копирования / восстановления, но можем ли мы ожидать улучшения производительности при этом?
  • Создайте 2 файла в файловой группе для текущих данных и распределите их по 2 различным физическим разделам
  • Создайте базы данных master-slave, содержащие текущие данные, чтобы операции вставки и чтения выполнялись в разных базах данных
  • Поместите файлы с текущими данными на SSD-диски (может ли зеркалирование повлиять на производительность SSD-дисков?)

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

sondergard
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Пол Уайт 9

Ответы:

8

5000 вставок в минуту - это около 83 вставок в секунду. С 5 индексами это 400 физических строк, вставленных в секунду. Если бы рабочая нагрузка была в памяти, это не создавало бы проблем даже для самых маленьких серверов. Даже если это была построчная вставка с использованием самого неэффективного способа, который я могу себе представить. 83 тривиальных запроса в секунду просто не интересны с точки зрения процессора.

Возможно, вы привязаны к диску. Вы можете убедиться в этом, посмотрев статистику ожидания или STATISTICS IO.

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

Представьте себе таблицу, в которую вы вставляете физически только в конце из-за постоянно увеличивающегося ключа. Рабочий набор будет одна страница: последняя. Это также сгенерирует последовательный ввод-вывод, когда ленивый пишущий или контрольный процесс записывает «конец» таблицы на диск.

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

Вы в середине. Ваши индексы имеют структуру (SomeValue, SequentialDateTime). Первый компонент частично рандомизирует последовательность, обеспечиваемую вторым. Я предполагаю, что есть довольно много возможных значений для " SomeValue", так что у вас есть много случайно расположенных точек вставки в ваших индексах.

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

Если бы вы могли уменьшить размер еженедельных «разделов» или немного увеличить объем памяти сервера, у вас, вероятно, все в порядке.

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

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

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

USR
источник
1
@usr Спасибо за очень подробный и понятный ответ! Мы на самом деле обсуждали увеличение серверной памяти, не зная, какой это будет эффект - но теперь у нас действительно есть веская причина для этого :) Вы правы, что SomeValue частично рандомизирует точки вставки - возможно, есть около 10000 идентификаторов устройств. Что касается промежуточной таблицы, является ли ваше предложение таблицей без каких-либо индексов, а затем заданием вставлять в основную таблицу каждые X минут?
Зондергард
@usr Reg. Ваше предложение по преобразованию кластерного индекса в последовательный, мы могли бы добавить авто-inc. тождественный столбец (целое число) и измените кластеризованный индекс на этот столбец с единственной целью сохранить его последовательным? Он не будет уникальным для всех таблиц, но пока первичный ключ, у нас все будет хорошо.
Зондергард
1
Если промежуточная таблица мала и ваши запросы могут с ней справиться, вам вообще не нужно индексировать. Но вы могли бы .; Одной из стратегий было бы сделать CI в столбце идентификаторов (как вы говорите). Это может творить чудеса, если CI велик, а другие индексы малы. Поскольку записи CI являются последовательными, они вносят гораздо меньший вклад в вашу проблему. Эта стратегия наиболее успешна, если есть существенная разница в размерах .; Другая идея будет иметь один стол в день. Возможно слияние ежемесячно.
USR
Итак, мы рассмотрели создание столбца идентификаторов для CI, но, к сожалению, это невозможно в разделенном представлении (столбец идентификаторов не разрешен, значения по умолчанию и все столбцы должны быть включены в вставку). Возможно, разделенный вид был плохо выбранным, хотя его рекомендовал консультант
sondergard
2
Если серьезно, для тех, кто сталкивается с той же проблемой, если у вас много записей и только несколько чтений, вы действительно хотите добавить в конце и задержать индексирование. С другой стороны, если вам нужно быстрое чтение, и вам все равно, сколько времени потребуется для вставки, вам нужен кластеризованный индекс.
Тиктак