我们正在使用在MySQL 5.7.27和PHP 7.2上运行的OpenCart电子商务平台,其中包含大约5000种产品。
我们使用扩展程序在管理面板中搜索产品,平均执行搜索查询大约需要70-80秒。
原始查询:
SELECT
SQL_CALC_FOUND_ROWS pd.*,
p.*,
(
SELECT
price
FROM
product_special
WHERE
product_id = p.product_id
AND
(
date_start = '0000-00-00'
OR date_start < NOW()
AND
(
date_end = '0000-00-00'
OR date_end > NOW()
)
)
ORDER BY
priority,
price LIMIT 1
)
AS special_price,
IF(p.image IS NOT NULL
AND p.image <> ''
AND p.image <> 'no_image.png', 'Igen', 'Nem') AS image_text,
IF(p.status, 'Engedélyezett', 'Letiltott') AS status_text,
GROUP_CONCAT(DISTINCT CONCAT_WS(' > ', fgd.name, fd.name)
ORDER BY
CONCAT_WS(' > ', fgd.name, fd.name) ASC SEPARATOR '
') AS filter_text, GROUP_CONCAT(DISTINCT fd.filter_id ORDER BY CONCAT_WS(' > ', fgd.name, fd.name) ASC SEPARATOR '_') AS filter, GROUP_CONCAT(DISTINCT cat.name ORDER BY cat.name ASC SEPARATOR ' ') AS category_text, GROUP_CONCAT(DISTINCT cat.category_id ORDER BY cat.name ASC SEPARATOR '_') AS category, GROUP_CONCAT(DISTINCT IF(p2s.store_id = 0, 'Butopêa HU', s.name) SEPARATOR ' ') AS store_text, GROUP_CONCAT(DISTINCT p2s.store_id SEPARATOR '_') AS store FROM product p LEFT JOIN product_description pd ON (p.product_id = pd.product_id AND pd.language_id = '2') LEFT JOIN product_to_category p2c ON (p.product_id = p2c.product_id) LEFT JOIN (SELECT cp.category_id AS category_id, GROUP_CONCAT(cd1.name ORDER BY cp.level SEPARATOR ' > ') AS name FROM category_path cp LEFT JOIN category c ON (cp.path_id = c.category_id) LEFT JOIN category_description cd1 ON (c.category_id = cd1.category_id) LEFT JOIN category_description cd2 ON (cp.category_id = cd2.category_id) WHERE cd1.language_id = '2' AND cd2.language_id = '2' GROUP BY cp.category_id ORDER BY name) AS cat ON (p2c.category_id = cat.category_id) LEFT JOIN product_to_category p2c2 ON (p.product_id = p2c2.product_id) LEFT JOIN product_filter p2f ON (p.product_id = p2f.product_id) LEFT JOIN filter f ON (f.filter_id = p2f.filter_id) LEFT JOIN filter_description fd ON (fd.filter_id = p2f.filter_id AND fd.language_id = '2') LEFT JOIN filter_group_description fgd ON (f.filter_group_id = fgd.filter_group_id AND fgd.language_id = '2')
LEFT JOIN
product_filter p2f2
ON (p.product_id = p2f2.product_id)
LEFT JOIN
product_to_store p2s
ON (p.product_id = p2s.product_id)
LEFT JOIN
store s
ON (s.store_id = p2s.store_id)
LEFT JOIN
product_to_store p2s2
ON (p.product_id = p2s2.product_id)
GROUP BY
p.product_id
ORDER BY
pd.name ASC LIMIT 0,
190
我尝试使用MySQL的EXPLAIN功能来查看发生了什么,但是没有任何东西立刻引起我的注意:
我的测试环境正在Intel NVME,2666 MHz DDR4 RAM和i7 8th gen上运行。 CPU,但仍然很慢。
我很感谢有关此查询速度降低的任何提示。
SELECT
中使用了[[看起来有些许多映射]。 WooCommerce(和底层的Wordpress)的实现方式效率低下。
[这是我有关如何更改架构以提高wp_postmeta和类似表(product_to_category和product_to_store)的性能的讨论:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta这可能是
bug
: ( date_start = '0000-00-00'
OR date_start < NOW()
AND ( date_end = '0000-00-00'
OR date_end > NOW() )
)
您可能想要额外的括号:
( ( date_start = '0000-00-00' OR date_start < NOW() ) AND ( date_end = '0000-00-00' OR date_end > NOW() ) )
也,
( date_start = '0000-00-00' OR date_start < NOW() )
可以到]简化
( date_start < NOW() )
并且,查询似乎具有,其中explode-implode症候群
JOINs
展开以生成一个大的临时表,而仅使GROUP BY
折叠至原始大小。解决方法是转向 GROUP_CONCAT(... foo.x ...) AS blah.
...
LEFT JOIN foo ... ON ...
进入
( SELECT GROUP_CONCAT(... foo.x ...) FROM foo WHERE ... ) AS blah,
如果消除了所有的,如果'right'表不是可选的。 (相反,说LEFT JOINs
,那么也可以消除GROUP BY p.product_id
。不说
LEFT JOIN
JOIN
。) LEFT JOIN category_description cd1 ON (cp.category_id = cd1.category_id)
WHERE cd1.language_id = '2' -- this invalidates the `LEFT`
似乎不使用,除了检查cd2
cd2.language_id = '2'
。考虑删除对其的引用。这需要两个临时表
,因为它们是不同的: GROUP BY p.product_id
ORDER BY pd.name ASC
[我是不是说pd.name
只是'语言'2中p.product_id
的名称?如果是这样,这在语义上可能是相同的,但是由于消除了临时表和排序,所以速度更快):
GROUP BY pd.name ORDER BY pd.name
完成后,最好将INDEX(language_id, name)
放在pd
上。通常被
LIMIT 0, 190
中的speedupSQL_CALC_FOUND_ROWS
消除。
过度规范化
导致这两个组成部分在单独的表中?CONCAT_WS(' > ', fgd.name, fd.name)