Хранение файлов — как уменьшить размер базы данных





Хранение файлов в базе 1С можно оптимизировать для уменьшения размера хранимых данных.

Очень часто, в базах 1С организуют хранилище файлов, что удобно. Например, удобно хранить переписку по электронной почте. И вот, представьте, вы берете к-нить картинку и отправляете 8 своим коллегам, с точки зрения обычного хранения данных, в вашей БД, сохранится 9 копий данной картинки, а если ваши коллеги начнут отвечать на письмо, то и больше.

Вот так это выглядит;одинаковые файлы, записываются несколько раз:

 

Хранение файлов в базе 1С можно оптимизировать для уменьшения размера хранимых данных. В этом нам поможет теория, что ХЕШ-функции способны определить изменения даже одного бита в файле.

1С поддерживает следующие алгоритмы, но можно использовать и внешние вызовы, для получения ХЕШа:

  • CRC32 (CRC32)
  • MD5 (MD5)
  • SHA1 (SHA1)
  • SHA256 (SHA256)

Основная идея в том, что прежде чем записать в БД файл, проверить — существует ли точно такой же файл или нет? Если существует, то записывать его нет необходимости, и можно сохранить только необходимые реквизиты — имя файла, ссылка на объект и т.д.

Для ускорения перебора файлов, мы сначала ищем их по размеру (число) потом уже по ХЕШу. Для чего у нас есть регистр:

 

Если файл не найден, мы его записываем в хранилище значений, если найден, то записываем ссылку на него, уже в другом регистре:

 

 

Я провел эксперимент, взял БД с обычным хранением файлов и БД с переделанным под описанный выше алгоритм хранением. После чего, в 2 БД были загружены письма за 1 год. Результат — база данных стала более чем в 3 раза меньше:

 

 

Для "усиления" надежности, можно использовать несколько ХЕШ-функций. Падения быстродействия при загрузке почты, я не заметил.

Немного кода:

&НаСервере
Функция ПолучитьРазмерФайлаИХешМД5(_ХранилищеЗначения) Экспорт
Структура = новый Структура;

ДД = _ХранилищеЗначения.Получить();
РазмерФайла = ДД.Размер();

Хэш = Новый ХешированиеДанных(ХешФункция.MD5);
Хэш.Добавить(ДД);
МД5Двоичный = Хэш.ХешСумма;
Результат = ПолучитьHexСтрокуИзДвоичныхДанных(МД5Двоичный);
ХешМД5 = Результат;

Структура.Вставить("РазмерФайла", РазмерФайла);
Структура.Вставить("ХешМД5",   ХешМД5);

Возврат Структура;
КонецФункции

&НаСервере
Процедура ЗаполнитьВложения(_Файлы, ИмяВложения, ИнтернетПочтовоеСообщение, _Владелец) Экспорт

Для Каждого ИнтернетПочтовоеВложение Из ИнтернетПочтовоеСообщение.Вложения Цикл

ИмяВложения = ИнтернетПочтовоеВложение.Имя;

Если ТипЗнч(ИнтернетПочтовоеВложение.Данные) = Тип("ДвоичныеДанные") Тогда

СтруктураФайла = Новый Структура;

СтруктураФайла.Вставить("ИмяФайла",    ИмяВложения);
СтруктураФайла.Вставить("Идентификатор",  ИнтернетПочтовоеВложение.Идентификатор);
СтруктураФайла.Вставить("ПутьКФайлу",   "");
СтруктураФайла.Вставить("ХранилищеЗначения" , Новый ХранилищеЗначения(ИнтернетПочтовоеВложение.Данные, Новый СжатиеДанных()));
СтруктураФайла.Вставить("СпособКодирования", Строка(ИнтернетПочтовоеВложение.СпособКодирования));
СтруктураФайла.Вставить("ТипСодержимого",  ИнтернетПочтовоеВложение.ТипСодержимого);

Файл = СоздатьФайл(_Владелец, СтруктураФайла);

НовоеФайлы    = _Файлы.Добавить();
НовоеФайлы.Файл  = Файл.Ссылка;
Иначе
ЗаполнитьВложения(_Файлы, ИмяВложения, ИнтернетПочтовоеВложение.Данные, _Владелец);
КонецЕсли;
КонецЦикла;

КонецПроцедуры

