Ускорьте автовакуум в Postgres

#postgresql #postgresql-12 #vacuum #autovacuum

Вопрос:

У меня есть вопрос относительно настроек автовакуума / вакуума Postgres. У меня есть таблица с 4,5 миллиардами строк, и был период времени с большим количеством обновлений, в результате которых было ~ 1,5 миллиарда мертвых кортежей. На данный момент для завершения автовакуума требовалось много времени (дней). При просмотре представления pg_stat_progress_vacuum я заметил, что:

 max_dead_tuples = 178956970
 

в результате происходит многократное повторное сканирование индекса (index_vacuum_count)
Согласно документам — max_dead_tuples-это количество мертвых кортежей, которые мы можем сохранить до того, как потребуется выполнить цикл очистки индекса на основе maintenance_work_mem.
Согласно этому, для одного мертвого кортежа требуется 6 байт пространства.
Таким образом, 6B x 178956970 = ~1 ГБ
Но мои настройки таковы

 maintenance_work_mem = 20GB
autovacuum_work_mem = -1
 

Так что же я упускаю? почему все мои 1,5-битные мертвые кортежи не поместились в max_dead_tuples, так как 20 ГБ должно было дать достаточно места, и почему было необходимо несколько запусков?

Ответ №1:

Существует жестко заданный предел в 1 ГБ для количества мертвых кортежей за один VACUUM цикл, см. Источник:

 /*
 * Return the maximum number of dead tuples we can record.
 */
static long
compute_max_dead_tuples(BlockNumber relblocks, bool useindex)
{
    long        maxtuples;
    int         vac_work_mem = IsAutoVacuumWorkerProcess() amp;amp;
    autovacuum_work_mem != -1 ?
    autovacuum_work_mem : maintenance_work_mem;

    if (useindex)
    {
        maxtuples = MAXDEADTUPLES(vac_work_mem * 1024L);
        maxtuples = Min(maxtuples, INT_MAX);
        maxtuples = Min(maxtuples, MAXDEADTUPLES(MaxAllocSize));

        /* curious coding here to ensure the multiplication can't overflow */
        if ((BlockNumber) (maxtuples / LAZY_ALLOC_TUPLES) > relblocks)
            maxtuples = relblocks * LAZY_ALLOC_TUPLES;

        /* stay sane if small maintenance_work_mem */
        maxtuples = Max(maxtuples, MaxHeapTuplesPerPage);
    }
    else
        maxtuples = MaxHeapTuplesPerPage;

    return maxtuples;
}
 

MaxAllocSize определяется src/include/utils/memutils.h как

 #define MaxAllocSize   ((Size) 0x3fffffff) /* 1 gigabyte - 1 */
 

Вы можете лоббировать в списке pgsql-хакеров, чтобы увеличить лимит.