2009-12-29 5 views
6

EDIT:Procedura przechowywana MySQL powodująca problemy?

mam zawężony mój mysql limit czasu oczekiwania na dół do tej linii:

IF @resultsFound > 0 THEN 
     INSERT INTO product_search_query (QueryText, CategoryId) VALUES (keywords, topLevelCategoryId); 
    END IF; 

Każdy pomysł, dlaczego miałoby to powodować problem? Nie mogę tego rozpracować!

Witajcie, napisałem przechowywany proc, aby wyszukać produkty w pewnych kategoriach, z powodu pewnych ograniczeń, z jakimi się zetknąłem, nie mogłem zrobić tego, co chciałem (ograniczając, ale wciąż zwracając całkowitą liczbę wierszy znalezione, z sortowaniem itp.)

Oznacza to, że dzieli ciąg identyfikatorów kategorii, od 1,2,3 do tabeli tymczasowej, a następnie tworzy zapytanie wyszukiwania pełnotekstowego w oparciu o opcje sortowania i ograniczenia, wykonuje ciąg zapytania, a następnie wybiera całkowitą liczbę wyników.

Wiem, że nie jestem guru MySQL, bardzo daleko od tego, mam to działa, ale mam coraz więcej czasu na szukanie produktu itp .. więc myślę, że to może być przyczyną pewnego rodzaju problemu?

Czy ktoś ma jakieś pomysły, jak mogę to posprzątać, czy nawet zrobić to o wiele lepiej, o czym prawdopodobnie nie wiem?

Dzięki ..

DELIMITER $$ 

DROP PROCEDURE IF EXISTS `product_search` $$ 
CREATE DEFINER=`root`@`localhost` PROCEDURE `product_search`(keywords text, categories text, topLevelCategoryId int, sortOrder int, startOffset int, itemsToReturn int) 
BEGIN 

declare foundPos tinyint unsigned; 
declare tmpTxt text; 
declare delimLen tinyint unsigned; 
declare element text; 
declare resultingNum int unsigned; 

drop temporary table if exists categoryIds; 
create temporary table categoryIds 
(
`CategoryId` int 
) engine = memory; 


set tmpTxt = categories; 

set foundPos = instr(tmpTxt, ','); 
while foundPos <> 0 do 
set element = substring(tmpTxt, 1, foundPos-1); 
set tmpTxt = substring(tmpTxt, foundPos+1); 
set resultingNum = cast(trim(element) as unsigned); 

insert into categoryIds (`CategoryId`) values (resultingNum); 

set foundPos = instr(tmpTxt,','); 
end while; 

if tmpTxt <> '' then 
insert into categoryIds (`CategoryId`) values (tmpTxt); 
end if; 

CASE 
    WHEN sortOrder = 0 THEN 
    SET @sortString = "ProductResult_Relevance DESC"; 
    WHEN sortOrder = 1 THEN 
    SET @sortString = "ProductResult_Price ASC"; 
    WHEN sortOrder = 2 THEN 
    SET @sortString = "ProductResult_Price DESC"; 
    WHEN sortOrder = 3 THEN 
    SET @sortString = "ProductResult_StockStatus ASC"; 
END CASE; 

