Работа с BLOBами

Практика работы с BLOB: не все бинарные данные одинаково полезны
BLOB (Binary Large Object) — механизм хранения бинарных данных в SQL-базах. В Delphi работа с ними идёт через TBlobField и методы LoadFromStream/SaveToStream. Ошибка №1 — думать, что BLOB подходит для любых файлов. На практике выбор между BLOB и файловой системой — вопрос производительности и архитектуры.
Разберём три реальных сценария, для которых я подбирал решение под конкретную задачу в 2023-2025 годах.
Сценарий 1: Много мелких файлов (до 200 КБ) — XPS-документы
Заказчик хранил 150 000 XPS-файлов в папке на NAS. Проблема: резервное копирование занимало 14 часов. Переход на SQLite с BLOB (без сжатия) уменьшил время бэкапа до 2 часов.
- БД: SQLite 3.45, поле
BLOB, движок WAL. - Выбор: размер файла до 200 КБ — именно для этого диапазона BLOB даёт выигрыш по IOPS.
- Типичная ошибка: не проверили размер кэша SQLite. Приставил
PRAGMA cache_size=-8000(8 МБ на сессию). - Результат: скорость записи — 12 МБ/с, чтения — 100+ МБ/с (пока в кэше).
Сценарий 2: Изображения 2-5 МБ — PostgreSQL + TStream
Интернет-магазин: 40 000 фото товаров. Поле BYTEA (PostgreSQL). Ошибка: все фото хранили одной таблицей. Пришлось сделать отдельную таблицу product_images и подключать LEFT JOIN — иначе сканирование строк тормозило вывод каталога.
- Шаг выбора: размер файла >1 МБ — файловая система лучше для частого чтения. Но клиент настоял на единой БД.
- Оптимизация: сжатие WebP (снизили размеры с 4.5 МБ до 1.2 МБ) перед записью в BLOB.
- Код загрузки на Delphi:
TMemoryStream -> TBlobField(FieldByName('image')).LoadFromStream. - Цифра: время выборки 100 записей с BLOB — 1.2 сек (без индексации). С индексом по ID — 0.3 сек.
Сценарий 3: Видеофрагменты (50-100 МБ) — BLOB убивает производительность
В проекте системы видеонаблюдения (Delphi 12, FireDAC) инженеры положили 30-секундные ролики в BLOB MS SQL Server. Результат: размер БД 200 ГБ, страницы памяти переполнены. Пришлось мигрировать на файловую систему, а в БД хранить только путь.
- Критерий для выбора BLOB: не более 5% от общего объёма данных. Если файлы больше и их много — храните вне СУБД.
- Шаг принятия решения: тест скорости. Для 500 видео по 70 МБ: запись в BLOB — 14 МБ/с, чтение — 8 МБ/с (с фрагментацией). Для NTFS: запись — 35 МБ/с, чтение — 120 МБ/с.
Типичные ошибки покупателей (разработчиков) при выборе стратегии
- Путают OLTP и архивирование. BLOB для транзакционной нагрузки — частая проблема. На приёме: “а почему база тормозит?” — потому что запрос SELECT * с BLOB полем выгружает мегабайты при выборе пары строк.
- Игнорируют резервное копирование. Маленькое изменение в файле — и полный дамп, если БД одна. Всегда храните BLOB отдельной файловой группой или таблицей.
- Забывают про потоковую запись. Код вида
FieldByName('data').AsString := FileToBase64— кошмар. ИспользуйтеLoadFromStream. - Не проверяют встроенные средства СУБД — например,
FILESTREAMв SQL Server илиloв PostgreSQL. Они работают быстрее обычного BLOB.
Пошаговый шаблон для принятия решения
- Шаг 1. Размер файла: до 200 КБ → BLOB в связке с кэшем. 200 КБ — 5 МБ → BLOB со сжатием. >5 МБ → только файловая система + путь в БД.
- Шаг 2. Частота чтения: ежедневно >100 раз на файл → подумать про отдельный кэш (Redis / MemoryStream).
- Шаг 3. Транзакционность: нужна атомарность между данными и файлом? Тогда
FILESTREAMили хранить в БД. - Шаг 4. Тест с типовой нагрузкой: сгенерировать 1000 записей с размером 80% от макс. размера. Замерить время SELECT + скачивания.
В Delphi для типовых действий с BLOB используйте TFDQuery или TADOQuery. Пример записи:
var MemStream: TMemoryStream;MemStream := TMemoryStream.Create;tryMemStream.LoadFromFile('foto.jpg');with FDQuery1 do begin SQL.Text := 'UPDATE products SET image = :blb WHERE id = 42'; Params.ParamByName('blb').LoadFromStream(MemStream, ftBlob); ExecSQL;end;finally MemStream.Free; end;
Избегайте ParamByName('blb').SetBlobData — в FireDAC он допускает утечку памяти на версиях до 2024.
Добавлено: 27.04.2026
