У меня есть программный проект, над которым я работаю на работе, который сводит меня с ума. Вот наша проблема: у нас есть ряд данных контактов, которые должны регистрироваться каждую секунду. Он должен включать время, пеленг (массив из 360-1080 байт), диапазон и несколько других полей. Нашей системе также требуется возможность хранить эти данные до 30 дней. На практике может быть до 100 различных контактов, поэтому максимум может быть от 150 000 000 до примерно 1 000 000 000 различных точек за 30 дней.
Я пытаюсь придумать лучший способ хранения всех этих данных и последующего извлечения. Моей первой мыслью было использовать некоторые СУБД, такие как MySQL. Будучи программистом на C / C ++, у меня очень мало опыта работы с MySQL с такими большими наборами данных. Я баловался этим с небольшими наборами данных, но не настолько большими. Я создал схему ниже для двух таблиц, в которых будут храниться некоторые данные:
CREATE TABLE IF NOT EXISTS `HEADER_TABLE` (
`header_id` tinyint(3) unsigned NOT NULL auto_increment,
`sensor` varchar(10) NOT NULL,
`bytes` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`header_id`),
UNIQUE KEY `header_id_UNIQUE` (`header_id`),
UNIQUE KEY `sensor_UNIQUE` (`sensor`)
) ENGINE=MyISAM AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `RAW_DATA_TABLE` (
`internal_id` bigint(20) NOT NULL auto_increment,
`time_sec` bigint(20) unsigned NOT NULL,
`time_nsec` bigint(20) unsigned NOT NULL,
`transverse` bit(1) NOT NULL default b'0',
`data` varbinary(1080) NOT NULL,
PRIMARY KEY (`internal_id`,`time_sec`,`time_nsec`),
UNIQUE KEY `internal_id_UNIQUE` (`internal_id`),
KEY `time` (`time_sec`)
KEY `internal_id` (`internal_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `rel_RASTER_TABLE` (
`internal_id` bigint(20) NOT NULL auto_increment,
`raster_id` int(10) unsigned NOT NULL,
`time_sec` bigint(20) unsigned NOT NULL,
`time_nsec` bigint(20) unsigned NOT NULL,
`header_id` tinyint(3) unsigned NOT NULL,
`data_id` bigint(20) unsigned NOT NULL,
PRIMARY KEY (`internal_id`, `raster_id`,`time_sec`,`time_nsec`),
KEY `raster_id` (`raster_id`),
KEY `time` (`time_sec`),
KEY `data` (`data_id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1;
Таблица заголовков содержит только 10 строк и является статической. Он просто сообщает, с какого датчика поступили необработанные данные, и количество байтов, выводимых датчиком этого типа. RAW_DATA_TABLE, по сути, хранит необработанные несущие данные (массив из 360-1080 байтов, он представляет до трех выборок на градус). Rel_RASTER_TABLE содержит метаданные для RAW_DATA_TABLE, может быть несколько контактов, которые ссылаются на одну и ту же строку необработанных данных. data_id
, найденное в rel_RASTER_TABLE, указывает на internal_id
некоторой строки в RAW_DATA_TABLE, я сделал это, чтобы уменьшить количество необходимых записей.
Очевидно, как вы, вероятно, можете сказать, у меня проблемы с производительностью при чтении и удалении из этой базы данных. Оператор нашего программного обеспечения может видеть данные в реальном времени по мере их поступления, а также переходить в режим реконструкции и перекрывать диапазон данных за прошлую, например, прошедшую неделю. Наш внутренний сервер журналирования захватывает строки истории и отправляет их на дисплей через интерфейс CORBA. Пока все это происходит, у меня есть рабочий поток, который одновременно удаляет 1000 строк для данных, превышающих 30 дней. Это происходит в случае, если сеанс длится более 30 дней, что может произойти.
Система, которую мы внедрили в настоящее время, хорошо работает для небольших наборов данных, но не для больших наборов. Наши операторы выбора и удаления могут занять до 2 минут, чтобы вернуть результаты. Это полностью убивает производительность нашего потока в реальном времени. Я подозреваю, что мы не проектируем наши схемы правильно, выбираем неправильные ключи, неправильно оптимизируем наши запросы SQL, или некоторые подмножества каждого. Наши записи не будут затронуты, если другие операции не займут слишком много времени.
Вот пример SQL-запроса, который мы используем для получения данных истории:
SELECT
rel_RASTER_TABLE.time_sec,
rel_RASTER_TABLE.time_nsec,
RAW_DATA_TABLE.transverse,
HEADER_TABLE.bytes,
RAW_DATA_TABLE.data
FROM
RASTER_DB.HEADER_TABLE,
RASTER_DB.RAW_DATA_TABLE,
RASTER_DB.rel_RASTER_TABLE
WHERE
rel_RASTER_TABLE.raster_id = 2952704 AND
rel_RASTER_TABLE.time_sec >= 1315849228 AND
rel_RASTER_TABLE.time_sec <= 1315935628 AND
rel_RASTER_TABLE.data_id = RAW_DATA_TABLE.internal_id AND
rel_RASTER_TABLE.header_id = HEADER_TABLE.header_id;
Я заранее прошу прощения за этот длинный вопрос, но я использовал другие ресурсы, и это мое последнее средство. Я полагаю, что постараюсь быть настолько описательным, насколько это возможно. Ребята, вы видите, каким образом я могу улучшить наш дизайн с первого взгляда? Или, в любом случае, мы можем оптимизировать наши операторы выбора и удаления для таких больших наборов данных? В настоящее время мы используем RHEL в качестве ОС и, к сожалению, не можем изменить конфигурацию нашего оборудования на сервере (4 ГБ ОЗУ, Quad Core). Мы используем C / C ++ и MySQL API. ЛЮБЫЕ улучшения скорости были бы ОЧЕНЬ выгодны. Если вам нужно, чтобы я что-то прояснил, пожалуйста, дайте мне знать. Спасибо!
РЕДАКТИРОВАТЬ: Кстати, если вы не можете предоставить конкретную помощь, может быть, вы можете связать меня с некоторыми отличными учебниками, с которыми вы столкнулись, для оптимизации запросов SQL, проектирования схем или настройки MySQL?