powered by simpleCommunicator - 2.0.59     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Процедуры MySQL оптимизация
20 сообщений из 20, страница 1 из 1
Процедуры MySQL оптимизация
    #39603776
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всем привет, столкнулся на своей практике с проектом, где нужно разобраться с долгой загрузкой страниц каталога предприятий (какой проект написать не могу). Потратив несколько часов на поиск причины, выяснилось что при запросе каталога предприятий, бд грузит проц сервера, дальше поковыряв логи, все свелось к одной процедуре. На одну страницу выводится всего шесть элементов, одна и та же процедура используется для категорий и подкатегорий, а так же и поиск в ней. Разрабатывал проект не я, я третий или 20 веб-мастер у этого проекта, информации как всегда нету у владельца проекта. По этому прошу помощи знающих людей, помогите пожалуйста найти проблемное место в процедуре.

авторDELIMITER $$

CREATE PROCEDURE `company_get_list_for_index_test`(IN param_lang VARCHAR(20), IN param_uri VARCHAR(1000), IN param_search_text VARCHAR(200), IN param_from INT, IN param_count INT)
sp:
BEGIN

DECLARE var_lang_id INT DEFAULT 0;
DECLARE var_category_id INT DEFAULT 0;
DECLARE var_main_category_id INT DEFAULT 0;

SET var_lang_id = IFNULL((SELECT id FROM lang WHERE alias = param_lang), 0);
SET var_category_id = (SELECT id FROM categories WHERE uri = param_uri);

SET var_main_category_id = (sf_get_main_category_id_by_category_id(var_category_id));

UPDATE companies AS c SET
rating = (SELECT ccs.sort_no FROM category_companies_sort AS ccs WHERE ccs.company_id = c.id AND category_id = var_main_category_id);
UPDATE companies AS c SET
rating = 0
WHERE rating IS NULL;

UPDATE companies AS c SET
rating = rating + IF(c.status_payment = 1, c.status_payment * 1000, 0);


PREPARE stmt FROM
"SELECT SQL_CALC_FOUND_ROWS
c.id,
c.map,
c.logo,
c.banner,
c.site,
c.status_id,
c.created_at,
c.created_by,
c.modified_at,
c.modified_by,
c.status_payment,
cl.lang_id,
cl.`name`,
cl.brief_text,
cl.full_text,
cl.address,
cl.phone_1,
cl.phone_2,
cl.phone_3,
cl.phone_4,
cl.phone_5,
cl.contacts_info,
(SELECT id FROM category_companies_sort WHERE company_id = c.id AND category_id = ? LIMIT 1) AS sort_id,
IF((SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = ?) IS NULL, 0,
(SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = ?)) AS sort_no,
IF(c.status_payment = 1, c.status_payment * 1000, 0) AS `status_payment_temp`
FROM companies AS c
INNER JOIN companies_lang AS cl ON c.id = cl.company_id AND cl.lang_id = ?
WHERE
(EXISTS(
SELECT * FROM companies_categories AS cc
WHERE cc.company_id = c.id
AND cc.category_id IN (SELECT id FROM categories WHERE uri LIKE CONCAT(?, '%'))
)OR ? = ''
)AND(
(MATCH (c.site, cl.name, cl.brief_text, cl.full_text, cl.address, cl.phone_1, cl.phone_2, cl.phone_3, cl.phone_4, cl.phone_5) AGAINST
(CONCAT('\"', ?, '\"') IN BOOLEAN MODE))
OR
? = ''
)
ORDER BY
rating ASC
LIMIT ?,?;";

SET @stmt_from = param_from;
SET @stmt_count = param_count;
SET @stmt_lang_id = var_lang_id;
SET @stmt_uri = param_uri;
SET @stmt_search_text = param_search_text;
SET @stmt_main_category_id = var_main_category_id;
SET @stmt_m_category_id = var_category_id;

EXECUTE stmt
USING
@stmt_m_category_id,
@stmt_main_category_id,
@stmt_main_category_id,
@stmt_lang_id,
@stmt_m_category_id
@stmt_uri,
@stmt_search_text,
@stmt_search_text,
@stmt_from,
@stmt_count;