SET @theSelect = CONCAT(CONCAT(" 
    SELECT SQL_CALC_FOUND_ROWS 
     supplier.SupplierId as Supplier_SupplierId, 
     supplier.Name as Supplier_Name, 
     supplier.ImageName as Supplier_ImageName, 

     product_result.ProductId as ProductResult_ProductId, 
     product_result.SupplierId as ProductResult_SupplierId, 
     product_result.Name as ProductResult_Name, 
     product_result.Description as ProductResult_Description, 
     product_result.ThumbnailUrl as ProductResult_ThumbnailUrl, 
     product_result.Price as ProductResult_Price, 
     product_result.DeliveryPrice as ProductResult_DeliveryPrice, 
     product_result.StockStatus as ProductResult_StockStatus, 
     product_result.TrackUrl as ProductResult_TrackUrl, 
     product_result.LastUpdated as ProductResult_LastUpdated, 

     MATCH(product_result.Name) AGAINST(?) AS ProductResult_Relevance 
    FROM 
     product_latest_state product_result 
    JOIN 
     supplier ON product_result.SupplierId = supplier.SupplierId 
    JOIN 
     category_product ON product_result.ProductId = category_product.ProductId 
    WHERE 
     MATCH(product_result.Name) AGAINST (?) 
    AND 
     category_product.CategoryId IN (select CategoryId from categoryIds) 
    ORDER BY 
     ", @sortString), " 
    LIMIT ?, ?; 
    "); 

    set @keywords = keywords; 
    set @startOffset = startOffset; 
    set @itemsToReturn = itemsToReturn; 

    PREPARE TheSelect FROM @theSelect; 
    EXECUTE TheSelect USING @keywords, @keywords, @startOffset, @itemsToReturn; 

    SET @resultsFound = FOUND_ROWS(); 

    SELECT @resultsFound as 'TotalResults'; 

    IF @resultsFound > 0 THEN 
     INSERT INTO product_search_query (QueryText, CategoryId) VALUES (keywords, topLevelCategoryId); 
    END IF; 

END $$ 

DELIMITER ; 

Każda pomoc jest bardzo bardzo cenione!

+1

Czy można uzyskać DESIGNIBE TABLE dla tabel używanych w tym? Myślę, że możemy zmniejszyć komplikacje i ewentualnie przyspieszyć to, gdybym miał więcej oczu na własnych stołach. –

+0

I drugi komentarz kevins. za mało, aby kontynuować. – DeveloperChris

Odpowiedz

4

Za pomocą tego zapytania niewiele można zrobić.

Spróbuj tego:

  1. Tworzenie PRIMARY KEY na categoryIds (categoryId)

    • Upewnij się, że supplier (supplied_id) jest PRIMARY KEY

    • Upewnij się, że category_product (ProductID, CategoryID) (w tej kolejności) to PRIMARY KEY lub masz indeks z wiodącymi ProductID.

Aktualizacja:

Jeśli to INSERT że przyczyną problemu i product_search_query w MyISAM stole problem może być z MyISAM ryglowania.

MyISAM blokuje cały stół, jeśli zdecyduje się wstawić wiersz do wolnego bloku na środku stołu, co może spowodować przekroczenie limitu czasu.

Spróbuj użyć INSERT DELAYED zamiast:

IF @resultsFound > 0 THEN 
    INSERT DELAYED INTO product_search_query (QueryText, CategoryId) VALUES (keywords, topLevelCategoryId); 
END IF; 

ten wprowadzi zapisy do kolejki wkładania i wrócić natychmiast. Rekord zostanie dodany później asynchronicznie.

Należy pamiętać, że możesz utracić informacje, jeśli serwer zginie po wydaniu polecenia, ale przed faktycznym wstawieniem rekordów.

Aktualizacja:

Ponieważ tabela jest InnoDB, może to być problem z blokowaniem tabeli. INSERT DELAYED nie jest obsługiwany na InnoDB.

W zależności od charakteru zapytania, zapytania o numer DML mogą być umieszczane na stole InnoDB, co może powodować blokadę wstawek.

Na przykład:

CREATE TABLE t_lock (id INT NOT NULL PRIMARY KEY, val INT NOT NULL) ENGINE=InnoDB; 
INSERT 
INTO t_lock 
VALUES 
     (1, 1), 
     (2, 2); 

To zapytanie wykonuje ref skany i umieszcza zamki w poszczególnych rekordów:

-- Session 1 
START TRANSACTION; 
UPDATE t_lock 
SET  val = 3 
WHERE id IN (1, 2) 

-- Session 2 
START TRANSACTION; 
INSERT 
INTO t_lock 
VALUES (3, 3) 
-- Success 

To zapytanie, robiąc to samo, wykonuje range skanowania i umieszcza lukę blokada po wartości klucza 2, która nie zezwala na wstawienie wartości klucza 3:

-- Session 1 
START TRANSACTION; 
UPDATE t_lock 
SET  val = 3 
WHERE id BETWEEN 1 AND 2 

-- Session 2 
START TRANSACTION; 
INSERT 
INTO t_lock 
VALUES (3, 3) 
-- Locks 
+0

Dziękuję za odpowiedź, nie działa to jednak powoli. Po prostu wydaje mi się, że robię to raz na jakiś czas, po prostu uruchomiłem to i zajęło 0,0014, więc nie ma problemu z rzeczywistą prędkością? –

+0

To będzie działać powoli, jeśli będziesz szukać często występujących słów kluczowych. Powiedz, że jeśli wyszukasz słowo kluczowe "profesjonalny" i "1 000 000" produktów zawierających to słowo kluczowe, silnik będzie musiał pobrać wszystkie te rekordy i posortować je według kolejności sortowania. Można to poprawić za pomocą zwykłego predykatu (nie pełnego tekstu), tworząc indeks złożony, ale niestety 'MySQL' nie pozwala na mieszanie pełnotekstowych i nie-pełnotekstowych kluczy w jednym indeksie. – Quassnoi

+0

Widzę, cóż, dla moich celów w tej chwili, myślę, że ogólna szybkość tego nie będzie problemem. Dzięki –

0

Włącz powolne zapytania, które dadzą ci pojęcie o tym, co trwa tak długo, aż upłynie limit czasu.

http://dev.mysql.com/doc/refman/5.1/en/slow-query-log.html

Pick najwolniejszą zapytanie i optymalizacji tego. następnie biegnij przez chwilę i powtórz.

Istnieją pewne doskonałe informacje i narzędzia tutaj http://hackmysql.com/nontech

DC

UPDATE:

Albo masz problem z siecią powoduje limit czasu, jeśli używasz lokalnej instancji mysql wtedy, że jest mało prawdopodobne, , LUB coś blokuje tabelę zbyt długo, powodując przekroczenie limitu czasu. proces, który blokuje tabelę lub tabele zbyt długo, będzie wyświetlany w powolnym logu jako powolne zapytanie. można również uzyskać powolne zapytanie dziennika, aby wyświetlić dowolne zapytania, które nie mogą korzystać z indeksu, powodując nieefektywne zapytanie.

Jeśli możesz napotkać problem, gdy jesteś obecny, możesz również użyć narzędzia, takiego jak phpmyadmin, lub wiersza poleceń, aby uruchomić "POKAŻ LICENCJĘ PROCESOWĄ \ G", co da ci listę zapytań, które są uruchomione, a problem występuje.

Uważasz, że problem występuje w instrukcji wstawiania, dlatego coś blokuje tę tabelę. dlatego musisz znaleźć to, co blokuje tę tabelę, dlatego musisz znaleźć to, co działa tak wolno, że blokowanie tabeli jest zbyt długie. Powolne pytania to jeden ze sposobów na zrobienie tego.

Inne rzeczy patrzeć

CPU - to jest bezczynny lub działa na pełnych futro

IO - jest io powodujących stopni zatrzymania

RAM - czy wymieniających się cały czas (spowoduje nadmierne IO)

Czy tabela product_search_query używa indeksu?

Jaki jest klucz podstawowy?

Jeśli twój indeks używa łańcuchów, które są zbyt długie? możesz zbudować ogromny plik indeksu, który powoduje bardzo powolne wstawianie (powolny dziennik zapytań również to pokazuje)

I tak, problem może być gdzie indziej, ale musisz gdzieś zacząć, nie możesz.

DC

+0

Po raz kolejny nie ma problemów z prędkością, innych niż limit czasu, kiedy to działa, działa z bardzo rozsądną prędkością. Limit czasu jest spowodowany przez blokowanie, a nie rzeczywisty problem z szybkością. –

+0

Zobacz aktualizację powyżej – DeveloperChris

0

Spróbuj owijając swoje EXECUTE z poniższym:

SET SESSION TRANSAKCJI izolacji LEVEL READ UNCOMMITTED;

WYKONAJ Wybranie USING za pomocą słów @, słowa kluczowe, @startOffset, @itemsToReturn;

USTAWIĆ POZIOM IZOLACJI TRANSAKCJI SESJA POWTÓRZYĆ READ;

Zrobić coś podobnego w TSQL dla wszystkich raportów przechowywanych proc i wyszukiwania, w których powtarzalne odczyty nie są ważne, aby zmniejszyć problemy blokowania/blokowania z innymi procesami uruchomionymi w bazie danych.