3 мая 2009 г.

Работа с двоичными полями в MySQL

Главная идея дипломного проекта была в том, чтобы хранить сигналы (отсчеты АЦП) в базе данных. 

Структура базы:

База данных была спроектирована задолго до меня и эксплуатируется уже несколько лет.
Нас интересует только одна таблица – adcdata, в которой и хранятся отсчеты АЦП. В поле adcdata хранится весь сигнал целиком.

Задача была записывать в это поле сигнал. Данные с АЦП поступаю порциями. При частоте 400кГц, поступает 768кБ/с. Эти данные нужно дописывать в гонец поля adcdata.

Конечно, хотелось писать в базу "налету", без всяких буферов на клиентской стороне. Но задача дозаписи данных в поле (запись в конец поля) для реляционных СУБД оказалась нетривиальной. Информации по таким решениям в интернете я не нашел. Вот несколько алгоритмов, придуманных мной.

Алгоритм №0

Идея: делаем SELECT уже записанных данных, на клиентской стороне приклеиваем к ним новую порцию данных и UPDATE-им все это в базу. Этот вариант пришел в голову первым и является совсем тупым решением. Я его даже не рассматривал.

Чтобы сделать UML диаграммы компактнее, я выбросил второстепенные параметры из SQL запросов.

Алгоритм №1

Сперва, этот вариант показался мне лучшим решением, которое только возможно придумать. Сигналы с АЦП записываются в базу данных налету (порциями), за конкатенацию отвечает СУБД, следовательно, не придется туда-сюда их гонять. Но не всебыло так хорошо...

INSERT INTO adcdata (exp_id, channel, gain, adcdata)
VALUES (@exp_id, @channel, @gain, @adcdata)

UPDATE adcdata SET adcdata = CONCAT(adcdata, @adcdata)
WHERE exp_id = @exp_id AND channel = @channelSyhi-подсветка кода


Как оказалось, при выполнении конкатенации сервер сначала извлекает все содержимое поля adcdata в оперативную память, затем производит склейку и только потом записывает результат в поле.

Алгоритм №2

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

INSERT INTO temp (adcdata) VALUES (@adcdata)Syhi-подсветка кода
Работать это должно так:

Алгоритм №3

Как-то перечитывая документацию к MySQL я наткнулся на ключевое слово DELAYED. Его употребление вместе с INSERT дает возможность не дожидаться окончания выполнения запроса. Жаль, что для UPDATE его нельзя применить :) Я модифицировал алгоритм №2 так:

INSERT DELAYED INTO temp (adcdata) VALUES (@adcdata)Syhi-подсветка кода

Тест алгоритмов

Чтобы положить какие-то цифри в фундамент моих догадок относительно производительности этих алгоритмов, я написал небольшое тестовое приложение на C#. "Причем тут C#?", - спросите Вы. Все просто. Во-первых, мне были нужны относительные результаты. Во-вторых, на C# это было сделать просто. В-третьих, я даже не заморачивался насчет аппаратной конфигурации, так как мне нужены был качественные различия.

Результаты

Вот такие результаты я получил:

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

Алгоритм 2. Показал стабильное время вставки данных с небольшими случайными всплесками. 

Алгоритм 3. Как и ожидалось, это самый стабильный алгоритм.

К слову, об алгоритме 2 и 3. Тут нужно использовать хранимые процедуры, а это только MySQL 5.0 и выше.

P.S.: А как я все-таки реализовал запись сигналов в базу данных читайте в следующем посте про LabVIEW Database Connectivity Toolset.

Комментариев нет:

Отправить комментарий