2012-02-09 10 views
10

Mam tabelę Joomla z tysiącami wierszy treści (około 3 milionów). Mam problem przy przepisywaniu zapytań do bazy danych tak szybko, jak to możliwe podczas wysyłania zapytań do tabel.Złożona kwerenda MySQL wciąż używająca filesort chociaż indeksy istnieją

Oto mój pełny zapytania:

SELECT cc.title AS category, a.id, a.title, a.alias, a.title_alias, a.introtext, a.fulltext, a.sectionid, a.state, a.catid, a.created, a.created_by, a.created_by_alias, a.modified, a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, a.attribs, a.hits, a.images, a.urls, a.ordering, a.metakey, a.metadesc, a.access, CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH(a.`fulltext`) AS readmore, u.name AS author, u.usertype, g.name AS groups, u.email AS author_email 
FROM j15_content AS a 
LEFT JOIN j15_categories AS cc 
ON a.catid = cc.id 
LEFT JOIN j15_users AS u 
ON u.id = a.created_by 
LEFT JOIN j15_groups AS g 
ON a.access = g.id 
WHERE 1 
AND a.access <= 0 
AND a.catid = 108 
AND a.state = 1 
AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 00:16:26') 
AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-02-08 00:16:26') 
ORDER BY a.title, a.created DESC 
LIMIT 0, 10 

Oto wynik od wyjaśniania:

+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+ 
| id | select_type | table | type | possible_keys           | key  | key_len | ref      | rows | Extra      | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+ 
| 1 | SIMPLE  | a  | ref | idx_access,idx_state,idx_catid,idx_access_state_catid | idx_catid | 4  | const      | 3108187 | Using where; Using filesort | 
| 1 | SIMPLE  | cc | const | PRIMARY            | PRIMARY | 4  | const      |  1 |        | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY            | PRIMARY | 4  | database.a.created_by  |  1 |        | 
| 1 | SIMPLE  | g  | eq_ref | PRIMARY            | PRIMARY | 1  | database.a.access   |  1 |        | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-----------------------------+ 

i pokazać co istnieją indeksy, pokaż indeks z j15_content:

+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| Table  | Non_unique | Key_name    | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | 
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 
| j15_content |   0 | PRIMARY    |   1 | id   | A   |  3228356 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_section   |   1 | sectionid | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access    |   1 | access  | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_checkout   |   1 | checked_out | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_state    |   1 | state  | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_catid    |   1 | catid  | A   |   6 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_createdby   |   1 | created_by | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | title     |   1 | title  | A   |  201772 |  4 | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access_state_catid |   1 | access  | A   |   1 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access_state_catid |   2 | state  | A   |   2 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_access_state_catid |   3 | catid  | A   |   7 |  NULL | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_title_created  |   1 | title  | A   |  3228356 |  8 | NULL |  | BTREE  |   | 
| j15_content |   1 | idx_title_created  |   2 | created  | A   |  3228356 |  NULL | NULL |  | BTREE  |   | 
+-------------+------------+------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+ 

Jak widać, z bazy danych pobieranych jest kilka danych. Teraz testowałem, upraszczając zapytanie, że prawdziwy problem leży w klauzuli ORDER BY. Bez zamawiania wyników, zapytanie jest bardzo czuły, tutaj jest wyjaśnienie:

+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+ 
| id | select_type | table | type | possible_keys           | key  | key_len | ref      | rows | Extra  | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+ 
| 1 | SIMPLE  | a  | ref | idx_access,idx_state,idx_catid,idx_access_state_catid | idx_catid | 4  | const      | 3108187 | Using where | 
| 1 | SIMPLE  | cc | const | PRIMARY            | PRIMARY | 4  | const      |  1 |    | 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY            | PRIMARY | 4  | database.a.created_by  |  1 |    | 
| 1 | SIMPLE  | g  | eq_ref | PRIMARY            | PRIMARY | 1  | database.a.access   |  1 |    | 
+----+-------------+-------+--------+-------------------------------------------------------+-----------+---------+---------------------------+---------+-------------+ 

Jak widać jest to śmiertelne filesort że zabija serwer. Przy tak wielu wierszach robię co w mojej mocy, aby zoptymalizować wszystko za pomocą indeksów, ale coś jest z tym nie w porządku. Wszelkie dane wejściowe będą bardzo mile widziane.

