Битрикс24: очистка таблиц модуля socialnetwork

Автоматическая очистка устаревших записей социальной сети в Битрикс24. Метод ClearOld, cron-задача и оптимизация базы данных.

Дмитрий Мещеряков
Дмитрий Мещеряков
📅 23 января 2025 г.📖 5 мин чтения

Живая лента в Битрикс24 — самая быстрорастущая подсистема портала. Каждое действие сотрудника, каждый комментарий, каждое изменение задачи оставляет запись, а вместе со связями по правам доступа одна запись превращается в несколько строк в разных таблицах.

На портале, работающем несколько лет, это десятки миллионов строк — и заметное замедление всего, что обращается к ленте. Разберём штатный способ чистки и то, как применять его, не уронив портал.

Почему растут таблицы модуля socialnetwork

Живая лента — это подсистема модуля socialnetwork, которая пишет запись на каждое действие сотрудника и раскладывает её сразу по нескольким таблицам:

  • b_sonet_log — лента новостей
  • b_sonet_log_comment — комментарии
  • b_sonet_log_site — привязка к сайтам
  • b_sonet_log_right — права доступа

Со временем эти таблицы разрастаются до миллионов записей, замедляя работу портала.

Как правильно чистить живую ленту

Битрикс предоставляет встроенный метод CSocNetLog::ClearOld() для очистки устаревших записей. Использовать штатный метод здесь принципиально: связи между таблицами ленты неочевидны, и ручной DELETE FROM b_sonet_log оставит осиротевшие записи в b_sonet_log_comment, b_sonet_log_right и b_sonet_log_site — то есть мусор, который уже ничем не вычистить, потому что связать его не с чем.

php
// Удаляет записи старше 90 дней
CSocNetLog::ClearOld(90);

// Удаляет записи старше 30 дней
CSocNetLog::ClearOld(30);

Как работает метод

