У меня есть программный проект, над которым я работаю на работе, который сводит меня с ума. Вот наша проблема: у нас есть ряд контактов с данными, которые необходимо регистрировать каждую секунду. Он должен включать время, азимут (массив из 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 дней, что может случиться.
Система, которую мы в настоящее время внедрили, хорошо работает для небольших наборов данных, но не для больших наборов. Наши операторы select и delete могут занять до 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 ГБ ОЗУ, четырехъядерный процессор). Мы используем C / C ++ и MySQL API. ЛЮБЫЕ улучшения скорости были бы ОЧЕНЬ полезными. Если вам нужно, чтобы я что-то прояснил, дайте мне знать. Спасибо!
РЕДАКТИРОВАТЬ: Кстати, если вы не можете предоставить конкретную помощь, может быть, вы можете связать меня с некоторыми отличными учебниками, с которыми вы столкнулись, по оптимизации запросов SQL, разработке схемы или настройке MySQL?