Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / MySQL [игнор отключен] [закрыт для гостей] / Процедуры MySQL оптимизация / 20 сообщений из 20, страница 1 из 1
18.02.2018, 21:20
    #39603776
awotaros
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
Всем привет, столкнулся на своей практике с проектом, где нужно разобраться с долгой загрузкой страниц каталога предприятий (какой проект написать не могу). Потратив несколько часов на поиск причины, выяснилось что при запросе каталога предприятий, бд грузит проц сервера, дальше поковыряв логи, все свелось к одной процедуре. На одну страницу выводится всего шесть элементов, одна и та же процедура используется для категорий и подкатегорий, а так же и поиск в ней. Разрабатывал проект не я, я третий или 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
18.02.2018, 21:34
    #39603784
MasterZiv
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
awotarosПотратив несколько часов на поиск причины, выяснилось что при запросе каталога предприятий, бд грузит проц сервера,

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

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

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

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

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

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

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

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

Вы думаете что в этом проблема ?
я бы начал с этого
...
Рейтинг: 0 / 0
19.02.2018, 03:34
    #39603877
вадя
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
awotaros,
и in я бы заменил на join
...
Рейтинг: 0 / 0
19.02.2018, 07:47
    #39603905
вадя
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
автор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
19.02.2018, 07:55
    #39603907
miksoft
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
вадя,

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

Версия 5.5.32вадяawotaros,
и in я бы заменил на join+1
В старых версиях конструкцию IN (SELECT ...) лучше избегать. Подзапрос в ней будет выполняться столько раз, сколько будет производиться проверка. Хотя он независимый и мог бы выполняться один раз.
...
Рейтинг: 0 / 0
19.02.2018, 08:52
    #39603927
vkle
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
вадяавторSQL_CALC_FOUND_ROWSтоже можно отказаться.
просто поставив поле-счётчикМожно пояснить, чем объективно плох SQL_CALC_FOUND_ROWS и чем лучше счетчик?
...
Рейтинг: 0 / 0
19.02.2018, 10:17
    #39603978
вадя
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
miksoftа LIMIT ?может и не потребоваться
...
Рейтинг: 0 / 0
19.02.2018, 10:19
    #39603981
вадя
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
vkleМожно пояснить, чем объективно плох SQL_CALC_FOUND_ROWS и чем лучше счетчик?куча споров за и против, но если есть спор - лучше избегать.
мне кажется - счётчик всяко должен быстрее.
...
Рейтинг: 0 / 0
19.02.2018, 10:46
    #39604003
awotaros
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
Я убрал 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
19.02.2018, 10:48
    #39604005
awotaros
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
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
19.02.2018, 10:49
    #39604007
Процедуры MySQL оптимизация
Сильно в запрос не вникал, но вот мое имхо:

Я бы попробовал избавиться от этих запросов в списке 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
20.02.2018, 01:48
    #39604485
awotaros
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Процедуры MySQL оптимизация
Вопрос решил, вот несколько работающих вариантов:

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


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