SELECT FOUND_ROWS() AS 'count';
END$$

DELIMITER ;


Я предполагаю что эта часть кода тормозит:

авторEXISTS(
SELECT * FROM companies_categories AS cc
WHERE cc.company_id = c.id
AND cc.category_id IN (SELECT id FROM categories WHERE uri LIKE CONCAT(?, '%'))
)OR ? = ''
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603784
Фотография MasterZiv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
awotarosПотратив несколько часов на поиск причины, выяснилось что при запросе каталога предприятий, бд грузит проц сервера,

Это нормально. СУБД работает, грузить процессор.
Это даже хорошо.

Так что подозреваю проблемы НЕТ, или она не в этой процедуре.
Чтобы найти проблемные запросы, включи slow query log, и гляди туда.
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603790
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
MasterZiv,

Я включил, и только этот запрос в логах фигурирует, особенно когда есть переход на страницу подкатегории предприятий
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603791
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
awotarosMasterZiv,

Я включил, и только этот запрос в логах фигурирует, особенно когда есть переход на страницу подкатегории предприятий

Дело в том что весь сайт при этом виснет ))
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603838
вадя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
awotaros,
от препаред можно обойтись
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603850
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
awotaros,

MySQL какой версии?
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603873
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
miksoft,

Версия 5.5.32
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603874
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
вадя,

Вы думаете что в этом проблема ?
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603876
вадя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
awotarosвадя,

Вы думаете что в этом проблема ?
я бы начал с этого
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603877
вадя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
awotaros,
и in я бы заменил на join
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603905
вадя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторSQL_CALC_FOUND_ROWSтоже можно отказаться.
просто поставив поле-счётчик
set @cn=0;
"SELECT
@cn:=@cn+1 as cn,
c.id,
c.map,
c.logo,
.......
;


select @cn as `count`;
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603907
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
вадя,

а LIMIT ?
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603909
miksoft
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
awotarosmiksoft,

Версия 5.5.32вадяawotaros,
и in я бы заменил на join+1
В старых версиях конструкцию IN (SELECT ...) лучше избегать. Подзапрос в ней будет выполняться столько раз, сколько будет производиться проверка. Хотя он независимый и мог бы выполняться один раз.
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603927
vkle
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
вадяавторSQL_CALC_FOUND_ROWSтоже можно отказаться.
просто поставив поле-счётчикМожно пояснить, чем объективно плох SQL_CALC_FOUND_ROWS и чем лучше счетчик?
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603978
вадя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
miksoftа LIMIT ?может и не потребоваться
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39603981
вадя
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
vkleМожно пояснить, чем объективно плох SQL_CALC_FOUND_ROWS и чем лучше счетчик?куча споров за и против, но если есть спор - лучше избегать.
мне кажется - счётчик всяко должен быстрее.
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39604003
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я убрал PREPARE и получилось вот:

авторDELIMITER $$

CREATE PROCEDURE `sp_company_get_list_for_index_nw`(IN param_lang VARCHAR(20), IN param_uri VARCHAR(1000), IN param_search_text VARCHAR(200), IN param_from INT, IN param_count INT)
sp:
BEGIN

DECLARE var_lang_id INT DEFAULT 0;
DECLARE var_category_id INT DEFAULT 0;
DECLARE var_main_category_id INT DEFAULT 0;

SET var_lang_id = IFNULL((SELECT id FROM lang WHERE alias = param_lang), 0);

SET var_category_id = (SELECT id FROM categories WHERE uri = param_uri);

SET var_main_category_id = (sf_get_main_category_id_by_category_id(var_category_id));

UPDATE companies AS c SET
rating = (SELECT ccs.sort_no FROM category_companies_sort AS ccs WHERE ccs.company_id = c.id AND category_id = var_main_category_id);
UPDATE companies AS c SET
rating = 0
WHERE rating IS NULL;

UPDATE companies AS c SET
rating = rating + IF(c.status_payment = 1, c.status_payment * 1000, 0);


