2009-12-29 13 views
6

EDIT:La procedura memorizzata MySQL sta causando problemi?

ho ristretto la mia mysql timeout di attesa fino a questa linea:

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

Qualsiasi idea del perché questo causerebbe un problema? Non riesco a risolverlo!

Ciao ragazzi, ho scritto un proc memorizzato per cercare prodotti in determinate categorie, a causa di alcuni vincoli che ho incontrato, non ero in grado di fare ciò che volevo (limitando, ma pur continuando a restituire il numero totale di righe trovato, con smistamento, ecc.)

Si intende dividere una stringa di ID di categoria, da 1,2,3 in una tabella temporanea, quindi creare la query di ricerca full-text basata su opzioni di ordinamento e limiti, esegue la stringa di query e quindi seleziona il numero totale di risultati.

Ora, so che non sono un guru di MySQL, molto lontano da esso, ho funzionato, ma continuo a ottenere timeout con ricerche di prodotti, ecc. Quindi sto pensando che questo potrebbe causare qualche tipo di problema?

Qualcuno ha qualche idea su come riordinarlo, o addirittura farlo in un modo molto migliore di cui probabilmente non so nulla?

Grazie ..

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 ; 

Qualsiasi aiuto è molto molto apprezzato!

+1

Sarebbe possibile ottenere una TABELLA DESCRIVERE per le tabelle utilizzate in questo? Penso che possiamo ridurre la complicazione e, possibilmente, accelerarla se avessi altri occhi sul tavolo stesso. –

+0

I secondo commento di kevins. troppo poco per andare avanti – DeveloperChris

risposta

4

C'è poco che puoi fare con questa query.

Prova questo:

  1. Creare un PRIMARY KEY su categoryIds (categoryId)

    • Assicurarsi che supplier (supplied_id) è un PRIMARY KEY

    • Assicurarsi che category_product (ProductID, CategoryID) (in questo ordine) è un PRIMARY KEY , o hai un indice con ProductID leader.

Aggiornamento:

Se è INSERT che causa il problema e product_search_query in una tabella MyISAM il problema può essere con MyISAM bloccaggio.

MyISAM blocca l'intera tabella se decide di inserire una riga in un blocco libero nel mezzo della tabella che può causare i timeout.

Provare a utilizzare INSERT DELAYED invece:

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

Questo metterà i record nella coda inserimento e restituire immediatamente. Il record verrà aggiunto in seguito in modo asincrono.

Nota che potresti perdere informazioni se il server muore dopo che il comando è stato emesso ma prima che i record siano effettivamente inseriti.

Aggiornamento:

Dal momento che il tavolo è InnoDB, può essere un problema con bloccaggio tavolo. INSERT DELAYED non è supportato su InnoDB.

A seconda della natura della query, le query DML nella tabella InnoDB possono inserire blocchi di separazione che bloccano gli inserti.

Ad esempio:

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); 

Questa interrogazione esegui ref scansioni e pone le serrature singoli record:

-- 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 

Questa query, mentre si fa lo stesso, esegue una scansione range e pone un gap blocca dopo il valore chiave 2, che non consente di inserire il valore chiave 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

Grazie per la tua risposta, in realtà non sta funzionando lentamente però ... Sembra proprio che lo faccia una volta ogni tanto, l'ho appena eseguito e ci sono voluti 0.0014s, quindi nessun problema con la velocità effettiva? –

+0

Si avvierà lentamente se cercherete le parole chiave che si verificano frequentemente. Supponiamo che, se cerchi la parola chiave '' professional'' e '1,000,000' prodotti contengono questa parola chiave, il motore dovrà recuperare tutti questi record e ordinarli in base al tuo ordinamento. Questo potrebbe essere migliorato con un normale predicato (non fulltext) creando un indice composito, ma, sfortunatamente, 'MySQL' non consente di mischiare chiavi di testo completo e non di testo completo in un indice. – Quassnoi

+0

Vedo, beh, per i miei scopi al momento, penso che la velocità generale di questo non sarà un problema. Grazie –

0

Attivare query lente, che ti daranno un'idea di cosa ci vuole così tanto tempo per eseguire che ci sia un timeout.

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

Scegli la query più lento e ottimizzare questo. quindi corri per un po 'e ripeti.

Ci sono alcune informazioni eccellente e strumenti qui http://hackmysql.com/nontech

DC

UPDATE:

O avete un problema di rete che causa il timeout, se si utilizza un'istanza MySQL locale, allora che è improbabile , O qualcosa sta bloccando un tavolo troppo a lungo causando un timeout. il processo che blocca la tabella o le tabelle troppo a lungo verrà elencato nel registro lento come una query lenta. è anche possibile ottenere la query di registro lento per visualizzare tutte le query che non riescono a utilizzare un indice risultante in una query inefficiente.

Se è possibile che il problema si verifichi mentre sei presente, puoi anche utilizzare uno strumento come phpmyadmin o la riga di comando per eseguire "SHOW PROCESSLIST \ G" questo ti darà un elenco di quali query sono in esecuzione mentre il problema sta succedendo.

Pensi che il problema sia nell'istruzione di inserimento, quindi qualcosa sta bloccando quella tabella. quindi è necessario trovare ciò che sta bloccando quella tabella, quindi è necessario trovare ciò che è in esecuzione in modo lento il suo blocco della tabella per troppo tempo. Le domande lente sono un modo per farlo.

Altre cose da guardare

CPU - è inattivo o funziona a tutta velocità

IO - è io che causano rapine

RAM - stai scambiando tutto il tempo (causerà io eccessiva)

La tabella product_search_query utilizza un indice?

Qual è la chiave primaria?

Se l'indice utilizza stringhe troppo lunghe? è possibile creare un file di indice enorme che causa inserimenti molto lenti (il log delle query lento mostrerà anche questo)

E sì, il problema potrebbe essere altrove, ma è necessario iniziare da qualche parte, non è vero?

DC

+0

Ancora una volta, nessun problema di velocità, a parte un timeout, quando funziona funziona a una velocità molto ragionevole. Il timeout è causato dal blocco, non un problema di velocità effettiva. –

+0

Vedere l'aggiornamento sopra – DeveloperChris

0

Provare avvolgendo il ESEGUI con il seguente:

SET ISOLATION LEVEL sessione di transazione READ UNCOMMITTED;

ESEGUI I Selezionare USING @keywords, @keywords, @startOffset, @itemsToReturn;

SET SESSION LIVELLO DI ISOLAMENTO TRANSAZIONE LEGGERE;

Faccio qualcosa di simile in TSQL per tutti i report stored proc e ricerche in cui le letture ripetibili non sono importanti per ridurre i problemi di blocco/blocco con altri processi in esecuzione sul database.

Problemi correlati