запрос огромной таблицы базы данных занимает слишком много времени в mysql

Я запускаю sql-запросы к таблице mysql db, которая имеет более 110 миллионов уникальных записей за весь день.

Проблема: всякий раз, когда я запускаю любой запрос с предложением «где», это занимает не менее 30-40 минут. Поскольку я хочу сгенерировать большую часть данных на следующий день, мне нужен доступ ко всей таблице БД.

Не могли бы вы помочь мне оптимизировать/реструктурировать модель развертывания?

Описание сайта:

mysql  Ver 14.12 Distrib 5.0.24, for pc-linux-gnu (i686) using readline 5.0
4 GB RAM, 
Dual Core dual CPU 3GHz
RHEL 3

содержимое my.cnf:

[mysqld]
datadir=/data/mysql/data/
socket=/tmp/mysql.sock

sort_buffer_size = 2000000
table_cache = 1024
key_buffer = 128M
myisam_sort_buffer_size = 64M

# Default to using old password format for compatibility with mysql 3.x
# clients (those using the mysqlclient10 compatibility package).
old_passwords=1

[mysql.server]
user=mysql
basedir=/data/mysql/data/

[mysqld_safe]
err-log=/data/mysql/data/mysqld.log
pid-file=/data/mysql/data/mysqld.pid
[root@reports root]#

Детали таблицы БД:

