我有一个包含大量产品的网站,每个产品都使用一个算法版本进行评分,列出后,会连接到下表中:
CREATE TABLE `product_score` (
`product_id` int(10) UNSIGNED NOT NULL,
`cohort_id` int(10) UNSIGNED NOT NULL,
`score` float UNSIGNED NOT NULL,
`last_updated` int(10) UNSIGNED NOT NULL DEFAULT 1709444313,
`order` int(10) UNSIGNED NOT NULL DEFAULT 0
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `product_score`
ADD PRIMARY KEY (`product_id`,`cohort_id`),
ADD KEY `score` (`score`),
ADD KEY `order` (`order`),
ADD KEY `product_id` (`product_id`,`cohort_id`,`order`);
大约有 20k 个产品,每个产品都有一个唯一的“顺序”(0-20000),具体取决于表示该条目所针对的算法版本的同类群组 ID。
有问题的查询示例:
SELECT product.*, user.*, additional_data.* FROM product
INNER JOIN product_score AS Score ON Score.product_id = product.product_id AND Score.cohort_id = '24')
LEFT JOIN user ON user.user_id = product.user_id
LEFT JOIN additional_data...
ORDER BY Score.order LIMIT 20
很敏捷。然而,使用 order by 则需要 1-2 秒。
就附加索引而言,我认为没有任何改进的空间。我已经对查询运行了 EXPLAIN,并且 Product_id 键也在连接中得到了正确的利用。
我注意到,当删除左连接时,查询只需要 0.2 秒而不是 1 秒(与没有 order by 相比仍然很慢,但没有那么慢)。但是,该数据是查询所必需的,因此我不能简单地将其删除。我对 DB io 的了解也不够,无法推断它的含义或如何最好地解决它。
更换
ADD KEY `product_id` (`product_id`,`cohort_id`,`order`);
与
INDEX(cohort_id, `order`, product_id)
如果这还不够加快速度,请重写查询以在子查询中查找 20 行,然后再查看其他列或表:
SELECT product.*, user.*, additional_data.*
FROM ( SELECT product_id, `order`
FROM Score.cohort_id = '24'
ORDER BY Score.order
LIMIT 20 ) AS s1
JOIN product USING(product_id)
LEFT JOIN user USING(user_id)
LEFT JOIN additional_data...
ORDER BY s1.order