2011-01-18 3 views
1

Попытка запускать следующий запрос в таблице mysql, которая содержит более 3 миллионов строк. Его очень медленный до такой степени, что он довольно сильно зависает, пока сценарий не истечет. Ниже приведен запрос и пояснения из этого запроса, какие-либо предложения?Mysql Query Slow on large table

SELECT SQL_CALC_FOUND_ROWS 
listing_track.listingid, 
listing_track.commid, 
listing.listingname, 
listing_package.packagename, 
listing.active, 
community.commname, 
SUM(listing_track.impression) AS listing_impressions, 
SUM(listing_track.view) AS listing_views, 
SUM(listing_track.phone) AS listing_phones, 
SUM(listing_track.forward) AS listing_forward, 
SUM(listing_track.coupon) AS listing_coupons, 
SUM(listing_track.email) AS listing_emails 
FROM listing_track 
INNER JOIN listing ON listing_track.listingid = listing.id 
INNER JOIN community ON listing_track.commid = community.id 
INNER JOIN listing_package ON listing.packageid = listing_package.id 
WHERE listing_track.commid =2 
GROUP BY listing_track.commid, listing_track.listingid, listing_track.trackip 
LIMIT 0 , 25 

Вот объясните: alt text

+0

Просто подумайте об этом, чтобы найти нужные данные, чтобы проверить более трех миллионов строк. Это займет время, даже с современными алгоритмами MySQL. –

+0

@Cobra_Fast Я понимаю это, но задавался вопросом, почему его хватало более миллиона строк, когда у меня есть предел. Возможно, я не понимаю mysql и как он работает в этом отношении? – John

+0

он захватывает миллионы строк, потому что, помимо прочего, вы используете 'SQL_CALC_FOUND_ROWS', который дает вам количество записей, возвращаемых запросом, без предложения' LIMIT'. Конечно, им следует подсчитать, сколько их там. – Quassnoi

ответ

0

Проблема заключается в том, что LIMIT применяется в конце запроса, после того как все дорогостоящие сканированные таблицы завершены. Стоимость заключается не в том, чтобы возвращать много строк, а вместо того, чтобы проверять множество строк.

Самый простой способ ускорения запросов, подобных этому, - covering index. Это позволит вам сканировать нужные строки, но требует меньше байтов ввода-вывода для каждой строки (поскольку вы просматриваете только часть данных каждой строки, а не всю строку). Кроме того, если ваш индекс сортируется так же, как и ваш запрос, вы можете избежать стоимости сортировки и можете сканировать значительно меньшее количество строк.

Ваш индекс должен иметь следующие столбцы. Первые три столбца должны быть в том же порядке, что и ваша GROUP BY. Это позволяет делать ваши GROUP BY и WHERE намного дешевле. Вторая строка позволяет индекс «покрыть» запрос, что означает, что MySQL будет в состоянии удовлетворить весь listing_track часть запроса только из индекса:

CREATE INDEX ix_listing_track_covering ON listing_track (
    commid, listingid, trackip, 
    listing_impression, listing_view, listing_phone, listing_forward, listing_coupon, listing_email); 

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

+0

В 'MySQL',' GROUP BY' также определяет 'ORDER BY'. Это расширение стандарта. – Quassnoi

+0

@Quassnoi прав - я забыл об этом причуде MySQL. Я только что отредактировал свой ответ, чтобы удалить жалобу о недостающем ORDER BY. Спасибо за напоминание! –

+0

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

0
SELECT listing_track.listingid, 
     listing_track.commid, 
     listing.listingname, 
     listing_package.packagename, 
     listing.active, 
     community.commname 
     SUM(listing_track.impression) AS listing_impressions, 
     SUM(listing_track.view) AS listing_views, 
     SUM(listing_track.phone) AS listing_phones, 
     SUM(listing_track.forward) AS listing_forward, 
     SUM(listing_track.coupon) AS listing_coupons, 
     SUM(listing_track.email) AS listing_emails 
FROM (
     SELECT * 
     FROM listing 
     ORDER BY 
       id 
     LIMIT 25 
     ) l 
JOIN listing_track lt 
ON  lt.listingid = l.id 
     AND lt.commid = 2 
JOIN community c 
ON  c.id = lt.commid 
JOIN listing_package lp 
ON  lp.packageid = l.packageid 
GROUP BY 
     l.id, lt.trackip 
LIMIT 25 

Создание составного индекса на listing_track (listingid, commid)

Эта оптимизация работает только без SQL_CALC_FOUND_ROWS, так как в последнем случае требуется сканировать все записи (как если запрос был запущен без LIMIT).

В вашем плане я вижу еще одну таблицу, account, которая не используется в запросе. Вам нужно опубликовать весь запрос, поскольку даже одна дополнительная таблица может изменить все.

+0

вложенные лимиты? Попытка получить что-либо кроме случайной выборки из этого запроса будет трудной – symcbean

+0

@symcbean: да, вложенные пределы. Пожалуйста, сообщите, что именно не так с этим запросом, по вашему мнению. – Quassnoi

0

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

ВЫБОР STRAIGHT_JOIN SQL_CALC_FOUND_ROWS ...

Это говорит MySQL, чтобы сделать в порядке, вы говорите это. У меня было сходство с данными gov't из 14 + миллионов строк, связанных с более чем 15 поисковыми таблицами (объединениями). MySQL пытался использовать меньшие таблицы поиска в качестве основы для присоединения, поскольку они были меньше и, таким образом, зависели от процесса (я имею в виду более 30 часов, прежде чем я его убил). Добавляя STRAIGHT_JOIN, так как вы ЗНАЕТЕ, что ваш LISTING_TRACK является основой всего, а ДРУГИЕ таблицы являются вторичными ссылками, он должен летать, хотя это намного быстрее. Это, как говорится, будет по-прежнему иметь некоторую работу, чтобы сделать более 1 миллиона строк в вашем столе, но, очевидно, более ограничено вашим столбцом COMMID = 2.

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

BTW, мой запрос gov't после добавления STRAIGHT_JOIN пробежал менее 2 часов.

НТН

0

Не зная больше о структуре данных его невозможно сказать, однако, учитывая имеющийся informnation, система, кажется, делает разумную попытку запроса. Если вы действительно хотите обрабатывать миллионы строк, я выбираю один, это займет время. Однако если отношения строго 1: N можно легко уронить поиск в других таблицах - и просто подсчитать записи в таблице listng без изменения результатов:

SELECT SQL_CALC_FOUND_ROWS * 
FROM listing 
WHERE commid=2 

Какова цель запроса, который возвращает миллион строк?

Возможно, существует определенная возможность для настройки СУБД - в частности, буфера соединения - попробуйте запустить mysqltuner против него.

Этот план, похоже, предполагает, что единственная фильтрация, которую вы применили (listing_track.commid = 2), лучше всего обслуживается поиском индекса в таблице, возвращающейся в область миллионов строк - если есть только 3 миллиона строк в базе данных, то сканирование полной таблицы, вероятно, будет быстрее.

+0

Как запрос с 'LIMIT 25' возвращает миллион строк? – Quassnoi