&НаСервере
Функция СоздатьФайл(_Владелец, _СтруктураФайла) Экспорт

ИмяФайла   = _СтруктураФайла.ИмяФайла;
Идентификатор  = _СтруктураФайла.Идентификатор;
ПутьКФайлу   = _СтруктураФайла.ПутьКФайлу;
ХранилищеЗначения = _СтруктураФайла.ХранилищеЗначения;
СпособКодирования = _СтруктураФайла.СпособКодирования;
ТипСодержимого  = _СтруктураФайла.ТипСодержимого;

СтруктураФайла  = ПолучитьРазмерФайлаИХешМД5(ХранилищеЗначения);
ФайлСсылка   = НайтиФайлПоРазмеруФайлаИХешуМД5(СтруктураФайла.РазмерФайла, СтруктураФайла.ХешМД5);

Если ФайлСсылка = неопределено Тогда

Файл = Справочники.Файлы.СоздатьЭлемент();
Файл.Наименование   = ИмяФайла;
Файл.ДанныеФайла    = ХранилищеЗначения;

Файл.Записать();

ФайлСсылка = Файл.Ссылка;
КонецЕсли;

Регистр = РегистрыСведений.Регистр_ВладельцыФайлов.СоздатьНаборЗаписей();
Регистр.Отбор.Источник.Установить(ФайлСсылка);
Регистр.Отбор.ВладелецФайла.Установить(_Владелец);

Регистр.Прочитать();

Если Регистр.Количество() = 0 Тогда
ЗаписьРегистра = Регистр.Добавить();

ЗаписьРегистра.Источник   = ФайлСсылка;
ЗаписьРегистра.ВладелецФайла = _Владелец;

ИначеЕсли Регистр.Количество() > 1 Тогда
ВызватьИсключение "В регистре Регистр_ВладельцыФайлов для (" + Строка(ФайлСсылка) + ") найдено несколько одинаковых значений. Сообщите разработчику. Сделайте скриншот вводимых данных.";
Иначе
ЗаписьРегистра = Регистр[0];
КонецЕсли;

ЗаписьРегистра.ИмяФайла    = ИмяФайла;
ЗаписьРегистра.Идентификатор  = Идентификатор;
ЗаписьРегистра.ПутьКФайлу   = ПутьКФайлу;
ЗаписьРегистра.СпособКодирования = СпособКодирования;
ЗаписьРегистра.ТипСодержимого  = ТипСодержимого;

Регистр.Записать();

Возврат ФайлСсылка;

КонецФункции


 