php
public static function ClearOld($days = 90)
{
    global $DB;

    $days = (int)$days;
    if ($days <= 0) {
        return true;
    }

    // Удаляем комментарии к старым записям
    $DB->Query("
        DELETE LC FROM b_sonet_log_comment LC 
        INNER JOIN (
            SELECT L.TMP_ID FROM b_sonet_log L 
            LEFT JOIN b_sonet_log_favorites LF ON L.ID = LF.LOG_ID 
            WHERE LF.USER_ID IS NULL 
            AND L.LOG_UPDATE < DATE_SUB(NOW(), INTERVAL {$days} DAY)
        ) L1 ON LC.LOG_ID = L1.TMP_ID
    ", true);
    
    // Удаляем привязки к сайтам
    $DB->Query("
        DELETE LS FROM b_sonet_log_site LS 
        INNER JOIN (
            SELECT L.ID FROM b_sonet_log L 
            LEFT JOIN b_sonet_log_favorites LF ON L.ID = LF.LOG_ID 
            WHERE LF.USER_ID IS NULL 
            AND L.LOG_UPDATE < DATE_SUB(NOW(), INTERVAL {$days} DAY)
        ) L1 ON LS.LOG_ID = L1.ID
    ", true);
    
    // Удаляем права
    $DB->Query("
        DELETE LR FROM b_sonet_log_right LR 
        INNER JOIN (
            SELECT L.ID FROM b_sonet_log L 
            LEFT JOIN b_sonet_log_favorites LF ON L.ID = LF.LOG_ID 
            WHERE LF.USER_ID IS NULL 
            AND L.LOG_UPDATE < DATE_SUB(NOW(), INTERVAL {$days} DAY)
        ) L1 ON LR.LOG_ID = L1.ID
    ", true);

    // Удаляем сами записи ленты
    return $DB->Query("
        DELETE FROM b_sonet_log 
        WHERE LOG_UPDATE < DATE_SUB(NOW(), INTERVAL {$days} DAY)
    ", true);
}
💡 Совет

Важно: Метод НЕ удаляет записи, добавленные в избранное (b_sonet_log_favorites). Это защищает важные для пользователей записи.

⚠️ Важно

Первый запуск на портале, который никогда не чистили, — самый опасный. Метод удалит всё накопленное за годы одной операцией: миллионы строк, длительные блокировки, разросшийся журнал транзакций и портал, не отвечающий на запросы всё это время.

Безопасный подход — заходить сверху и постепенно: сначала ClearOld(1095) (три года), убедиться, что портал жив и объём уменьшился, затем ClearOld(730), затем ClearOld(365). Каждый шаг удаляет ощутимо меньше предыдущего, и ни один не создаёт пиковой нагрузки.

И обязательно — на копии базы сначала. Не ради проверки корректности метода (он штатный), а чтобы измерить, сколько операция реально занимает на ваших объёмах.

Автоматизация через cron

Создайте файл /local/cron/cleanup_socialnetwork.php:

php
<?php
/**
 * Очистка таблиц socialnetwork
 * Запуск: 0 3 * * 0 php /home/bitrix/www/local/cron/cleanup_socialnetwork.php
 */

$_SERVER["DOCUMENT_ROOT"] = realpath(dirname(__FILE__) . "/../..");
$DOCUMENT_ROOT = $_SERVER["DOCUMENT_ROOT"];

// Отключаем лишнюю логику
define("NO_KEEP_STATISTIC", true);
define("NOT_CHECK_PERMISSIONS", true);
define("NO_AGENT_CHECK", true);
define("DisableEventsCheck", true);

require($_SERVER["DOCUMENT_ROOT"] . "/bitrix/modules/main/include/prolog_before.php");

// Увеличиваем лимиты для тяжёлой операции
ini_set('ignore_user_abort', 1);
ini_set('max_execution_time', 3600);
ini_set('memory_limit', '1024M');
set_time_limit(3600);

if (!\Bitrix\Main\Loader::includeModule('socialnetwork')) {
    die('Ошибка: не удалось подключить модуль socialnetwork');
}

$startTime = microtime(true);

// Очищаем записи старше 90 дней
$result = CSocNetLog::ClearOld(90);

$executionTime = round(microtime(true) - $startTime, 2);

// Логируем результат
$logMessage = date('Y-m-d H:i:s') . " - Очистка socialnetwork завершена за {$executionTime} сек.\n";
file_put_contents(
    $_SERVER["DOCUMENT_ROOT"] . "/local/logs/cleanup_socialnetwork.log",
    $logMessage,
    FILE_APPEND
);

echo $logMessage;

Настройка cron

bash
# Запуск каждое воскресенье в 3:00
0 3 * * 0 /usr/bin/php /home/bitrix/www/local/cron/cleanup_socialnetwork.php >> /home/bitrix/www/local/logs/cron.log 2>&1

Дополнительная оптимизация

После массового удаления рекомендуется оптимизировать таблицы:

sql
OPTIMIZE TABLE b_sonet_log;
OPTIMIZE TABLE b_sonet_log_comment;
OPTIMIZE TABLE b_sonet_log_site;
OPTIMIZE TABLE b_sonet_log_right;

Скрипт полной оптимизации

php
<?php
// После ClearOld выполняем оптимизацию
global $DB;

$tables = [
    'b_sonet_log',
    'b_sonet_log_comment',
    'b_sonet_log_site',
    'b_sonet_log_right',
];

foreach ($tables as $table) {
    $DB->Query("OPTIMIZE TABLE {$table}");
    echo "Оптимизирована таблица: {$table}\n";
}

Мониторинг размера таблиц

sql
SELECT 
    table_name AS 'Таблица',
    ROUND(data_length / 1024 / 1024, 2) AS 'Данные (MB)',
    ROUND(index_length / 1024 / 1024, 2) AS 'Индексы (MB)',
    table_rows AS 'Строки'
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name LIKE 'b_sonet_log%'
ORDER BY data_length DESC;

Итоги

Чистить ленту нужно, и лучше регулярно. Ежедневное удаление небольшого объёма не создаёт нагрузки; разовая операция над данными за пять лет требует окна обслуживания.

Только штатным методом. Ручной DELETE по одной таблице оставляет мусор в связанных, и убрать его потом нечем.

Первый запуск — ступенями сверху вниз, а не сразу целевым значением.

Срок хранения — вопрос к бизнесу, а не к разработчику. Лента содержит рабочую переписку и историю обсуждений; 90 дней подходят большинству компаний, но узнать это нужно до удаления, а не после.

После чистки место на диске не освободится само — потребуется перестройка таблиц, а это отдельная операция с блокировкой записи, которую планируют в окно минимальной нагрузки.

⚠️ Важно

Перед первым запуском на боевом сервере — обязательно копия базы и прогон на ней. Нужно знать не только что метод отработает, но и сколько времени это займёт на ваших объёмах: разница между «пять минут» и «два часа» полностью меняет план работ.

Частые вопросы

Почему разрастаются таблицы socialnetwork в Битрикс24?

Потому что живая лента пишет запись на каждое действие сотрудника: комментарий, изменение задачи, публикацию. Одна запись при этом раскладывается сразу по нескольким таблицам — сама запись в b_sonet_log, комментарии в b_sonet_log_comment, привязка к сайтам в b_sonet_log_site и права доступа в b_sonet_log_right. На портале, работающем несколько лет, это десятки миллионов строк.

Можно ли чистить живую ленту запросом DELETE?

Нет. Связи между таблицами ленты неочевидны, и ручное удаление из b_sonet_log оставит осиротевшие записи в таблицах комментариев, прав и привязок к сайтам — мусор, который уже ничем не вычистить, потому что связать его не с чем. Используйте штатный метод CSocNetLog::ClearOld: он знает про все связи.

Чем опасен первый запуск очистки ленты?

Тем, что на портале, который никогда не чистили, метод удалит всё накопленное за годы одной операцией: миллионы строк, длительные блокировки, разросшийся журнал транзакций и портал, не отвечающий на запросы всё это время. Безопасный подход — заходить сверху и постепенно: сначала 1095 дней, убедиться, что портал жив и объём уменьшился, затем 730, затем 365. Каждый шаг удаляет ощутимо меньше предыдущего.

Удаляет ли ClearOld важные записи?

Записи, добавленные пользователями в избранное, метод не трогает — они защищены отдельной таблицей. Всё остальное удаляется по возрасту, поэтому срок хранения стоит согласовать с бизнесом: для одних компаний года истории достаточно, для других лента — это рабочая переписка, которую нельзя терять.

Нужно ли оптимизировать таблицы после очистки?

Да, но как отдельную операцию с простоем, а не завершающий шаг скрипта. Для InnoDB оптимизация означает полную перестройку таблицы с блокировкой записи и требует свободного места под вторую копию. Выполнять только в окно минимальной нагрузки, а на действительно больших таблицах — через инструмент онлайн-изменения схемы.

Как автоматизировать очистку ленты?

Отдельным cron-скриптом, а не агентом на хите: операция длительная и не должна зависеть от наличия посетителей. Запускать ежедневно или еженедельно с фиксированным сроком хранения — тогда каждый запуск удаляет немного и не создаёт нагрузки. И добавьте мониторинг размера таблиц: он покажет, работает ли чистка вообще.