SELECT SQL_CALC_FOUND_ROWS
c.id,
c.map,
c.logo,
c.banner,
c.site,
c.status_id,
c.created_at,
c.created_by,
c.modified_at,
c.modified_by,
c.status_payment,
cl.lang_id,
cl.`name`,
cl.brief_text,
cl.full_text,
cl.address,
cl.phone_1,
cl.phone_2,
cl.phone_3,
cl.phone_4,
cl.phone_5,
cl.contacts_info,
(SELECT id FROM category_companies_sort WHERE company_id = c.id AND category_id = var_category_id LIMIT 1) AS sort_id,
IF((SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = var_main_category_id) IS NULL, 0,
(SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = var_main_category_id)) AS sort_no,
IF(c.status_payment = 1, c.status_payment * 1000, 0) AS `status_payment_temp`
FROM companies AS c
INNER JOIN companies_lang AS cl ON c.id = cl.company_id AND cl.lang_id = var_lang_id
WHERE
(EXISTS(
SELECT * FROM companies_categories AS cc
WHERE cc.company_id = c.id
AND cc.category_id IN (SELECT id FROM categories WHERE uri LIKE CONCAT(?, '%'))
)OR param_uri = ''
)AND(
(MATCH (c.site, cl.name, cl.brief_text, cl.full_text, cl.address, cl.phone_1, cl.phone_2, cl.phone_3, cl.phone_4, cl.phone_5) AGAINST
(CONCAT('\"', param_search_text, '\"') IN BOOLEAN MODE))
OR
param_search_text = ''
)
ORDER BY
rating ASC
LIMIT param_from,param_count;


SELECT FOUND_ROWS() AS 'count';
END$$

DELIMITER ;


Я тоже согласен что в IN проблема. Как мне обойтись без IN, убрать оттуда, ведь при помощи IN он выбираете материалы по совпадению.

авторEXISTS(
SELECT * FROM companies_categories AS cc
WHERE cc.company_id = c.id
AND cc.category_id IN (SELECT id FROM categories WHERE uri LIKE CONCAT(param_uri, '%'))
)OR param_uri = ''


А что касается SQL_CALC_FOUND_ROWS да я согласен что он лишний и что он считает все записи не зависимо от лимита (хотя он используется для постраничной навигации), с этим вопросом я попробую разобраться, но меня больше всего волнует IN, несколько запросов таких где участвует в этой процедуре IN по сути грузят mysql и в процессах долго висят (это если несколько посетителе открывают подкатегорию предприятий, в этом случае срабатывает IN)
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39604005
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
awotaros,

Вместо вопросика там param_uri

