Mysql подсчет строк с использованием фильтров в базе данных с высоким трафиком - PullRequest
9 голосов
/ 10 июня 2011

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

Итак, предположим, что сайт посещает не менее 300 тыс. (300 000) пользователей в день, и пользователь выбирает параметры из формы не менее 40 раз за посещение, что означает 12 млн. Запросов ajax + 12 млн. Запросов к базе данных, что выглядит слишком много.

Вопрос в том, как реализовать быстрый подсчет (используя php (Zend Framework) и MySQL), чтобы дополнительные 12M запросов к базе данных не влияли на загрузку сайта.

Одним из решений было бы иметь таблицу, в которой хранятся все комбинации выбранных полей и их соответствующие значения (когда продукт добавляется или удаляется из таблицы продуктов, таблица, в которой хранится число, обновляется). Хотя это не очень хорошая идея, когда для 8 фильтров (выбор параметров) из 43 будет вставлено + 8M строк, которыми необходимо управлять.

Есть еще мысли о том, как этого добиться?

p.s. Мне не нужны примеры кода, но сама идея, которая будет работать в этом сценарии.

Ответы [ 10 ]

7 голосов
/ 10 июня 2011

Я бы, наверное, имел предварительно рассчитанную таблицу - как вы предлагаете сами. Импорт в том, что у вас есть умный механизм для 2 вещей:

  1. Легко запрашивать, на какие записи влияет какое изменение.
  2. Иметь уникальное поле поиска для всего запроса формы.

Записи 8M не будут иметь большого значения, если у вас есть сплошные ключи, поскольку вам потребуется только прямой поиск.

Я бы попытался написать конкретные обновления для этой таблицы во всех местах, где это необходимо. Даже с большим количеством изменений это все еще эффективно. Если все сделано правильно, вы узнаете, какие строки нужно обновить или сделать недействительными при вставке / обновлении / удалении продукта.

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

4 голосов
/ 10 июня 2011

есть несколько сценариев

  1. MySQL имеет кеш запросов, вам не нужно беспокоиться о кешировании IF the update of table is not that frequently

  2. 99% пользователь не будет беспокоиться о количестве совпадений, he/she just need the top few records

  3. используйте explain - если вы заметите, explain вернет, сколько строк будет сопоставлено в запросе, is not 100% precise, но должно быть достаточно хорошим, чтобы действовать как приблизительное число строк

3 голосов
/ 14 июня 2011

