2012-10-23 26 views
8

to pytanie typu "wykonaj moją pracę domową dla mnie", ale naprawdę utknąłem tutaj, próbując szybko wykonać to zapytanie względem tabeli z wieloma wierszami. Here's a SQLFiddle, który pokazuje schemat (mniej więcej).Zoptymalizuj zapytanie do tabeli z setkami milionów wierszy

Grałem z indeksami, próbując uzyskać coś, co pokaże wszystkie wymagane kolumny, ale nie odniosło dużego sukcesu. Oto create:

CREATE TABLE `AuditEvent` (
    `auditEventId` bigint(20) NOT NULL AUTO_INCREMENT, 
    `eventTime` datetime NOT NULL, 
    `target1Id` int(11) DEFAULT NULL, 
    `target1Name` varchar(100) DEFAULT NULL, 
    `target2Id` int(11) DEFAULT NULL, 
    `target2Name` varchar(100) DEFAULT NULL, 
    `clientId` int(11) NOT NULL DEFAULT '1', 
    `type` int(11) not null, 
    PRIMARY KEY (`auditEventId`), 
    KEY `Transactions` (`clientId`,`eventTime`,`target1Id`,`type`), 
    KEY `TransactionsJoin` (`auditEventId`, `clientId`,`eventTime`,`target1Id`,`type`) 
) 

I (wersja) select:

select ae.target1Id, ae.type, count(*) 
from AuditEvent ae 
where ae.clientId=4 
    and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
group by ae.target1Id, ae.type; 

I skończyć z 'używanie tymczasowego' i 'Korzystanie filesort' a. Próbowałem zamiast tego usunąć count(*) i zamiast tego użyć select distinct, co nie powoduje "Używanie filesort". Prawdopodobnie byłoby to w porządku, gdyby istniał sposób na odzyskanie liczby od join.

Pierwotnie podjęto decyzję o śledzeniu celów target1Name i target2Name celów, które istniały podczas tworzenia rekordu kontroli. Też potrzebuję tych imion (zrobię to ostatnio).

Obecnie zapytanie (powyżej, z brakującymi kolumnami target1Name i target2Name) działa w około 5 sekund przy rekordach ~ 24 miliony. Nasz cel jest w setkach milionów i chcielibyśmy, aby zapytanie dalej działało zgodnie z tymi zasadami (mając nadzieję, że uda nam się je zachować w ciągu 1-2 minut, ale chcielibyśmy, aby było o wiele lepiej), ale mój strach jest raz trafimy na większą ilość danych, których nie będzie (praca nad symulacją dodatkowych wierszy jest w toku).

Nie jestem pewien najlepszej strategii, aby uzyskać dodatkowe pola. Jeśli dodaję kolumny bezpośrednio do select, tracę wskaźnik "Używanie indeksu" w zapytaniu. Próbowałem z powrotem do tabeli join, która zachowuje "Indeks użycia", ale zajmuje około 20 sekund.

Próbowałem zmienić kolumnę eventTime na int, a nie na datetime, ale nie miało to wpływu na użycie indeksu lub czas.

+0

Jaki jest Twój obecny moment kwerendy i co rozumiesz pod "szybko"? – feeela

+0

Przepraszamy, dodano te szczegóły –

+0

Czy masz indeksy na clientId i eventTime? Sprawdź również, czy masz te, które używają wskaźnika eventTime i nie wykonujesz pełnego skanowania tabeli. –

Odpowiedz

3

Jak zapewne zrozumieć, tutaj problemem jest stan Zakres ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00' który (jak zawsze) łamie efektywne wykorzystanie Transactions indeksu (czyli indeks jest rzeczywiście używany tylko do clientId równania i pierwszej części stanu zakresie oraz indeks nie jest używany do grupowania).

Najczęściej rozwiązaniem jest zastąpienie warunku przedziału z czekiem równości (w przypadku wprowadzenia period kolumny, grupę eventTime na okresy i zastąpić klauzulę BETWEEN z period IN (1,2,3,4,5)). Ale może to stać się przyczyną twojego stolika.

Innym rozwiązaniem, które można spróbować jest dodanie innego indeksu (prawdopodobnie zastąpi Transactions jeśli nie jest już używany): (clientId, target1Id, type, eventTime) i użyć następującego zapytania:

SELECT 
    ae.target1Id, 
    ae.type, 
    COUNT(
    NULLIF(ae.eventTime BETWEEN '2011-09-01 03:00:00' 
          AND '2012-09-30 23:57:00', 0) 
) as cnt, 
FROM AuditEvent ae 
WHERE ae.clientId=4 
GROUP BY ae.target1Id, ae.type; 

ten sposób będziesz a) ruch stan zasięg do końca, b) umożliwienia korzystania indeks grupy, c) sprawiają, że indeks obejmujące indeks dla zapytania (to zapytanie nie potrzebuje dyskowych operacji IO)

UPD1: Przykro mi, ale nie przeczytałem dokładnie tego posta i nie zauważyłem, że masz problem z odzyskaniem target1Name i target2Name. Przede wszystkim nie jestem pewien, czy poprawnie rozumiesz znaczenie Using index. Brak Using index nie oznacza, że ​​do zapytania nie jest używany indeks, Using index oznacza, że ​​sam indeks zawiera wystarczającą ilość danych, aby wykonać podzapytanie (czyli indeks pokrywa). Od target1Name i target2Name nie są zawarte w żadnym indeksie, podzapytanie, które je pobiera, nie będzie miało Using index.

Jeśli pytanie brzmi, jak dodać te dwa pola do zapytania (które można rozważyć wystarczająco szybko), a potem po prostu spróbuj wykonać następujące czynności:

SELECT a1.target1Id, a1.type, cnt, target1Name, target2Name 
FROM (
    select ae.target1Id, ae.type, count(*) as cnt, MAX(auditEventId) as max_id 
    from AuditEvent ae 
    where ae.clientId=4 
     and (ae.eventTime between '2011-09-01 03:00:00' and '2012-09-30 23:57:00') 
    group by ae.target1Id, ae.type) as a1 
JOIN AuditEvent a2 ON a1.max_id = a2.auditEventId 
; 
+0

Oba są właściwie poprawnymi odpowiedziami; Chciałem zwiększyć wydajność zapytania/uzyskać porady na temat różnych sposobów jego struktury, a ponadto zastanawiałem się, jak najlepiej odzyskać nieindeksowane kolumny. Obie Twoje sugestie poprawiły wydajność w porównaniu do zapytań, które wypróbowałem! –

+0

@nickSpacek, ok, cieszę się, że pomogło =) – newtover

Powiązane problemy