автор(EXISTS(
SELECT * FROM companies_categories AS cc
WHERE cc.company_id = c.id
AND cc.category_id IN (SELECT id FROM categories WHERE uri LIKE CONCAT(param_uri, '%'))
)OR param_uri = ''
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39604007
Сильно в запрос не вникал, но вот мое имхо:

Я бы попробовал избавиться от этих запросов в списке select, попробовать переписать их на join

(SELECT id FROM category_companies_sort WHERE company_id = c.id AND category_id = ? LIMIT 1) AS sort_id,
IF((SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = ?) IS NULL, 0,
(SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = ?)) AS sort_no

И убрать звездочку в условии, заменить на PK или вообще на 1 (если логика запроса позволяет)
WHERE
(EXISTS(
SELECT * FROM companies_categories

Ну и классическое предложение проверить наличие/использование необходимых индексов и отсутствующие создать. Еще проверить "валидность" имеющихся индексов и возможно пересоздать "поломанные", если таковые имеются.
Не знаю точно на счет MySQL, но статистика в базе свежая? Вакуум/Аналайз делаете?
...
Рейтинг: 0 / 0
Процедуры MySQL оптимизация
    #39604485
awotaros
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Вопрос решил, вот несколько работающих вариантов:

Это решение от Akina, вот его ответ 21201983 за что ему огромное спасибо

Код: php
1.
2.
3.
4.
5.
SELECT cc.company_id, cc.category_id, ct.id, ct.uri
            FROM companies_categories AS cc, categories AS ct 
            WHERE cc.company_id = c.id 
              AND cc.category_id = ct.id 
              AND ct.uri LIKE CONCAT(param_uri, '%')



И вот еще один вариант с JOIN, ребята тут подсказали:

Код: php
1.
2.
3.
SELECT cc.category_id, ct.id, cc.company_id  FROM companies_categories AS cc 
            JOIN (SELECT id FROM categories WHERE uri LIKE CONCAT(param_uri, '%')) ct ON cc.category_id = ct.id
            WHERE cc.company_id = c.id



Второй вариант тоже работает, но я пока использую вариант от Akina, работает хорошо, а вариант с JOIN только тестировал но не пробовал на проде, мне кажется что первый вариант быстрее работает. Думаю многим пригодится кто столкнется с процедурами и где есть зависания mysql.

Вот готовый вариант процедуры:

Код: php
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
DELIMITER $$

CREATE PROCEDURE `sp_company_get_list_for_index_nw`(IN param_lang VARCHAR(20), IN param_uri VARCHAR(1000), IN param_search_text VARCHAR(200), IN param_from INT, IN param_count INT)
sp:
BEGIN

    DECLARE var_lang_id                 INT DEFAULT 0;
    DECLARE var_category_id             INT DEFAULT 0;
    DECLARE var_main_category_id        INT DEFAULT 0;

    SET var_lang_id = IFNULL((SELECT id FROM lang WHERE alias = param_lang), 0);
 
    SET var_category_id = (SELECT id FROM categories WHERE uri = param_uri);

    SET var_main_category_id = (sf_get_main_category_id_by_category_id(var_category_id));

    UPDATE companies AS c SET 
        rating = (SELECT ccs.sort_no FROM category_companies_sort AS ccs WHERE ccs.company_id = c.id AND category_id = var_main_category_id);
    UPDATE companies AS c SET
        rating = 0
    WHERE rating IS NULL;
    
    UPDATE companies AS c SET
        rating = rating + IF(c.status_payment = 1, c.status_payment * 1000, 0);

    SELECT SQL_CALC_FOUND_ROWS
            c.id,
            c.map,
            c.logo,
            c.banner,
            c.site,
            c.status_id,
            c.created_at,
            c.created_by,
            c.modified_at,
            c.modified_by,
            c.status_payment,
            cl.lang_id,
            cl.`name`,
            cl.brief_text,
            cl.full_text,
            cl.address,
            cl.phone_1,
            cl.phone_2,
            cl.phone_3,
            cl.phone_4,
            cl.phone_5,
            cl.contacts_info,
            (SELECT id FROM category_companies_sort WHERE company_id = c.id AND category_id = var_category_id LIMIT 1) AS sort_id,
             IF((SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = var_main_category_id) IS NULL, 0, 
            (SELECT sort_no FROM category_companies_sort WHERE company_id = c.id AND category_id = var_main_category_id)) AS sort_no,
            IF(c.status_payment = 1, c.status_payment * 1000, 0) AS `status_payment_temp`
        FROM companies AS c
        INNER JOIN companies_lang AS cl ON c.id = cl.company_id AND cl.lang_id = var_lang_id
        WHERE
        (EXISTS(

            /*SELECT cc.category_id, ct.id, cc.company_id  FROM companies_categories AS cc 
            JOIN (SELECT id FROM categories WHERE uri LIKE CONCAT(param_uri, '%')) ct ON cc.category_id = ct.id
            WHERE cc.company_id = c.id */

            SELECT cc.company_id, cc.category_id, ct.id, ct.uri
            FROM companies_categories AS cc, categories AS ct 
            WHERE cc.company_id = c.id 
              AND cc.category_id = ct.id 
              AND ct.uri LIKE CONCAT(param_uri, '%')

            )OR param_uri = ''
        )AND(
                (MATCH (c.site, cl.name, cl.brief_text, cl.full_text, cl.address, cl.phone_1, cl.phone_2, cl.phone_3, cl.phone_4, cl.phone_5) AGAINST
                (CONCAT('\"', param_search_text, '\"') IN BOOLEAN MODE))
                OR
                param_search_text = ''
        )
        ORDER BY rating ASC
        LIMIT param_from,param_count;

        
    SELECT FOUND_ROWS() AS 'count';
END$$

DELIMITER ;



Спасибо большое за помощь, всем !
...
Рейтинг: 0 / 0
20 сообщений из 20, страница 1 из 1
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Процедуры MySQL оптимизация
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]