spróbował wykorzystać FORCE INDEX bezskutecznie:

explain  SELECT cc.title AS category, a.id, a.title, a.alias, a.title_alias, a.introtext, a.fulltext, a.sectionid, a.state, a.catid, a.created, a.created_by, a.created_by_alias, a.modified, a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, a.attribs, a.hits, a.images, a.urls, a.ordering, a.metakey, a.metadesc, a.access, CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH(a.`fulltext`) AS readmore, u.name AS author, u.usertype, g.name AS groups, u.email AS author_email 
    ->  FROM bak_content AS a 
    ->  FORCE INDEX (idx_title_created) 
    ->  LEFT JOIN bak_categories AS cc 
    ->  ON a.catid = cc.id 
    ->  LEFT JOIN bak_users AS u 
    ->  ON u.id = a.created_by 
    ->  LEFT JOIN bak_groups AS g 
    ->  ON a.access = g.id 
    ->  WHERE 1 
    ->  AND a.access <= 0 
    ->  AND a.catid = 108 
    ->  AND a.state = 1 
    ->  AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 
    ->  AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-0 
    ->  ORDER BY a.title, a.created DESC 
    ->  LIMIT 0, 10; 

Produkuje:

+----+-------------+-------+--------+---------------+---------+---------+------- 
| id | select_type | table | type | possible_keys | key  | key_len | ref 
+----+-------------+-------+--------+---------------+---------+---------+------- 
| 1 | SIMPLE  | a  | ALL | NULL   | NULL | NULL | NULL 
| 1 | SIMPLE  | cc | const | PRIMARY  | PRIMARY | 4  | const 
| 1 | SIMPLE  | u  | eq_ref | PRIMARY  | PRIMARY | 4  | database 
| 1 | SIMPLE  | g  | eq_ref | PRIMARY  | PRIMARY | 1  | database 
+----+-------------+-------+--------+---------------+---------+---------+------- 
+0

Spróbowałbym jednego z tych indeksów: '(stan, catid, dostęp)' lub '(stan, catid, publish_up)' lub '(stan, catid, publish_down)' –

+0

Jeśli nie używasz 'LIMIT' wiele wierszy jest zwracanych? –

+0

Można również spróbować wymusić użycie indeksu 'idx_title_created'. –

Odpowiedz

0

Czasami MySQL ma problemy ze znalezieniem odpowiedniego indeksu. Możesz rozwiązać ten problem, podpowiadając odpowiedni indeks.

Podpowiedź składnia: http://dev.mysql.com/doc/refman/4.1/en/index-hints.html

Upewnij się, że masz prawo indeksu i melodię To wykonanie przez eksperymentowanie.

Pozdrawiam!

+0

Próbowałem już wymusić indeks, ale to nie rozwiązuje problemu. Wygląda na to, że prawdopodobnie zindeksowałem niewłaściwe kolumny, ale nie jestem pewien, które z nich powinny/nie powinny znajdować się w indeksie. Zakładam, że ze względu na naturę zapytania wymagany jest indeks złożony. – user1199057

0

możesz spróbować tej odmiany:

SELECT cc.title AS category, ... 
FROM 
    (SELECT * 
     FROM j15_content AS a 
       USE INDEX (title)    --- with and without the hint 
     WHERE 1 
     AND a.access <= 0 
     AND a.catid = 108 
     AND a.state = 1 
     AND (publish_up = '0000-00-00 00:00:00' 
      OR publish_up <= '2012-02-08 00:16:26') 
     AND (publish_down = '0000-00-00 00:00:00' 
      OR publish_down >= '2012-02-08 00:16:26') 
     ORDER BY a.title, a.created DESC 
     LIMIT 0, 10 
    ) AS a 
    LEFT JOIN j15_categories AS cc 
    ON a.catid = cc.id 
    LEFT JOIN j15_users AS u 
    ON u.id = a.created_by 
    LEFT JOIN j15_groups AS g 
    ON a.access = g.id 

indeksu na (catid, state, title) byłoby jeszcze lepiej, myślę.

+0

