2009-12-29 4 views
6

EDIT:Ошибка в хранимой процедуре MySQL?

Я сузили мой MySQL ждать таймаут до этой строки:

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

Любая идея, почему это вызвало бы проблемы? Я не могу это решить!

Привет, ребята, я написал сохраненный proc для поиска продуктов в определенных категориях, из-за определенных ограничений, с которыми мне приходилось сталкиваться, я не мог делать то, что хотел (ограничивая, но при этом возвращаю общее количество строк найдено, с сортировкой и т. д.)

Это означает разбиение строки идентификаторов категории от 1,2,3 до временной таблицы, а затем построение полнотекстового поискового запроса на основе параметров сортировки и ограничений, выполняет строку запроса, а затем выбирает общее количество результатов.

Теперь, я знаю, что я не гуру MySQL, очень далеко от него, у меня есть работа, но я продолжаю получать тайм-ауты с поиском продуктов и т. Д., Поэтому я думаю, что это может вызвать какой-то вид проблемы?

Есть ли у кого-нибудь идеи, как я могу это убрать, или даже сделать это гораздо лучше, чем я, вероятно, не знаю?

Спасибо ..

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 ; 

Любая помощь очень очень признателен!

+1

Можно ли получить ТАБЛИЦУ DESCRIBE для таблиц, используемых в этом? Я думаю, что мы можем уменьшить усложнение и, возможно, ускорить его, если у меня появятся еще несколько глаз, которые он сам забьет. –

+0

Я второй комментарий кевинов. слишком мало, чтобы продолжать. – DeveloperChris

ответ

4

С этим запросом вы мало что можете сделать.

Попробуйте это:

  1. Создать PRIMARY KEY на categoryIds (categoryId)

    • Убедитесь, что supplier (supplied_id) является PRIMARY KEY

    • Убедитесь, что category_product (ProductID, CategoryID) (в таком порядке) является PRIMARY KEY , или у вас есть индекс с ведущим ProductID.

Update:

Если это INSERT, что вызывает проблемы и product_search_query в MyISAM таблице проблема может быть с MyISAM блокировкой.

MyISAM блокирует всю таблицу, если она решает вставить строку в свободный блок в середине таблицы, что может привести к таймаутам.

Попробуйте использовать INSERT DELAYED вместо:

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

Это поместит записи в очередь вставки и немедленно вернуться. Запись будет добавлена ​​позже асинхронно.

Обратите внимание, что вы можете потерять информацию, если сервер умирает после выдачи команды, но до фактического ввода записей.

Update:

Поскольку ваша таблица InnoDB, это может быть проблема с блокировкой таблицы. INSERT DELAYED не поддерживается на InnoDB.

В зависимости от характера запроса, DML запросов на InnoDB Таблица может помещать блокировки замков, которые будут блокировать вставки.

Например:

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

Этот запрос выполняет ref сканирование и помещает замки на отдельных записей:

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

Этот запрос, в то время как делать то же самое, выполняет range сканирования и помещает пробел блокировка после значения ключа 2, что не позволит ввести значение ключа 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

Спасибо за ваш ответ, на самом деле он не работает медленно, хотя ... Кажется, это время от времени, я просто запустил его, и это заняло 0.0014s, поэтому никаких проблем с фактической скоростью? –

+0

Это будет работать медленно, если вы будете искать часто используемые ключевые слова. Скажем, если вы ищете ключевое слово '' professional'', а '1,000,000' - это ключевое слово, движок должен будет восстановить все эти записи и отсортировать их в соответствии с порядком сортировки. Это можно было бы улучшить с помощью обычного предиката (не полного текста), создав составной индекс, но, к сожалению, «MySQL» не позволяет смешивать полнотекстовые и неполные ключи в одном индексе. – Quassnoi

+0

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

0

Включите медленные запросы, что даст вам представление о том, что так долго нужно выполнять, что есть тайм-аут.

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

Выберите медленный запрос и оптимизировать это. затем запустите некоторое время и повторите.

Существует некоторая отличная информация и инструменты здесь http://hackmysql.com/nontech

DC

UPDATE:

Либо у вас есть проблемы сети вызывая тайм-аут, если вы используете локальный экземпляр MySQL то, что маловероятно , ИЛИ что-то блокирует таблицу слишком долго, вызывая тайм-аут. процесс, который блокирует таблицу или таблицы слишком долго, будет указан в медленном журнале как медленный запрос. вы также можете получить медленный запрос журнала для отображения любых запросов, которые не могут использовать индекс, приводящий к неэффективному запросу.

Если у вас возникла проблема, возникающая во время присутствия, вы также можете использовать инструмент, например phpmyadmin или командную строку для запуска «SHOW PROCESSLIST \ G», это даст вам список запросов, выполняемых в то время как проблема происходит.

Вы считаете, что проблема заключается в вашей инструкции insert, поэтому что-то блокирует эту таблицу. поэтому вам нужно найти то, что блокирует эту таблицу, поэтому вам нужно найти то, что работает так медленно, что он блокирует таблицу слишком долго. Медленные запросы - это один из способов сделать это.

Другие вещи, чтобы посмотреть на

CPU - это простаивает или работает на полную шкурки

IO - это И.О., вызывая разбойные нападения

RAM - вы поменяв все время (приводит к чрезмерному И.О.)

Использует ли таблица product_search_query индекс?

Что такое первичный ключ?

Если ваш индекс использует слишком длинные строки? вы можете создать огромный индексный файл, который вызывает очень медленные вставки (журнал медленных запросов также покажет это)

И да проблема может быть в другом месте, но вы должны начать где-то, не так ли.

DC

+0

Опять же, никаких проблем с производительностью, кроме таймаута, когда он работает, работает с очень разумной скоростью. Тайм-аут вызван блокировкой, а не фактической скоростью. –

+0

См. Обновление выше – DeveloperChris

0

Попробуйте обертывание ваш ВЫПОЛНИТЬ со следующим:

SET SESSION ИЗОЛЯЦИИ TRANSACTION READ УРОВЕНЬ UNCOMMITTED;

EXECUTE TheSelect USING @keywords, @keywords, @startOffset, @itemsToReturn;

УСТАНОВЛЕННАЯ СЕССИЯ СДЕЛКА УРОВЕНЬ ИЗОЛЯЦИИ ПОВТОРЯЮЩИЙСЯ ЧИТАТЬ;

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

Смежные вопросы