17 Comments

  1. stepan_s

    Такой подход допустим в случае хранения файлов в едином месте хранения (справочник, регистр сведений), но существуют реализации когда хранилище с файлом в документах, или прочих уникальных метаданных. Как быть в таком случае? Есть ли варианты?

    И мягко сказать не мало работы для перевода логики хранения…

    Ошибаюсь?

    Reply
  2. 2tvad

    (1) Думаю, не принципиально где вы храните файлы. Потому что, придется переделывать их отображение и получение на основании регистра (в моем случае Регистр_ВладельцыФайлов). Измерение Источник получит «Составной тип данных» на все возможные хранилища файлов (если нельзя сослаться прямо, то будут некие ключи для ссылок).

    Для перевода логики на формах, можно разработать общую форму для работы с файлами.

    Я планирую переделывать такой механизм у одного своего Заказчика, по факту отпишусь что вышло по объему базы и сколько это заняло в чел.часах.

    Reply
  3. МимохожийОднако

    В порядке бредовой идеи.

    Добавить в типовой регистр с картинками реквизит ХэшМд5. Добавить регистр с измерением ХэшМд5 и хранилищем файла. При записи сначала сохранять в добавленный регистр ХэшМд5 и картинку (Хранилище значений), а после этого записывать ХэшМд5 в добавленный реквизит в типовом регистре.

    Для тех записей типового регистра, в которых еще не заполнен добавленный реквизит запустить фоновое задание, которое перезаписывает типовой регистр с очисткой ранее записанных картинок из ресурса хранилища значений

    Reply
  4. Diversus

    (0) Что будет когда есть файл, который используется, например, в двух документах (файлы одинаковые) тех же электронные письма входящие и мы пометим на удаление одно из входящих писем и удалим?

    Ваша доработка на это никак не отреагирует и будет удалено вложение, которое используется в другом документе.

    Нужен дополнительный анализ удалять или не удалять файл-вложение иначе это может быть проблемой.

    Reply
  5. 2tvad

    (4) Будет удалена запись в регистре «Регистр_ВладельцыФайлов».

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

    Reply
  6. Diversus

    (5) Если мы говорим о стандартном функционале, то из справочника «ЭлектронноеПисьмоВходящееПрисоединенныеФайлы» файл будет удален при удалении объекта владельца подпиской на событие «ВыполнитьДействияПередУдалениемПрисоединенногоФайла». Так удалится сам файл.

    Если я все правильно понял, вы создаете новый файл только тогда, когда нет уникальной записи с такой MD5, а если нашли,то просто указываете, что файл находится «вот здесь» остальные механизмы вы не трогали. Соответственно при удалении любого владельца файла стандартная БСП удалит исходный файл, а все остальные владельцы, которые ссылаются на этот же файл останутся без этого файла.

    Reply
  7. 2tvad

    (6) Про стандартный функционал — вы абсолютно правы. Что БСП, что функционал 1С в прочих конфах заточен на работу со справочниками, где хранятся файлы. Все эти вызовы придется переделать на работу с регистром.

    Reply
  8. user1274438

    Все не читал. Штатную возможность хранения в томах уже предлагали?

    Reply
  9. 2tvad

    В описанной ситуации, если у вас в томах — 6,5 Гб файлов, будет 1,8 Гбайт.

    Reply
  10. user1274438

    (9) Дык, елы палы — даже 6.5 Гб, хранящихся в томах, уменьшают размер базы данных на 6.5 Гб

    Что же касается нестыковок, типа (4) и (6) — то наложите на это еще, например, РИБ

    Дальше даже думать не хочется, как можно потом разобраться — «куда потерялся мой файлик!!!!111». Особенно — если это не электропочта, а например, документооборот, а сами документы еще и электроподписями подписываются.

    Reply
  11. 2tvad

    (10) согласен. Но можно уменьшить 6,5 Гбайт в томах до 1,8 Гбайт. Что тоже хорошо с точки зрения скорости резервирования.

    Reply
  12. triviumfan

    Интересное решение, но нужно ли?!

    Reply
  13. Светлый ум

    +1 Взял на вооружение

    Reply
  14. azubar

    Как по мне, почту тянуть в 1С это зло, особенно учитывая реализацию почтового клиента 1С.

    По поводу оптимизации хранения вложений также реализовал подобный механизм: отдельный справочник для хранения вложений, стандартный функционал работы был переписан так чтобы при изменении файлов система проверяла есть ли в хранилище файл с таким хешем, если нет то создавала, а в стандартном справочнике хранилась только ссылка на дополнительный справочник, при этом обновление / удаление файла не затрагивало сам файл и остальные элементы ссылающиеся на него. Регламентное задание периодически проверяло и очищало (архивировало) файлы на которые не ссылается ни один элемент из стандартного справочника. Такой подход очень сильно экономит пространство (независимо от варианта хранения в базе или в томах) и непонятно почему он до сих пор не реализован в типовых.

    Reply
  15. EVKash

    (14) в типовых и без этого косяков предостаточно…

    в 1С руководствуются идеей, что компьютеры становятся все мощнее и надо всю эту мощь использовать. оптимизация? не… не слышали.

    Reply
  16. 2tvad

    (15) Согласен. Стоит допилить, например, механизм регистров и не записывать физически данные, если нет в них изменений. Это сколько ресурсов сэкономит.

    Reply
  17. 2tvad

    (14) почту тянут для разных целей. Например, что бы переписка с заказчиком по контрактам была видна. Плюс это дисциплинирует продажников. Одно дело обсуждать в своей почте, другое, когда это видно сразу же нач.отдела. И не «Петров, а что у тебя там с контрактом», а «Петров, у тебя контракт подвис на согласовании сроков поставки, ты когда график вышлешь».

    Есть у меня еще интересный механизм — архивирование файлов. Это когда старые файлы — более одного года давности, раз в год, переносятся из базы на диск. Фитча в том, что их можно не бекапить постоянно. Т.к. год этот архив не меняется.

    И с одной стороны база не раздувается, а с другой не нужно файлы отдельно постоянно бекапить, боясь потерять ссылки.

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *