Очень интересная проблема MYSQL (связана с индексацией, миллионами записей, алгоритмом.) - PullRequest
3 голосов
/ 03 мая 2010

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

У меня есть таблица с около 1 миллиона записей. Структура таблицы примерно такая:

items{
  uid (primary key, bigint, 15)
  updated (indexed, int, 11)
  enabled (indexed, tinyint, 1)
}

Сценарий такой. Я должен выбирать все записи каждый день и делать некоторую обработку. Обработка каждого предмета занимает около 3 секунд.

Я написал PHP-скрипт для выборки 200 элементов каждый раз, используя следующее.

select * from items where updated > unix_timestamp(now()) - 86400 and enabled = 1 limit 200;

Затем я обновлю поле «обновленные» выбранных элементов, чтобы убедиться, что оно не будет выбрано снова в течение одного дня. Выбранный запрос выглядит примерно так:

update items set updated = unix_timestamp(now()) where uid in (1,2,3,4,...);

Затем PHP продолжит работать и обрабатывать данные, которые больше не требуют подключения к MYSQL.


Поскольку у меня есть миллион записей, и каждая запись обрабатывается 3 секунды, это невозможно сделать последовательно. Поэтому я буду выполнять PHP каждые 10 секунд.

Однако с течением времени и ростом таблицы выбор становится намного медленнее. Иногда для запуска требуется более 100 секунд!


Ребята, у вас есть предложения, как мне решить эту проблему?

Ответы [ 6 ]

3 голосов
/ 03 мая 2010

Есть два момента, о которых я могу подумать:

а. unix_timestamp (now ()) - 86400)

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

б. Индексы помогают читать, но могут замедлить запись

Рассмотрите возможность удаления индексов перед обновлением (DISABLE KEYS), а затем повторно добавьте их перед чтением (ENABLE KEYS).

2 голосов
/ 03 мая 2010

Вы можете сделать это:

  1. dispatcher.php: Управляет всем процессом.
    • выбирает предметы в удобных пакетах из базы данных
    • вызывает worker.php на том же сервере с HTTP-сообщением, содержащим все извлеченные UID (я понимаю, что worker.php не потребуется больше, чем UID, чтобы выполнить свою работу)
    • поддерживает счетчик количества worker.php скрипов. Когда один запускается, счетчик увеличивается до определенного предела, когда один работник возвращается, то счетчик уменьшается. См. «Асинхронные вызовы PHP? ».
    • повторяется, пока все записи не будут выбраны один раз. Поддерживайте счетчик MySQL LIMIT и не работайте с updated.
  2. worker.php: выполняет реальную работу
    • делает свое дело с каждым отправленным элементом.
    • записывает в вспомогательную таблицу идентификатор каждого обработанного элемента (без индекса для этой таблицы)
  3. dispatcher.php: домохозяйство.
    • как только все работники вернулись, обновляет основную таблицу с помощью вспомогательной таблицы в одном операторе
  4. исправление ошибок
    • , поскольку worker.php обновляет вспомогательную таблицу после выполнения каждого элемента, вы можете использовать состояние вспомогательной таблицы для восстановления после сбоя. Сохранение «рабочего пакета» каждого отдельного работника до его запуска также поможет восстановить рабочие состояния.

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

2 голосов
/ 03 мая 2010

Не думаю, что индекс на enabled приносит вам пользу, количество элементов слишком мало. Удалите это, и ваши UPDATE должны идти быстрее.

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

1 голос
/ 03 мая 2010

Вы можете попробовать запустить это до обновления:

ALTER TABLE items DISABLE KEYS;

и затем, когда вы закончите обновление,

ALTER TABLE items ENABLE KEYS;

Это должно воссоздать индекс намного быстрее, чем обновление каждой записи за раз.

0 голосов
/ 03 мая 2010

Одна идея:

Используйте HANDLER, который значительно улучшит вашу производительность:

http://dev.mysql.com/doc/refman/5.1/en/handler.html

0 голосов
/ 03 мая 2010

Для таблицы, содержащей менее пары миллиардов записей, первичным ключом должен быть unsigned int, а не bigint.

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