CREATE TABLE `RAW_LOG_20100504` (
  `DT` date default NULL,
  `GATEWAY` varchar(15) default NULL,
  `USER` bigint(12) default NULL,
  `CACHE` varchar(12) default NULL,
  `TIMESTAMP` varchar(30) default NULL,
  `URL` varchar(60) default NULL,
  `VERSION` varchar(6) default NULL,
  `PROTOCOL` varchar(6) default NULL,
  `WEB_STATUS` int(5) default NULL,
  `BYTES_RETURNED` int(10) default NULL,
  `RTT` int(5) default NULL,
  `UA` varchar(100) default NULL,
  `REQ_SIZE` int(6) default NULL,
  `CONTENT_TYPE` varchar(50) default NULL,
  `CUST_TYPE` int(1) default NULL,
  `DEL_STATUS_DEVICE` int(1) default NULL,
  `IP` varchar(16) default NULL,
  `CP_FLAG` int(1) default NULL,
  `USER_LOCATE` bigint(15) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1 MAX_ROWS=200000000;

Заранее спасибо! С уважением,


person Vijay Gharge    schedule 04.05.2010    source источник
comment
Не могли бы вы предоставить нам несколько примеров операторов выбора, которые вы выполняете и которые кажутся очень медленными?   -  person NebuSoft    schedule 05.05.2010
comment
Есть ли у вас какие-либо индексы в таблице, которые можно использовать в предложении WHERE?   -  person Martin Smith    schedule 05.05.2010
comment
@ Nebusoft - Thnx для ответа выберите количество (*), WEB_STATUS из $table_name, где CP_FLAG › 0 группирует по 2 порядка по 1 убыванию; @Martin: Спасибо за ответ. Я не знаю, как поместить индекс в эту таблицу БД, поскольку она не содержит уникального ключа. Вы чувствуете, что использование auto_increment помогает мне здесь?   -  person Vijay Gharge    schedule 05.05.2010
comment
Индексы не нужны вам уникальными....   -  person Frank V    schedule 05.05.2010
comment
отредактируйте свой вопрос с дополнительной информацией, чтобы вы могли отформатировать его, и люди действительно могли его прочитать.   -  person    schedule 05.05.2010


Ответы (2)


Я бы посоветовал вам научиться использовать EXPLAIN. для анализа плана базы данных по оптимизации запросов. Также см. презентацию барона Шварца EXPLAIN Demystified (ссылка на PDF-файл его слайды есть на этой странице).

Узнайте, как создавать индексы — это не то же самое, что первичный ключ или псевдоключ с автоинкрементом. См. презентацию Дополнительные сведения об искусстве индексирования, автор Ёсинори Мацунобу.

Ваша таблица может использовать индекс для CP_FLAG и WEB_STATUS.

CREATE INDEX CW ON RAW_LAW_20100503 (CP_FLAG, WEB_STATUS);

Это помогает искать подмножество строк на основе вашего условия cp_flag.

Тогда вы все равно столкнетесь с досадной неэффективностью MySQL с GROUP BY запросами. Он копирует промежуточный набор результатов во временный файл на диске и сортирует его там. Дисковый ввод-вывод снижает производительность.

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

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


Комментарий от @Marcus дал мне еще одну идею. Вы группируете по веб-статусу, и набор различных значений веб-статуса представляет собой довольно короткий список, и они не меняются. Таким образом, вы можете запускать отдельный запрос для каждого отдельного значения и генерировать нужные результаты намного быстрее, чем при использовании запроса GROUP BY, который создает временную таблицу для сортировки. Или вы можете запустить подзапрос для каждого значения состояния и UNION их вместе:

(SELECT COUNT(*), WEB_STATUS FROM RAW_LOG_20100504 WHERE CP_FLAG > 0 AND WEB_STATUS = 200)
UNION
(SELECT COUNT(*), WEB_STATUS FROM RAW_LOG_20100504 WHERE CP_FLAG > 0 AND WEB_STATUS = 404)
UNION
(SELECT COUNT(*), WEB_STATUS FROM RAW_LOG_20100504 WHERE CP_FLAG > 0 AND WEB_STATUS = 304)
UNION
...etc...
ORDER BY 1 DESC;

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

Отчет EXPLAIN, который я пробовал (с 1 млн строк тестовых данных), показывает, что он хорошо использует индексы и не создает временную таблицу:

+------+--------------+------------------+------+--------------------------+
| id   | select_type  | table            | key  | Extra                    |
+------+--------------+------------------+------+--------------------------+
|  1   | PRIMARY      | RAW_LOG_20100504 | CW   | Using where; Using index |
|  2   | UNION        | RAW_LOG_20100504 | CW   | Using where; Using index |
|  3   | UNION        | RAW_LOG_20100504 | CW   | Using where; Using index |
| NULL | UNION RESULT | <union1,2,3>     | NULL | Using filesort           |
+------+--------------+------------------+------+--------------------------+

Using filesort для последней строки просто означает, что она должна сортироваться без индекса. Но сортировка трех строк, созданных подзапросами, тривиальна, и MySQL делает это в памяти.


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

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


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

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

Индексация — сложная тема. Если вы определите индекс для неправильных столбцов или если вы получите столбцы в неправильном порядке, он может быть неприменим для данного запроса. Я поддерживаю разработчиков SQL с 1994 года и так и не нашел ни одного краткого правила, объясняющего, как проектировать индексы.

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

person Bill Karwin    schedule 04.05.2010
comment
@Bill, COUNT (*) использует индекс покрытия? - person Marcus Adams; 05.05.2010
comment
@ Билл: Извините за это. Я заблудился из-за множества вариантов добавления комментариев. Я попытался использовать ' CREATE INDEX USER ON RAW_LOG_20100503 (MSISDN, BYTES_RETURNED, REQ_SIZE); ', но даже через 41614 секунд он не закончен. Мне пришлось прервать запрос, нажав Ctrl + C. Вы все еще рекомендуете мне использовать индекс? Похоже, индексы на 100Mn+ записях не дают наилучшей производительности. Еще одна вещь: я буду использовать новую таблицу каждый день. Как индексация будет работать в этой ситуации? - person Vijay Gharge; 05.05.2010
comment
@ Билл: Я понимаю, что не будет единого решения, подходящего для всех подходов. Но я хочу понять, как мы можем оптимизировать mysql в моем случае. Поэтому запросите ваши ценные мнения/отзывы, которые помогут мне улучшить производительность mysql db. - person Vijay Gharge; 05.05.2010
comment
@ Билл: Я понял твою точку зрения. Я бы определенно просмотрел документы, относящиеся к индексу, а затем попытался оптимизировать свою базу данных. В любом случае большое спасибо всем за терпение и ответы на понятном языке. - person Vijay Gharge; 06.05.2010

Добавьте индекс к любому полю, которое находится в вашем предложении where. Первичные ключи должны быть уникальными; уникальные индексы должны быть уникальными, но уникальность не является обязательным условием для индекса.

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

Краткая информация:

person Frank V    schedule 04.05.2010
comment
@Frank: Спасибо за ответ. Считаете ли вы, что изменение движка db с myisam на innodb поможет? Единственная причина, по которой движок myisam должен поддерживать более 100 миллионов записей в одной таблице БД. Я бы предпочел выполнять более 1 одновременных запросов к одной и той же таблице, не влияя на другие текущие запросы. - person Vijay Gharge; 05.05.2010
comment
Я не совсем уверен в смене двигателя. Я использую MyISAM сам, но у меня нет таблиц в базе данных MySql, даже близкого к вашему размеру. Итак, я не лучший человек, чтобы ответить на такой вопрос... Тем не менее, я уверен, что вы увидите улучшение, просто добавив несколько индексов... - person Frank V; 05.05.2010
comment
@Билл: Спасибо за ответ. Не могли бы вы уточнить следующее утверждение. Возможно, вам придется прибегнуть к предварительному расчету COUNT(), который вам нужен, и периодически обновлять эту статистику. Пока я добавляю индексы в таблицу, не могли бы вы пролить свет на конфигурацию, присутствующую в my.cnf? Достаточно или чего-то не хватает? @Frank: Спасибо за ответ. Я слежу за ответом Билла и добавляю индексы, чтобы увидеть магию в окончательном выводе. Любой конкретный комментарий относительно конфигурации my.cnf? - person Vijay Gharge; 05.05.2010
comment
@Bill: у меня в этой таблице ~ 20 полей, и каждое из них будет использоваться при создании отчета. Считаете ли вы, что я должен следовать модели индексации для всех 20? Кроме того, приведенный выше пример содержит простой запрос. Мне нужно использовать IN, ›=, ‹=, LIKE, SUBSTR и т. д. для создания необходимого отчета. Не могли бы вы пролить свет на то, как обращаться с ними с большими номерами. записей таблицы? - person Vijay Gharge; 05.05.2010
comment
@Vijay: В будущем, когда вы будете обращаться к конкретному человеку, добавляйте комментарии к соответствующему ответу. Я почти не заметил, что ты меня о чем-то спрашиваешь. - person Bill Karwin; 05.05.2010
comment
Переключение таблицы с MyISAM на InnoDB действительно позволит вам выполнять одновременные операторы SELECT. Это также возможно для таблиц MyISAM, но разрешающие условия более сложны и встречаются редко. - person staticsan; 05.05.2010
comment
@Staticsan - Спасибо за ответ. Обратите внимание, что я имею в виду только 1 таблицу с более чем 100 млн уникальных записей. Пожалуйста, дайте мне знать ваши отзывы. - person Vijay Gharge; 05.05.2010