Почему COUNT(*) на большой таблице медленный
Вопрос
Счётчик «всего заказов» на главной странице делает SELECT COUNT(*) FROM orders по таблице на 200 млн строк и занимает секунды. Почему PostgreSQL не хранит готовое число и что делают на практике?
Попробуйте сами
Выберите вариант, и откроется разбор.
Показать ответ и разбор
Коротко
Варианты ответа
- Из-за MVCC число строк у транзакций разное; приблизительно
reltuples, точно счётчикправильный ответ - PostgreSQL хранит число строк, но
COUNT(*)его игнорирует; надо писатьCOUNT(1) COUNT(*)читает все колонки строки, поэтомуCOUNT(id)будет в разы быстрее- Нужно добавить индекс по первичному ключу, и счёт станет мгновенным
Разбор
В MVCC нет единого «числа строк»: для каждой транзакции оно своё, в зависимости от снимка. Поэтому COUNT(*) обязан проверить видимость строк: Seq Scan или Index Only Scan по самому маленькому индексу (быстрее при свежей visibility map). COUNT(1) и COUNT(*) в PostgreSQL выполняются одинаково, а COUNT(id) ещё и проверяет NULL. Индекс по первичному ключу уже есть. Если точность не нужна, берут оценку SELECT reltuples FROM pg_class WHERE relname = 'orders' (её обновляют VACUUM и ANALYZE), если нужна, ведут счётчик триггером или в приложении.
Ответьте сами, а не прочитайте
В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.
Предыдущий вопрос
Почему в плане Bitmap Heap Scan, а не Index Scan
Следующий вопрос
Подготовленный запрос внезапно стал медленным