标签:mysql optimization schema index
我一直试图解决这个问题一段时间了,所以我决定在这里试试运气.
我有一个非常复杂的在线商店数据库结构(一个数据库中的多个商店,数十万个产品),我正在查询MySQL以返回指定类别的产品.产品与类别有多对多(M:N)的关系,因此我有表product,category和product_category(代表弱实体类型).
表模式如下:
CREATE TABLE `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) COLLATE utf8_czech_ci NOT NULL,
`description` text COLLATE utf8_czech_ci NOT NULL,
`date` timestamp NOT NULL DEFAULT current_timestamp(),
`date_edited` timestamp NOT NULL DEFAULT current_timestamp(),
`visibility` tinyint(1) NOT NULL DEFAULT 1,
`main_slider` tinyint(1) NOT NULL DEFAULT 0,
`bought` int(11) NOT NULL DEFAULT 0,
`price_action` int(11) NOT NULL DEFAULT 0,
`quantity_action` int(11) NOT NULL DEFAULT 0,
`duration_action` timestamp NOT NULL DEFAULT current_timestamp(),
`new` tinyint(1) NOT NULL DEFAULT 1,
`type` int(2) NOT NULL DEFAULT 0,
`origin_url` varchar(250) COLLATE utf8_czech_ci DEFAULT NULL,
`origin_price` int(11) NOT NULL DEFAULT 0,
`origin_price_dph` int(11) NOT NULL DEFAULT 0,
`rating` int(11) NOT NULL DEFAULT 0,
`ratingcount` int(11) NOT NULL DEFAULT 0,
`facebook_flag` tinyint(1) DEFAULT 0,
`salebot` tinyint(1) NOT NULL DEFAULT 0,
`shop_supplier_id` int(11) NOT NULL,
`shop_id` int(11) NOT NULL,
`product_global_id` int(11) DEFAULT NULL,
`prioritize` tinyint(1) NOT NULL DEFAULT 0,
`free_delivery` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `product_global_id_2` (`product_global_id`,`shop_id`),
KEY `shop_id` (`shop_id`),
KEY `shop_supplier_id` (`shop_supplier_id`),
KEY `product_global_id` (`product_global_id`),
KEY `rating` (`rating`),
KEY `date` (`date`),
KEY `quantity_action` (`quantity_action`),
KEY `duration_action` (`duration_action`),
KEY `main_slider` (`main_slider`),
KEY `shop_id_2` (`shop_id`,`main_slider`),
KEY `shop_id_3` (`shop_id`,`prioritize`),
KEY `shop_id_4` (`shop_id`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
CREATE TABLE `product_category` (
`category_id` int(11) NOT NULL,
`product_id` int(11) NOT NULL,
PRIMARY KEY (`category_id`,`product_id`),
KEY `category` (`category_id`),
KEY `product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci;
我正在使用的查询:
SELECT product.id,
product.name,
product.shop_supplier_id,
product.price_action,
product.quantity_action,
product.duration_action,
variant.id as variant_id,
IF(variant.price_default IS NULL, variant_global.price_vat, variant.price_default) as price_default,
variant.price_dph as price,
variant_global.delivery_date,
variant.value AS variant_name,
IF(product.type = 0, variant_global.voc_silver, variant.voc_dph) as voc_dph,
shop_supplier.text_available,
shop_supplier.text_unavailable,
IF(product.type = 0, variant_global.available = 1, variant.availability = 1) as avail,
variant.availability_type,
supplier.id as supplier_id,
variant_global.price_vat as price_default_ds,
product.type
FROM `product`
LEFT JOIN shop_supplier ON (`product`.`shop_supplier_id` = `shop_supplier`.`id`)
LEFT JOIN supplier ON (shop_supplier.supplier_id = `supplier`.`id`)
LEFT JOIN variant_global ON ((SELECT id FROM variant_global WHERE variant_global.product_global_id=product.product_global_id ORDER BY `available` DESC LIMIT 1) = `variant_global`.`id`)
LEFT JOIN product_global ON (variant_global.product_global_id = product_global.id)
LEFT JOIN image ON (`product`.`id` = `image`.`product_id`)
LEFT JOIN variant ON ((SELECT id FROM variant WHERE `product_id`=product.id ORDER BY `availability` DESC LIMIT 1) = `variant`.`id`)
LEFT JOIN product_category ON (`product`.`id` = `product_category`.`product_id`)
WHERE (product.shop_id=100000)
AND (product.visibility= 1)
AND (image.id IS NOT NULL)
AND (IF(product.type = 0, variant_global.available = 1, variant.availability = 1) = (CASE WHEN display_available = 1 AND display_unavailable = 1 THEN IF(product.type = 0, variant_global.available = 1, variant.availability = 1) WHEN display_available = 1 THEN 1 ELSE 0 END))
AND (variant_global.deleted IS NULL OR variant_global.deleted = 0)
AND (product_category.category_id= 5)
GROUP BY product.id
ORDER BY prioritize DESC, RAND() LIMIT 5;
对于不同的类别,在单个页面上多次调用该查询,并且非常慢,我尝试了所有不同的键和组合,但无法弄明白.
查询的EXPLAIN输出:
+------+--------------------+------------------+--------+-----------------------------------------------+---------------------+---------+-------------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+--------------------+------------------+--------+-----------------------------------------------+---------------------+---------+-------------------------------------+------+----------------------------------------------+
| 1 | PRIMARY | product_category | ref | PRIMARY,category,product | PRIMARY | 4 | const | 4264 | Using index; Using temporary; Using filesort |
| 1 | PRIMARY | product | eq_ref | PRIMARY,shop_id,shop_id_2,shop_id_3,shop_id_4 | PRIMARY | 4 | dropohs.product_category.product_id | 1 | Using where |
| 1 | PRIMARY | shop_supplier | eq_ref | PRIMARY | PRIMARY | 4 | dropohs.product.shop_supplier_id | 1 | |
| 1 | PRIMARY | supplier | eq_ref | PRIMARY | PRIMARY | 4 | dropohs.shop_supplier.supplier_id | 1 | Using where; Using index |
| 1 | PRIMARY | variant | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | Using where |
| 1 | PRIMARY | image | ref | PRIMARY,product_id_2,product_id | product_id_2 | 4 | dropohs.product_category.product_id | 1 | Using where; Using index |
| 1 | PRIMARY | variant_global | eq_ref | PRIMARY | PRIMARY | 4 | func | 1 | Using where |
| 3 | DEPENDENT SUBQUERY | variant | ref | product_id,product_id_2 | product_id_2 | 4 | dropohs.product.id | 1 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | variant_global | ref | product_global_id,product_global_id_2 | product_global_id_2 | 4 | dropohs.product.product_global_id | 1 | Using where; Using index |
+------+--------------------+------------------+--------+-----------------------------------------------+---------------------+---------+-------------------------------------+------+----------------------------------------------+
如果您需要查询中使用的其他表的任何进一步信息或模式,请告诉我,但我现在忽略它们,因为我认为它们不是问题的原因,因为它们都正确使用索引.谢谢.
解决方法:
LEFT JOIN图像.. AND(image.id IS NOT NULL).如果您不想要没有图像的产品,那么INNER JOIN图像并省略AND image.id …标准.与LEFT JOIN product_category相同,如果您想在WHERE中使用非空类别,请不要使用LEFT JOIN,[INNER] JOIN是您实际请求的内容.
使用RAND()进行排序可能也是导致速度缓慢的原因.
变种*可用性的子查询应该是您在以后的MySQL版本中尝试使用window functions的内容.
尝试将所有类别合并到查询中并获取结果一次而不是多次.
也可以尝试将此查询结果缓存到数据库之外,这样您就不需要经常这样做了.
标签:mysql,optimization,schema,index 来源: https://codeday.me/bug/20190806/1601016.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。