Почему BLOB-поле - недополе, и как его сделать полем
Сегодня все больше и больше встречается мультимедийных данных - картинок, мелодий, песен, фильмов. Современная тенденция - хранить их в базе данных, а не в файловой системе. Научные графические данные попросту неудобно хранить вне базы данных, т.к. они сопровождаются большим количеством дополнительных цифровых данных. Две наиболее типичные операции с BLOB-ами - это:
- скачивание (с сервера, в т.ч. с СУБД)
- получение (от клиента, который отправляет его по собственной инициативе)
Перечислим, какие задачи они порождают, и предложим, как их решить.
Скачивание
Соединения иногда разрываются. Кроме того, может понадобится перезагрузить компьютер-получатель, или выключить ноутбук-получатель. Таким образом в поле нужно хратить:
- внешний адрес BLOB-а для продолжения скачивания
- уже скачанную часть BLOB - и даже части, если СУБД может разделять удаленный BLOB на несколько частей (подобно обычным программам для скачивания, например "ReGet") и скачивать каждую часть через отдельное TCP-соединение
- смещения начала и конца каждой части для продолжения скачивания и сборки в единый BLOB
И нам нужны операторы:
- возвращающий BLOB
- возвращающий процент завершенности скачивания
- возвращающий адрес BLOB-а на удаленном сервере (вместо NULL-а чтобы уведомить запрашивающего BLOB, что скачивание еще не закончено, и уведомить, где можно получить BLOB до завершения скачивания или в случае, если СУБД будет разрушена)
- запускающий скачивание в фоновом режиме (т.е. разрешающий выполнение следующих SQL-операторов в процессе скачивания), и сообщающий, запущено скачивание или нет
Получение
Мы не можем продолжить получение после разрыва TCP-соединения, после перезагрузки или выключения питания - мы можем только надеяться, что соединение не будет разорвано, компьютер не повиснет, и что источник бесперебойного питания дотянет до конца передачи. Но если кто-нибудь попросит СУБД выдать этот BLOB в процессе получения, что СУБД должна ответить ему? Отвечая NULL, она сообщит, что ничего не знает о BLOB-е, что является ложью, т.к. получение BLOB-а - это только вопрос времени. Таким образом мы видим, что поле должно хранить:
- уже полученную часть BLOB
- значение "ACCEPTING", подобное NULL, но не равное ему
Пусть третья база данных копирует BLOB-поле в себя в тот момент, когда наша база данных его еще получает. Может ли третья база данных содержать значение "ACCEPTING"? Очевидно нет, т.к. это выглядело бы так, как будто третья база данных сама получает BLOB. Таким образом третья база данных дезинформировала бы своих пользователей, и внушила бы им ложные надежды. Итак, нам нужно еще одно значение, подобное NULL, например
в которое превращается "ACCEPTING" в процессе копирования. NOTGOT превращается в NOTGOT при копировании.
Оба случая
Следует заметить, что BLOB - это часто картинка, которая должна быть продемонстрирована в клиенте (например, в браузере). И было бы очень неудобно, если бы адрес содержал имя BLOB-поля (чтобы отличить его от других BLOB-полей), имя поля первичного ключа (чтобы указать запись), и имя таблицы
[1]. И как записать такой адрес? Это будет совершенно не похоже на URL, кроме того, клиент совершенно не обязан знать SQL. Таким образом мы видим, что
- идентификатор, уникальный во всей базе данных
должен быть помещен в BLOB-поле сразу, как только BLOB будет скачал или получен полностью. Помещение должно выполняться СУБД автоматически, без участия программиста
[2].
Вообще же BLOB - это файл, и как всякий файл он имеет тип - jpg, mpg, и т.д. Файлы одних форматов содержат указание типа в самом начале файла, файлы других форматов - не содержат, и в этом случае тип должен хранится в BLOB-поле и копироваться из поля в поле при копировании BLOB-а
[3].
Резюме
Давайте подытожим: BLOB-поле должно быть способно содержать один из следующих
наборов значений:
or
- значение ACCEPTING
- тип файла
- одну часть (начало) BLOB-а
or
or
- идентификатор, уникальный во всей базе данных
- тип файла
- целый BLOB
or
- URL [4]
- тип файла
- несколько частей BLOB-а
- смещения начала и конца каждой части BLOB-а
Операторы, умомянутые выше, будут выглядеть так:
- "SELECT fieldname ..." - для извлечения BLOB-а
[5]
- "SELECT ID(fieldname) ..." - для извлечения идентификатора BLOB
- "... WHERE fieldname=542" - для указания записи по идентификатору BLOB, равному 542
- "SELECT PERCENT(fieldname) ..." - для извлечения процента завершенности скачивания
- "SELECT TYPE(fieldname) ..." - для типа BLOB-а
[5]
- "SELECT DOWNLOAD(fieldname) ..." - для запуска скачивания в фоновом режиме и для информирования, запущено скачивание или нет (т.е. возвращает Y/N)
- "SELECT STOP(fieldname) FROM * WHERE fieldname=542" - для остановки скачивания BLOB-а с идентификатором, равным 542
[1] Предполагаем, что имя схемы объединено вместе с именем таблицы в один токин
[2] Было бы очень глупо, если бы программист был должен создавать последовательность для BLOB-ов, и создавать по триггеру для каждого BLOB-поля в базе данных
[3] Хорошо, если бы СУБД понимала большое количество форматов и исправляла бы тип файла в BLOB-поле на значение из содержимого файла
[4] Вида "site.com/~dbowner/dbname/identifier" или "111.222.333.444/~dbowner/dbname/identifier"
[5] Знать тип BLOB-а нужно только чтобы отобрать BLOB-ы для конвертирования из одного формата в другой.
Тип BLOB-а должен извлекаться автоматически вместе с самим BLOB-ом при его пересылке,
таким образом излишне запрашивать тип BLOB-а отдельно.
Кроме того, BLOB-ы должны передаваться не в самой записи (record), а после всех записей.
Иначе они будут блокировать реакцию получателя на быстро передаваемые не-BLOB поля.
<tablename fieldname1= fieldname="3652435"/>
<?file value="3652435" type="mpg" size="3">Y29</?file>
P.S.
Статья является руководством по реализации идей
с.167-175
pdf-документа.
Кроме того, для принудительного построения пиринговых сетей (peer-to-peer network)
типа BitTorrent, Kademlia и пр.
могут быть добавлены команды выяснения количества скачиваемых частей и их начал и концов
select reckon( @field ) from ... ; -- returns quantity of blob parts
select beginning( @field, 5) from ... ; -- beginnings of 5th part
select end( @field, 5) from ... ; -- ends of 5th part
Тюрин Дмитрий, dmitryturin@yandex.ru
Используются технологии
uCoz