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

Публикация № 1119859

Администрирование - Чистка базы

ХешФункция MD5 ХранилищеЗначения Файлы

9
Хранение файлов в базе 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];
	КонецЕсли;
	
	ЗаписьРегистра.ИмяФайла				= ИмяФайла;
	ЗаписьРегистра.Идентификатор		= Идентификатор;
	ЗаписьРегистра.ПутьКФайлу			= ПутьКФайлу;
	ЗаписьРегистра.СпособКодирования	= СпособКодирования;
	ЗаписьРегистра.ТипСодержимого		= ТипСодержимого;	
	
	Регистр.Записать();			
	
	Возврат ФайлСсылка; 
	
КонецФункции


 

9

См. также

Специальные предложения

Комментарии
Избранное Подписка Сортировка: Древо
1. stepan_s 11.09.19 03:48 Сейчас в теме
Такой подход допустим в случае хранения файлов в едином месте хранения (справочник, регистр сведений), но существуют реализации когда хранилище с файлом в документах, или прочих уникальных метаданных. Как быть в таком случае? Есть ли варианты?
И мягко сказать не мало работы для перевода логики хранения...
Ошибаюсь?
2. 2tvad 38 11.09.19 09:27 Сейчас в теме
(1) Думаю, не принципиально где вы храните файлы. Потому что, придется переделывать их отображение и получение на основании регистра (в моем случае Регистр_ВладельцыФайлов). Измерение Источник получит "Составной тип данных" на все возможные хранилища файлов (если нельзя сослаться прямо, то будут некие ключи для ссылок).

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

Я планирую переделывать такой механизм у одного своего Заказчика, по факту отпишусь что вышло по объему базы и сколько это заняло в чел.часах.
stepan_s; +1 Ответить
3. МимохожийОднако 127 15.09.19 08:05 Сейчас в теме
В порядке бредовой идеи.
Добавить в типовой регистр с картинками реквизит ХэшМд5. Добавить регистр с измерением ХэшМд5 и хранилищем файла. При записи сначала сохранять в добавленный регистр ХэшМд5 и картинку (Хранилище значений), а после этого записывать ХэшМд5 в добавленный реквизит в типовом регистре.

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