Ускорение подсчета строк в MySQL - PullRequest
41 голосов
/ 26 августа 2009

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

(идентификатор, название, статус)

  • id - это первичный ключ
  • название название книги
  • status может быть перечислением, описывающим текущее состояние книги (например, ДОСТУПНО, ПРОВЕРЕНО, ОБРАБОТАНО, ОТСУТСТВУЕТ)

Простой запрос, чтобы сообщить, сколько книг попадает в каждое состояние:

SELECT status, COUNT(*) FROM books GROUP BY status

или чтобы узнать, сколько книг доступно:

SELECT COUNT(*) FROM books WHERE status = "AVAILABLE"

Однако, как только таблица увеличится до миллионов строк, выполнение этих запросов займет несколько секунд. Добавление индекса в столбец «status», похоже, не меняет моего опыта.

Помимо периодического кэширования результатов или явного обновления сводной информации в отдельной таблице каждый раз, когда книга меняет состояние (с помощью триггеров или другого механизма), существуют ли какие-либо методы для ускорения таких запросов? Кажется, что запросы COUNT заканчиваются просмотром каждой строки, и (не зная больше деталей) я немного удивлен, что эту информацию нельзя каким-то образом определить из индекса.

UPDATE

Используя образец таблицы (с индексированным столбцом «status») с 2 миллионами строк, я провел сравнительный анализ запроса GROUP BY. Используя механизм хранения InnoDB, запрос занимает 3,0 - 3,2 секунды на моем компьютере. Используя MyISAM, запрос занимает 0,9 - 1,1 секунды. В обоих случаях не было существенной разницы между количеством (*), количеством (статусом) или количеством (1).

MyISAM по общему признанию немного быстрее, но мне было любопытно посмотреть, есть ли способ заставить эквивалентный запрос выполняться намного быстрее (например, 10-50 мс - достаточно быстро, чтобы вызываться на каждом запрос веб-страницы для сайта с низким трафиком) без лишних затрат на кэширование и триггеры. Похоже, что ответ «нет возможности быстро выполнить прямой запрос», чего я и ожидал - я просто хотел убедиться, что я не пропустил простой вариант.

Ответы [ 5 ]

37 голосов
/ 26 августа 2009

Так что вопрос

Существуют ли способы ускорения запросов такого типа?

Ну, не совсем. Механизм хранения на основе столбцов, вероятно, будет быстрее с этими запросами SELECT COUNT (*), но он будет менее производительным для любого другого запроса.

Лучше всего поддерживать сводную таблицу с помощью триггеров. Он не требует больших накладных расходов, и часть SELECT будет мгновенной независимо от размера таблицы. Вот некоторый шаблонный код:

DELIMITER //

CREATE TRIGGER ai_books AFTER INSERT ON books
FOR EACH ROW UPDATE books_cnt SET total = total + 1 WHERE status = NEW.status
//
CREATE TRIGGER ad_books AFTER DELETE ON books
FOR EACH ROW UPDATE books_cnt SET total = total - 1 WHERE status = OLD.status;
//
CREATE TRIGGER au_books AFTER UPDATE ON books
FOR EACH ROW
BEGIN
    IF (OLD.status <> NEW.status)
    THEN
        UPDATE books_cnt SET total = total + IF(status = NEW.status, 1, -1) WHERE status IN (OLD.status, NEW.status);
    END IF;
END
//
9 голосов
/ 26 августа 2009

MyISAM на самом деле довольно быстр с подсчетом (*), недостатком является то, что хранилище MyISAM не настолько надежно и его лучше избегать там, где важна целостность данных.

InnoDB может быть очень медленным для выполнения запросов типа count (*), потому что он разработан, чтобы допускать несколько одновременных просмотров одних и тех же данных. Поэтому в любой момент времени недостаточно перейти к индексу, чтобы получить счет.

От: http://www.mail-archive.com/mysql@lists.mysql.com/msg120320.html

База данных начинается с 1000 записей в нем начинаю транзакцию начинаешь транзакцию удаляю 50 записей добавить 50 записей, которые я делаю COUNT () и посмотреть 950 записей. Вы делаете COUNT () и видите 1050 записей. Я совершаю свою транзакцию - база данных теперь имеет 950 записей для всех, кроме вас. Вы делаете свой транзакция - база данных имеет 1000 снова записи.

Как InnoDB справляется с какими записями «видимые» или «модифицируемые» с уважение к любой сделке через блокировка на уровне строк, транзакция уровни изоляции и мульти-версии. http://dev.mysql.com/doc/refman/4.1/en/innodb-transaction-model.html http://dev.mysql.com/doc/refman/4.1/en/innodb-multi-versioning.html

Вот что значит считать сколько записи каждый человек может видеть не так прямо вперед.

Итак, суть в том, что вам нужно как-то взглянуть на кеширование подсчетов, а не переходить к таблице, если вам нужно часто и быстро получать эту информацию.

8 голосов
/ 26 августа 2009

от: http://dev.mysql.com/doc/refman/5.0/en/innodb-restrictions.html

InnoDB не ведет внутренний подсчет строк в таблице. (На практике это было бы несколько сложно из-за мульти-версия.) Для обработки SELECT COUNT (*) FROM t, InnoDB должен отсканировать индекс таблицы, которая занимает некоторое время, если индекс не полностью в буферном пуле.

Предлагаемое решение:

Чтобы получить быстрый счет, вы должны использовать счетчик таблицы вы создаете сами и пусть ваше приложение обновит его в соответствии со вставками и удаляет оно делает. ПОКАЗАТЬ СТАТУС СТАТУС также может быть используется, если приблизительное количество строк достаточно.

Вкратце: count (*) (в innoDB) займет много времени для таблиц, содержащих большое количество строк. Это сделано специально, и ничего не поделаешь.

Напишите свой собственный обходной путь.

3 голосов
/ 28 декабря 2016

Многие ответы здесь говорят, что индекс не поможет, но в моем случае это помогло ...

Моя таблица использовала MyISAM и имела только около 100 тыс. Строк. Запрос:

select count(*) from mytable where foreign_key_id=n

заняло 7-8 секунд.

Я добавил индекс на foreign_key_id:

create index myindex on mytable (foreign_key_id) using btree;

После создания индекса вышеприведенная инструкция select сообщает о времени выполнения 0,00 секунд.

2 голосов
/ 09 сентября 2015

Не было существенной разницы между количеством (*), числом (состоянием) или количеством (1)

count (column) возвращает количество строк, в которых столбец NOT NULL. Поскольку 1 НЕ НЕДЕЙСТВИТЕЛЕН, а статус также, предположительно, НЕ НУЛЕЙ, база данных оптимизирует тест и преобразует их всех в число (*). Что, по иронии судьбы, не означает «считать строки, где все столбцы не равны нулю» (или любую другую комбинацию), это просто означает «считать строки» ...

Теперь, возвращаясь к вашему вопросу, вы не можете съесть свой торт и съесть его ...

  • Если вы хотите, чтобы «точный» счет был доступен постоянно, то вы должны увеличивать и уменьшать его в реальном времени с помощью триггеров, что замедляет ваши записи

  • Или вы можете использовать count (*), но это будет медленно

  • Или вы можете согласиться на приблизительную оценку или устаревшее значение и использовать кэширование или другие вероятностные подходы.

Как правило, при значениях, приведенных выше о «нескольких», NO-ONE заинтересован в точном подсчете в реальном времени. В любом случае, это красная сельдь, так как к тому времени, как вы ее прочитаете, значение, скорее всего, изменится.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...