У вас на самом деле есть только три варианта, и ни один из поисков вряд ли выявит четвертый:

  1. Подсчитайте результаты вручную.O (n) с общим количеством результатов во время запроса.
  2. Сохранение и ведение счета для каждой комбинации фильтров.O (1) для извлечения счетчика, но требует O (2 ^ n) памяти и O (2 ^ n) времени для обновления всех подсчетов при изменении записей.
  3. Кэширует подсчеты, только вычисляя их (за #1) когда они не найдены в кеше.O (1) когда данные находятся в кэше, O (n) в противном случае.

По этой причине системы, которые должны масштабироваться за пределы тривиального значения, то есть большинство из них, либо ограничиваютколичество результатов, которые они подсчитают (например, элементы в вашем почтовом ящике GMail или непрочитанные в Google Reader), подсчитайте количество на основе статистики (например, количество результатов поиска Google) или оба.

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

3 голосов
/ 10 июня 2011

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

Если у вас нет необходимости получать актуальную информацию из поиска, вы можете использовать систему очередей для отправки обновлений и вставок в Lucene время от времени (поэтомувам не нужно беспокоить Lucene несколькими тысячами обновлений и вставок каждый день).

2 голосов
/ 18 июня 2011

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

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

псевдокод:

CREATE TABLE counts (
  id unsigned integer auto_increment primary key
  option integer indexed using hash key
  user_id integer indexed using hash key
  rowcount unsigned integer
  unique key user_option (user, option)
) engine = memory

DELIMITER $$

CREATE TRIGGER ai_tablex_each AFTER UPDATE ON tablex FOR EACH ROW
BEGIN
  IF (old.option <> new.option) OR (old.user_id <> new.user_id) THEN BEGIN
    UPDATE counts c SET c.rowcount = c.rowcount - 1 
      WHERE c.user_id = old.user_id and c.option = old.option; 
    INSERT INTO counts rowcount, user_id, option  
      VALUES (1, new.user_id, new.option)
      ON DUPLICATE KEY SET c.rowcount = c.rowcount + 1; 
  END; END IF;
END $$

DELIMITER ;

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

Ссылки:
Движок памяти: http://dev.mysql.com/doc/refman/5.5/en/memory-storage-engine.html
Триггеры: http://dev.mysql.com/doc/refman/5.5/en/triggers.html

2 голосов
/ 10 июня 2011

Несколько вещей, которые вы можете легко оптимизировать:

  1. Кэшируйте все, что вы можете позволить себе кэшировать.Например, опции для ваших выпадающих меню, они должны быть получены вызовами ajax? Эта страница ответила на многие мои вопросы, когда я внедрила memcache, и, конечно, у memcached.org также есть отличная документация.

  2. Служите всему, что может обслуживаться статически.То есть, параметры, которые не меняются часто, могут храниться в виде плоского файла в виде массива через cron, например, каждый час и включаться в сценарий во время выполнения.

  3. MySQL с настройками конфигурации по умолчанию частонеоптимальный для любой серьезной прикладной нагрузки и должен быть настроен в соответствии с потребностями поставленной задачи.Возможно, посмотрите на механизм памяти для высокопроизводительного доступа для чтения.

Вы можете взглянуть на эти 3 замечательных, но очень технических сообщения наматериализованные взгляды , на самом деле весь блог - это действительно золотая жила советов по производительности для mysql.

GOod-luck

0 голосов
/ 18 июня 2011

Как уже предлагали другие, вам действительно нужен какой-то механизм кэширования на стороне сервера.Будь то таблица MySQL или memcache, все будет работать.Но чтобы уменьшить количество обращений к серверу, получите полный список кэшированных подсчетов за один запрос и кешируйте это локально в javascript.Это довольно простой способ устранить почти 12 млн. Обращений к серверу.

Возможно, вы даже можете сохранить информацию о количестве в файле cookie, срок действия которого истекает через час, поэтому при последующих загрузках страницы нет необходимости повторять запрос.Это если вам не нужны цифры в реальном времени.

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

Вы можете разместить много данных в 1-2 КБСтруктура данных JSON.Поэтому, даже если у вас есть тысячи возможных вариантов подсчета, это все равно меньше, чем у вашего обычного изображения.Просто имейте в виду максимальные размеры файлов cookie, если вы используете кэширование файлов cookie.

0 голосов
/ 16 июня 2011

Я чувствую себя сумасшедшим из-за того, что предлагаю это, так как кажется, что никто другой этого не сделал, но рассматривали ли вы кеширование на стороне клиента? JavaScript не страшен при работе с большими списками, особенно если они относительно простые списки.

Я знаю, что ваш идеал в том, что у вас есть желание сделать цифры абсолютно точными, но эвристика здесь ваш друг, тем более что синхронизация никогда не будет 100% - медленное соединение или высокая задержка из-за трафика на стороне сервера сделает запрос AJAX устаревшим, особенно если эти данные не являются константами. ЕСЛИ ДАННЫЕ МОЖНО ИЗМЕНИТЬ ДАННЫЕ ДРУГИМИ ПОЛЬЗОВАТЕЛЯМИ, СИНХРОНИЧНОСТЬ ПРИ ИСПОЛЬЗОВАНИИ AJAX невозможна. ЕСЛИ ЭТО НЕ МОЖЕТ БЫТЬ ИЗМЕНЕНО НИКОГДА, ТОГДА КЕШИНГ КЛИЕНТА БУДЕТ РАБОТАТЬ И ПОДОБНО ВАШ ЛУЧШИЙ ВАРИАНТ . Да, и если вы используете какое-то соединение с портом, то все, что отправляет на сервер, может просто обновить все другие клиенты, пока не будет выполнена синхронизация.

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

0 голосов
/ 14 июня 2011

Подумайте, какую роль может сыграть репликация в вашей архитектуре. Если вам нужно уменьшить масштаб, вы можете подумать о репликации своих таблиц из InnoDB в MyISAM. Механизм MyISAM автоматически поддерживает количество таблиц, если вы выполняете count(*) запросов. Если вы делаете count(col) where запросов, то вам нужно сильно полагаться на хорошо разработанные индикаторы. В этом случае ваши запросы подсчета могут выглядеть следующим образом:

alter table A add index ixA (a, b);
select count(a) using from A use index(ixA) where a=1 and b=2;
0 голосов
/ 14 июня 2011

Предположительно, вы используете ajax, чтобы позвонить на сервер, о котором вы говорите. Используйте какой-то разбитый плоский файл в качестве промежуточного звена для данных. Установите время истечения 5 секунд или что угодно. Назовите файл данных как ключ запроса = строка значения. В запросе ajax, если файл данных старше вашего времени восстановления, затем обновите, если нет, используйте значение, хранящееся в вашем файле данных.

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

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