Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Чому count(*) на великій таблиці повільний і як отримати приблизну кількість рядків?

У PostgreSQL немає збереженого лічильника рядків таблиці. Через MVCC різні транзакції одночасно бачать різну кількість рядків, тож SELECT count(*) FROM orders мусить переглянути рядки (або індекс) і перевірити видимість кожного. На сотнях мільйонів рядків це секунди чи хвилини.

Що можна зробити:

1. Приблизна кількість зі статистики - миттєво:

SELECT reltuples::bigint AS estimate
FROM pg_class
WHERE oid = 'public.orders'::regclass;

Значення оновлює ANALYZE/autovacuum. Для лічильника «близько 2,4 млн записів» на сторінці адмінки - цілком достатньо.

2. Оцінка для запиту з умовою - з плану:

EXPLAIN SELECT * FROM orders WHERE status = 'paid';
-- rows=183000 у плані - оцінка планувальника

3. Точна кількість швидше: index-only scan по невеликому індексу, якщо visibility map свіжа (після вакууму), - помітно швидше за читання всієї таблиці.

4. Лічильник, що ведеться окремо: таблиця-лічильник, оновлювана тригером чи застосунком, або кеш на кілька хвилин. Точно й миттєво, але з ціною на кожен запис (і точкою конкуренції, якщо всі пишуть в один рядок лічильника).

Практичні висновки:

  • Пагінація з «сторінка 1 з 48 213» - дорога на великих таблицях: кожна сторінка - ще й count(*). Laravel simplePaginate() чи cursorPaginate() обходяться без підрахунку.
  • «Чи є хоч один рядок» - не count(*) > 0, а EXISTS (SELECT 1 ... ): зупиняється на першому знайденому.
  • count(column) не швидший за count(*) - він ще й перевіряє NULL.

Докладніше в документації: Оцінка кількості рядків

Перевір себе

20 випадкових питань за спробу, після завершення - розбір кожної помилки

Схожі питання