Dzięki, spróbuję krótko i skontaktuję się z Tobą. Jaki byłby wpływ indeksu na (title, catid, id) (w tej kolejności), aby wyniki zostały już posortowane według tytułu.Zastanawiam się nad podzieleniem tego na 2 pytania, jeśli mogę ewentualnie wyciągnąć proste identyfikatory artykułów, a następnie wykonać osobne zapytanie, które następnie zwróci wszystkie powiązane informacje o artykule, używając gdzie id IN (1, 2, 3 itd.) – user1199057

+0

Zapytanie wciąż wraca z: "# 1028 - Sort aborted" niestety. – user1199057

0

Być może próbuje to może pomóc:

CREATE INDEX idx_catid_title_created ON j15_content (catid,title(8),created); 
DROP INDEX idx_catid ON j15_content; 
5

AFAIK to nie można racjonalnie rozwiązać za pomocą indeksu, wskazówki lub restrukturyzację samego zapytania.

Powodem, dla którego jest to powolne, jest fakt, że wymaga on pliku z 2 milionami wierszy, co zajmuje dużo czasu. Jeśli powiększysz kolejność, określając ją jako ORDER BY a.title, a.created DESC. Problem polega na połączeniu sortowania na więcej niż jednej kolumnie i posiadaniu części DESC. Mysql nie obsługuje malejących indeksów (słowo kluczowe DESC jest obsługiwane w CREATE INDEX statement, ale tylko w przyszłości).

Sugerowane obejście polega na utworzeniu dodatkowej kolumny "reverse_created", która zostanie automatycznie wypełniona w taki sposób, aby zapytanie mogło korzystać z ORDER BY a.title, a.reverse_created. Wypełniasz go więc max_time - created_time.Następnie utwórz indeks tej kombinacji i (jeśli to konieczne) określ indeks jako wskazówkę.

Istnieje kilka naprawdę dobrych artykułów na blogu o tym temacie, które wyjaśniają to dużo lepiej iz przykładami:

-Update- Powinieneś być w stanie zrobić szybki test na tym, usuwając część "DESC" z zamówienia przez zapytanie. Wyniki będą funkcjonalnie nieprawidłowe, ale powinny korzystać z istniejącego indeksu, który posiadasz (lub w przeciwnym razie siła powinna działać).

0

Czy próbowałeś zwiększenie wartości te tmp_table_size i max_heap_table_size:

Jest krótkie wyjaśnienie here a także linki do szczegółowych informacji na temat każdego z nich.

Mam nadzieję, że to pomoże!

0

Mam nadzieję, że to jest poprawny składniowo

SELECT 
    cc.title AS category, 
    a.id, a.title, a.alias, a.title_alias, 
    a.introtext, a.fulltext, a.sectionid, 
    a.state, a.catid, a.created, a.created_by, 
    a.created_by_alias, a.modified, a.modified_by, 
    a.checked_out, a.checked_out_time, 
    a.publish_up, a.publish_down, a.attribs, 
    a.hits, a.images, a.urls, a.ordering, a.metakey, 
    a.metadesc, a.access, 
    CASE WHEN CHAR_LENGTH(a.alias) THEN CONCAT_WS(":", a.id, a.alias) ELSE a.id END AS slug, 
    CASE WHEN CHAR_LENGTH(cc.alias) THEN CONCAT_WS(":", cc.id, cc.alias) ELSE cc.id END AS catslug, CHAR_LENGTH(a.`fulltext`) AS readmore, 
    u.name AS author, u.usertype, g.name AS groups, u.email AS author_email 
FROM 
(
    SELECT aa.* 
    FROM 
    (
     SELECT id FROM 
     FROM j15_content 
     WHERE catid=108 AND state=1 
     AND a.access <= 0 
     AND (publish_up = '0000-00-00 00:00:00' OR publish_up <= '2012-02-08 00:16:26') 
     AND (publish_down = '0000-00-00 00:00:00' OR publish_down >= '2012-02-08 00:16:26') 
     ORDER BY title,created DESC 
     LIMIT 0,10 
    ) needed_keys 
    LEFT JOIN j15_content aa USING (id) 
) a 
LEFT JOIN j15_categories AS cc ON a.catid = cc.id 
LEFT JOIN j15_users AS u ON a.created_by = u.id 
LEFT JOIN j15_groups AS g ON a.access = g.id; 

Musisz indeks nośną dla needed_keys podzapytaniu

ALTER TABLE j15_content ADD INDEX subquery_ndx (catid,state,access,title,created); 

spróbować !!!

Powiązane problemy