Новогодняя ночь несколько лет назад. Пять организаций надо было слить в одну, база примерно 550 гигабайт, окно на всю работу трое суток. Перезапись ссылок планировалась ровно на эти трое суток, потому что документов там было очень много, а другого времени, когда база стоит целиком, в году не случается.
Один раз процесс повис. Понял я это не по логам и не по монитору: заранее прикрутил телеграм-уведомления, и в какой-то момент цифры в них перестали меняться. Крутилка в такой ситуации не помогает вообще, она вертится одинаково и когда работа идёт, и когда всё встало.
Закончилось хорошо, к утру люди сели работать. Но на входе я не знал двух вещей: сколько это займёт и что развалится, если я где-то промахнусь. Обе пришлось выяснять уже внутри окна, когда откатываться некуда.
Дальше про то, как эти два вопроса считаются, и про шесть боевых баз, на которых мой механизм трижды оказался неправ. Числа все свои, снятые за один день.
Рынок отвечает "не знаю", и отвечает честно
На полке чистки данных сейчас восемь заметных инструментов: менеджер очистки битых ссылок, убийца зависимостей, удаление помеченных с отбором, менеджер дублей, две универсальные очистки, центр администрирования, поиск битых ссылок в большой базе. Все они удаляют, каждый закрывает свою категорию мусора и закрывает хорошо.
Интересное лежит в комментариях под ними.
В январе под универсальной очисткой справочников человек спросил напрямую: сколько времени займёт удаление организации, если учёт вели пять лет по десять тысяч документов в месяц. Автор ответил, что всё зависит от инфраструктуры и оценить это невозможно.
В мае там же другой человек описал справочник штрихкодов, где записей огромное количество и все они ссылочные на чеки, и спросил, критично ли такое удаление без контроля ссылок. Ответ был: по вашей базе решение можете принять только вы.
Оба ответа честные. Автор не отмахивается, он говорит правду про свой инструмент: инструмент удаляет, а прогнозировать и заглядывать вперёд он не умеет.
Рядом, под очисткой документов, ещё одна показательная история: человек надстроил над чужой обработкой собственный автопилот и чистил базу за десять лет сутки подряд. И четвёртый сигнал, из описания совсем другой обработки: типовое "Удаление помеченных на удаление" перестаёт открываться, когда помеченных больше миллиона. То есть на базах, где вопрос про сроки встаёт по-настоящему, штатное средство диагностики просто отсутствует.
Что надо посчитать перед уборкой
Прежде чем разбирать механику, перечислю поимённо, из чего складывается ответ. Десять разделов, и каждый существует потому, что без него план уборки получается неполным.
- Экспресс-ответ. Три числа: сколько можно убрать точно, сколько помечено к разбору, сколько хранится и на учёт не влияет. Разница между первым и вторым принципиальная, ниже про неё отдельно.
- С чего начинать. Какой рычаг больше: чистка помеченных или свёртка периода. На боевой бухгалтерии это 209 858 помеченных против 53 078 документов старше трёх лет, и вывод очевиден. На соседней базе бывает наоборот.
- Помеченные на удаление. Сколько и в каких объектах, с долей каждой группы от общего числа. Верхние пять групп обычно дают больше половины.
- Сколько это займёт. Замер скорости ссылочного контроля на этой базе и пересчёт на весь объём.
- Что сломается. По группе мусора: в каком поле какой таблицы лежат ссылки и сколько их.
- Файлы и версии. Точный вес присоединённых файлов, файлы без владельца, объём истории версий.
- Старые периоды. Документов старше N лет по видам, с раскладкой по годам.
- Дубли. Группы одинаковых наименований по справочникам и сколько записей в них лишние.
- Битые ссылки. Указатели на объекты, которых в базе нет, поимённо по полям.
- Границы. Сколько базы просмотрено и чего инструмент не делает. Раздел, который обычно пропускают, хотя он важнее половины остальных.
Дальше по находкам. Их шесть, и три из них про мои собственные ошибки.
Находка первая: штатный поиск не виснет, он идёт семьдесят одну минуту
Мне сказали, что НайтиПоСсылкам на больших объёмах виснет. Я прогнал его через свой канал к базе, получил пять таймаутов подряд и объявил механизм непригодным. Это был вывод из числа без проверки условий: потолка самого канала я не знал. Пять одинаковых отказов выглядели как доказательство.
Потом померил честно, без ограничения по времени, на одних и тех же данных. База УПП, группа помеченных документов, 200 ссылок.
штатный НайтиПоСсылкам, область 6 объектов 4 250,8 с (71 минута) наш адресный запрос по полю, та же область до 450 с наш подход с бюджетом фазы 30 с
Он не виснет. Он честно работает семьдесят одну минуту на шести объектах. Отсюда и ощущение зависания у всех, кто его запускал: живой процесс, который час не отвечает, от мёртвого визуально не отличается ничем.
Границу утверждения назову сразу: мерено через HTTP-канал к базе, в двух режимах, с откатом транзакции и без него. В интерактивном клиенте поведение может отличаться.
Находка вторая: цену задаёт число мест, а число объектов ни при чём
В голове у всех одна и та же модель: сколько объектов, столько и работы. Помечено три миллиона, значит будет втрое дольше, чем на миллионе. Модель интуитивная, она подпирается всем опытом с циклами, и она же делает прогноз бессмысленным: три миллиона умножить на неизвестно сколько равно неизвестно сколько.
Я начал ровно с этой модели. Формула была такая: время на один объект умножить на число помеченных. Замерил цену одного, умножил, вписал в отчёт.
Живая база формулу убила. 391 документ, 426 справочников в конфигурации:
обход метаданных "кто может держать этот тип" 0,19 с найдено полей, способных держать тип 275 осталось мест к проверке 39 адресных запросов сделано 25 время поиска 22,77 с (~0,9 с на запрос)
Двадцать пять запросов дали 22,77 секунды. Дальше я менял число самих ссылок: пятьдесят, триста, три тысячи. Время не менялось.
Причина видна прямо в тексте запроса. Проверка ссылок это скан таблицы по условию. Обхода объектов там нет вовсе. Список в В (&Ссылки) от количества ссылок растёт, а скан таблицы от него не зависит: таблица какая была, такая и осталась. Цену определяет число проверяемых мест.
Моя формула завышала срок в разы. И увидеть это без живой базы было нельзя: на бумаге она выглядит абсолютно разумной.
Зато из неё следует хорошая новость. Если цена определяется числом мест, а места это метаданные, прогноз перестаёт быть гаданием. Места считаются заранее, за доли секунды, вообще без обращения к данным:
// Ищем МЕСТА, где ссылка может лежать. // Обращений к данным здесь ноль. Для Каждого Реквизит Из Объект.Реквизиты Цикл Типы = Реквизит.Тип.Типы(); Если Типы.Количество() > Порог Тогда // Поле принимает почти всё подряд: версионирование, наборы // доступа, заметки. Оно связывает всё со всем. Продолжить; КонецЕсли; Если Типы.Найти(ИскомыйТип) <> Неопределено Тогда Места.Добавить(Объект.ПолноеИмя() + "|" + Реквизит.Имя); КонецЕсли; КонецЦикла;
Побочная выгода вылезла сама. Обход метаданных отвечает на вопрос, на который штатный поиск не отвечает в принципе: он видит только сегодняшние ссылки, а метаданные показывают весь радиус, включая места, где ссылок сегодня нет, но завтра они появятся. Для планирования это важнее фактического числа.
Находка третья: число типов оказалось неверным признаком
Обратите внимание на Порог в середине куска выше. Я поставил его равным восьми: поле, принимающее больше восьми ссылочных типов, считал шумом. Основание было. На УТ таких полей 236 из 275, на рознице один реквизит держал 449 типов при 918 объектах конфигурации. Половина конфигурации в одном поле.
Потом инструмент прогнали за один день на шести боевых базах: две бухгалтерии, две ERP, розница, торговля. И порог развалился.
На бухгалтерии он отсекал 632 места из 641, то есть 98,6 процента. После этого отчёт писал "ссылок не найдено", а читается это как "удалять безопасно". Вот что он выбрасывал:
Справочник.БанковскиеСчета.СубконтоЗатратБУ1 67 типов Справочник.ДоговорыКонтрагентов.Субконто1 90 Справочник.КлючиАналитикиУчетаЗатрат.Субконто1 90 Справочник.РасходыБудущихПериодов.СубконтоЗатрат1 90
Субконто. То, через что в бухгалтерии ходит вся аналитика. Механизм, который должен отвечать "кто держит этот объект", молча выбрасывал главного держателя и отчитывался нулём.
А вот что на тех же базах оказалось настоящим шумом:
Справочник.ВидыПроверок.Свойство1 840 типов Справочник.ИдентификаторыОбъектовМетаданных.ЗначениеПустойСсылки 422 Справочник.Заметки.Предмет 387
Разрыв между двумя классами четырёхкратный, и он держится на всех шести базах. Значит признак есть, но он относительный: шумовое поле принимает заметную долю ВСЕХ ссылочных объектов конфигурации, а рабочее принимает десяток процентов. Абсолютное число типов не говорит ни о чём: на маленькой конфигурации сорок типов это половина мира, на большой это узкое поле.
Порог стал третью от числа ссылочных объектов конфигурации, не меньше восьми. На бухгалтерии это 134 вместо 8, и субконто проходит, а свойства объектов отсекаются.
Мораль тут не про порог. Я держал в комментарии рядом с этим кодом фразу "на документы ссылаются через универсальные поля, а типизованную ссылку чаще держит справочник" и не сделал её правилом. Пояснение, которое не стало кодом, не работает.
Находка четвёртая: 216 704 ссылки, которых не видел никто
После правки порога тот же прогон на тех же базах:
было мест стало было ссылок стало
ERP 49 из 418 49 0 216 704
торговля 392 из 406 35 197 369
ERP вторая 50 из 234 50 0 754
розница 84 из 264 55 0 188
Двести шестнадцать тысяч ссылок на ERP лежали там всегда. Инструмент их просто не видел и честно печатал ноль.
Ответ на вопрос "что сломается" выглядит теперь так. Ситуация: на боевой бухгалтерии помечено на удаление 37 796 договоров контрагентов, и вопрос ровно один - что держит их от удаления.
объект и поле, где лежит ссылка ссылок как чинить РегистрНакопления.РеализацияТМЗ.ДоговорКонтрагента 1 946 перепроведение Справочник.Контрагенты.ОсновнойДоговорКонтрагента 1 477 правка поля Документ.ЭСФ.ДоговорПоставки 429 правка поля Документ.СчетФактураВыданный.ДоговорКонтрагента 381 правка поля Документ.РеализацияТоваровУслуг.ДоговорКонтрагента 320 правка поля
Первая строка читается так: в движениях регистра РеализацияТМЗ, в поле ДоговорКонтрагента, лежит 1 946 ссылок на те самые помеченные договоры. Пока они там лежат, договор не удалится, и типовое удаление помеченных на нём споткнётся.
Правая колонка появилась последней и оказалась важнее, чем я думал. Ссылка в реквизите документа правится полем. Ссылка в движениях регистра руками не правится вовсе: её переписывает только перепроведение документа-регистратора. Это разные работы, разные окна и разная цена, а до этого обе строки выглядели в отчёте одинаково.
Различить их можно по метаданным, не трогая данные: у регистра сведений смотрится режим записи, а регистры накопления, бухгалтерии и расчёта подчинены регистратору всегда.
Находка пятая: миллион триста битых ссылок при трети проверенных полей
Битая ссылка это указатель на объект, которого в базе нет: разыменование через точку даёт NULL. Платформа показывает такое поле как "Объект не найден", отчёты по нему врут, проведение падает.
ВЫБРАТЬ КОЛИЧЕСТВО(*) КАК Штук ИЗ Документ.РеализацияТоваровУслуг КАК Пр ГДЕ Пр.Контрагент <> НЕОПРЕДЕЛЕНО И Пр.Контрагент.Ссылка ЕСТЬ NULL
На боевой бухгалтерии таких нашлось 1 350 560. И тут же вторая моя ошибка, которую поймали те же шесть отчётов: во всех шести стояло "проверено полей 40 из 40, сто процентов". Сорок это был мой собственный предел. Число полей в конфигурации туда не входило совсем. Одинаковая сотня процентов на шести разных базах и выдала подделку.
Настоящий знаменатель на той базе 1 969 полей. Сейчас в отчёте написано "проверено 600 из 1 969, тридцать процентов", и вердикт "битых не найдено" прямо говорит, что это "нет среди проверенных", а не "нет в базе".
Правило отсюда общее и скучное: у каждой проверки должен стоять знаменатель. Отрицательный результат одинаково означает "чисто" и "мерить было нечем", и различить их можно только по числу, из скольких.
Находка шестая: бюджет прогона выбирала сеть
Прогон разбит на шаги: строится план, шаги выполняются по одному, между ними рисуется счётчик. Форма индикатора взята из той самой новогодней ночи, где зависание я опознал по замершим цифрам.
На боевой бухгалтерии это дало 1 225 шагов, 359,8 секунды и 545 пропущенных шагов при бюджете 240. При этом сумма по фазам была 90 секунд. Остальные 270 оказались круговыми вызовами клиент-сервер: по 0,22 секунды на шаг, а шагов больше тысячи. Бюджет считает время по часам, и его выбирала сеть, а не запросы.
Лечится это числом вызовов, бюджет тут ни при чём. Сервер теперь крутит шаги порцией по полторы секунды и только потом отдаёт управление на перерисовку. Индикатор от этого не пострадал, счётчик так же меняется.
было стало
время 359,8 с 125,8 с
пропущено 545 шагов 117
запросов 54 482
Гигабайты я печатать отказался
Первое, чего хочет любой, кто чистит базу: сколько гигабайт освободится. Оценить размер по метаданным несложно, объявленная длина реквизитов известна, число записей известно, перемножай и печатай.
У меня есть свой замер, который это запрещает. База УПП на 3 446,7 гигабайта, 932 сопоставимые строки, оценка по метаданным против факта из СУБД:
топ-5 списка совпал 0 из 5 топ-10 совпал 3 из 10 корреляция Спирмена по всему массиву 0,907 корреляция внутри топ-50 0,484 медиана отношения факт/оценка 1,15 p10 / p90 того же отношения 0,25 / 2,07
По всему массиву корреляция отличная. А в верхушке, то есть ровно там, куда человек смотрит, она разваливается. Смещение систематическое: узкие таблицы занижаются, широкие завышаются в разы, потому что строковый реквизит считается по объявленной длине, а лежит по факту заполнения.
Поэтому гигабайтов по метаданным в отчёте нет ни одного. Есть точные счётчики строк и объектов. Есть точный вес там, где платформа его знает: размер присоединённого файла лежит в реквизите, это факт. А настоящие размеры таблиц отдаёт SQL-скрипт, суженный до тех таблиц, которые чек-ап назвал виноватыми.
Число, которое красиво выглядит и врёт в верхушке, хуже отсутствия числа: первое человек унесёт в план, второе заставит спросить админа.
В каком порядке это чинить
Порядок вылезает из тех же шести отчётов и почти везде одинаковый.
- Файлы и версии. Самый крупный и самый безопасный кусок: на учёт они не влияют вообще. На боевой бухгалтерии это 21 244 066 объектов, из них присоединённых файлов на 5,6 гигабайта. Версии чистятся штатной настройкой срока хранения, файлы выносятся томами на диск.
- Файлы без владельца. Объекта, к которому файл был прикреплён, уже нет, открыть его не может никто. Место при этом занято. Это единственная категория, которую можно убирать не глядя.
- Битые ссылки. Они мешают прямо сейчас, а не когда-нибудь: отчёты врут, проведение падает. Чинятся точечно по полю.
- Помеченные на удаление. Только после того, как посчитано, кто их держит. Те, на которые никто не ссылается, уходят штатным механизмом.
- Дубли. Разбираются глазами. Одинаковое наименование это кандидат в дубли, а не дубль: две номенклатуры с одним названием бывают разными позициями, два контрагента разными юрлицами.
- Свёртка периода. Последней, штатным механизмом конфигурации, и только когда период закрыт и отчётность сдана.
Обратите внимание, что удаление помеченных стоит четвёртым, хотя именно с него все начинают. Причина простая: на пяти базах из шести первые три пункта дают больше освобождённого места и не требуют ни одного решения о том, что можно потерять.
Чего этот подход не делает
Он не доказывает, что объект можно удалить. Доказать это можно только полным ссылочным контролем по конкретному объекту, а он идёт минуты на таблицу. Выборка даёт признак, и признак не равен доказательству, поэтому в корзину "можно убрать точно" попадает только то, что проверено целиком.
Он не считает поля составного типа при поиске битых ссылок: у составного типа разыменование через точку неоднозначно. Их битые ссылки ищет штатное "Тестирование и исправление".
Он не заменяет свёртку и не пишет за неё код. У каждой конфигурации она своя, и универсальный шаблон под неё был бы вредительством.
Если не хочется собирать это руками
Всё описанное собрано в обработку. Она ничего не удаляет и не меняет: ни одной записи в базу, ни одного изменения настроек, запускать можно на боевой базе в рабочий день. Отчёт устроен как разбор у врача: диагноз и вердикт по каждой находке, а под находкой лежит готовый код, который её закрывает. Удаление помеченных с контролем ссылок, схлопывание дублей, очистка битого поля, вынос файлов на диск, чистка версий. Код обработка не выполняет, она его показывает.
Карточка здесь: //infostart.ru/1c/tools/2779829/
Другие наши инструменты:
- Карта объёмов базы 1С - что занимает место в базе по объектам метаданных, с тем же SQL-скриптом администратору. Продолжение раздела про гигабайты: там тот же отказ печатать вес по объявленной длине реквизитов.
- Чек-ап СУБД под 1С - сорок с лишним проверок настроек SQL Server с вердиктом по каждой. Чистка большой базы упирается в модель восстановления и размер журнала чаще, чем в саму 1С.
- Анализ нагрузки кластера 1С - кто держал блокировку и кто грузил сервер, по данным RAS. Пригодится ровно в ту ночь, когда процесс замер и надо понять, живой он или всё.
Вопрос к тем, кто дочитал
Порог я в итоге сделал относительным: треть от числа ссылочных объектов конфигурации. На шести базах он разделяет субконто и свойства объектов без единого промаха, но шесть баз это шесть баз, а конфигураций в природе сильно больше.
Откройте у своей базы любой справочник, который считаете рабочим, и посмотрите, сколько типов принимает его самый широкий реквизит. Если это больше трети от числа справочников и документов вместе - у меня проблема, и мне нужно полное имя этого поля.
И второй вопрос, для тех, кто чистил базу большими партиями. Когда вы это делали в последний раз, вы знали заранее, сколько времени уйдёт? Или, как я в ту новогоднюю ночь, выясняли это уже внутри окна, когда откатываться некуда?
Вступайте в нашу телеграмм-группу Инфостарт