Работа в многопользовательской среде на платформе 1С:Предприятие 7.7 с использованием формата DBF часто превращается в борьбу с блокировками. Особенно остро это проявляется в конфигурациях типа «Торговля и Склад» (ТиС) при количестве пользователей более 20–30. Основная проблема заключается в том, что движок базы данных блокирует таблицы целиком, а не построчно. В данной статье мы подробно разберем, как диагностировать причины блокировок и какие программные методы позволяют минимизировать простои системы.
Разберем, какие таблицы чаще всего становятся причиной остановки работы 40 пользователей. Проанализируем типичные ситуации:
Рассмотрим стандартное поведение: когда таблица заблокирована, другие пользователи ждут (по умолчанию до 30 секунд), и их интерфейс «замерзает». Мы рекомендуем установить время ожидания в 0. Это позволит системе мгновенно выводить сообщение о блокировке, не «подвешивая» сессию пользователя. Разберем код, который можно вставить в процедуру ПриНачалеРаботыСистемы, чтобы автоматически патчить файл конфигурации пользователя 1Cv7.CFG:
Функция УстановитьВ0ВремяОжидания()
ВремяОжиданияЗахватаБазы = Константа.ВремяОжиданияЗахватаБазы; // Предположим, там 0
ЛокВэйт = 0;
Попытка
МойТекст1С = СоздатьОбъект("Текст");
МойТекст1С.Открыть(КаталогПользователя() + "1Cv7.CFG");
Для НомерСтроки = 1 По МойТекст1С.КоличествоСтрок() Цикл
СтрокаТекста = МойТекст1С.ПолучитьСтроку(НомерСтроки);
Если Найти(СтрокаТекста, "LockWaitTime") > 0 Тогда
Если Найти(СтрокаТекста, Симв(34) + "LockWaitTime" + Симв(34) + "," + Симв(34) + "0" + Симв(34)) = 0 Тогда
МойТекст1С.ЗаменитьСтроку(НомерСтроки, "{" + Симв(34) + "LockWaitTime" + Симв(34) + "," + Симв(34) + "0" + Симв(34) + "}}");
ЛокВэйт = -1;
Прервать;
Иначе
ЛокВэйт = 1;
КонецЕсли;
КонецЕсли;
КонецЦикла;
Если ЛокВэйт = -1 Тогда
МойТекст1С.Записать(КаталогПользователя() + "1Cv7.CFG");
Предупреждение("Настройки обновлены. Пожалуйста, перезапустите 1С.", 5);
Возврат 0;
КонецЕсли;
Исключение
КонецПопытки;
Возврат 1;
КонецФункции
Важный момент: При установке ожидания в ноль критически важно использовать правильные библиотеки. Мы рекомендуем использовать Kernel33.dll (для решения проблемы файлов > 1 ГБ) или Kernel37.dll, которая также оптимизирует нагрузку на процессор при ожидании блокировок.
Если в вашей конфигурации одни документы создают другие (например, «Реализация» создает «Счет-фактуру»), это часто приводит к ошибкам захвата таблицы 1SJOURN. Рассмотрим ситуацию, когда мы пытаемся создать документ в цикле, делая паузы, чтобы «проскочить» между блокировками других пользователей. Для реализации паузы (Sleep) лучше использовать внешние компоненты, например 1C++ или FormEx.
Процедура СоздатьДокументСПовтором()
Сервис = СоздатьОбъект("Сервис"); // Объект FormEx или 1C++
Успешно = 0;
Для ПопыткаНомер = 1 По 20 Цикл
Попытка
Док = СоздатьОбъект("Документ.СчетФактура");
Док.Новый();
// Заполнение реквизитов...
Док.Записать();
Успешно = 1;
Прервать;
Исключение
// Если таблица занята, ждем 0.5 секунды и пробуем снова
Сервис.Sleep(500);
КонецПопытки;
КонецЦикла;
Если Успешно = 0 Тогда
Сообщить("Не удалось создать документ из-за длительных блокировок. Попробуйте позже.");
КонецЕсли;
КонецПроцедуры
Проанализируем интересную особенность 1С 7.7: открытие существующего документа обычным способом накладывает блокировку на его запись в журнале, даже если вы просто смотрите на него. Однако открытие через «Лупу» (только просмотр) блокировку не вызывает. Мы можем перехватить выбор строки в журнале и принудительно открывать документы в режиме только чтения для пользователей, которым не нужны правки.
Процедура ПриВыбореСтроки()
СтатусВозврата(0);
// Проверяем права или условия
Если (ТекущийДокумент.Вид() = "ПоступлениеТМЦ") И (НуженТолькоПросмотр = 1) Тогда
// Третий параметр "1" открывает форму в режиме только просмотра
ОткрытьФорму(ТекущийДокумент, , 1);
Иначе
ОткрытьФорму(ТекущийДокумент);
КонецЕсли;
КонецПроцедуры
Часто блокировки возникают, когда внутри транзакции проведения одного документа выполняются тяжелые действия с другими объектами. Рассмотрим пример: при проведении накладной нужно автоматически создать и напечатать Счет-Фактуру. Чтобы не удлинять транзакцию накладной, мы можем использовать генерацию внешнего события (требуется FormEx). Процесс будет выглядеть так: накладная проводится быстро, закрывает транзакцию, а затем система «сама себе» посылает команду создать СФ.
В модуле документа:
Процедура ОбработкаПроведения()
// Основная логика проведения
...
// Генерируем событие для асинхронного создания СФ
Сервис = СоздатьОбъект("Сервис");
Сервис.ВнешнееСобытие("ГенераторСФ", "Создать", ЗначениеВСтрокуВнутр(ТекущийДокумент()));
КонецПроцедуры
В глобальном модуле:
Процедура ОбработкаВнешнегоСобытия(Источник, Событие, Данные)
Если Источник = "ГенераторСФ" Тогда
ДокОснование = ЗначениеИзСтрокиВнутр(Данные);
// Здесь создаем СФ вне транзакции проведения накладной
глСоздатьСчетФактуру(ДокОснование);
КонецЕсли;
КонецПроцедуры
Выясним причину замедления проведения документов: если файлы регистров (особенно RA328.DBF — партии) приближаются к 2 ГБ, 1С начинает работать крайне нестабильно. Это критический порог для DBF.
Рассмотрим способы ускорения расчетов:
Заявки) не «висят» годами. Незакрытые регистры заставляют систему пересчитывать огромные объемы данных при каждом проведении.ТА (Точку Актуальности) из начала месяца может быть долгим. Переход на 5-дневную или 10-дневную периодичность может ускорить проведение, но увеличит размер файла итогов RG*. Будьте осторожны, чтобы не превысить лимит 2 ГБ.Для глубокого понимания ситуации проанализируем положения работы движка:
Новый() и Записать() всегда неявно открывают транзакцию.Подведем итог: чтобы победить блокировки на 40 пользователях в DBF, необходимо максимально сократить время нахождения системы внутри транзакции. Выносите расчеты цен, поиск партий и заполнение сложных структур данных до момента вызова Записать() или Провести(). Используйте кеширование констант и эмуляцию режима чтения для журналов. Это позволит вашей системе работать стабильно даже при высоких нагрузках.