“使用临时;使用文件排序”不是小人,架构和索引是。
这是tags 的最佳选择:
CREATE TABLE Tags (
item_id ...,
tag ... -- either a string or a tag_id, it does not matter much,
PRIMARY KEY(tag, item_id), -- for clustering and searching
INDEX(item_id) -- for maintenance (eg deleting an item_id)
-- no FOREIGN KEYs; they slow things down
-- no AUTO_INCREMENT; it is a total waste here
) ENGINE=InnoDB; -- so PK will be clustered.
因此,即使有数十亿行,这样的表也能非常有效地访问——所有“黄色”项目都将被“聚集”起来,并且只需要很少的磁盘命中。
(你建议tag_id 和另一张桌子。我认为这是否更好是一个折腾。)
计算磁盘命中 - 它们是大型表的主要性能指标。
对于select all laptops (tag) with Core i7 processor (another tag) and sort them by rating (t_item table); --
SELECT i.id
FROM Items AS i
JOIN Tags AS t1 ON t1.item_id = i.id
JOIN Tags AS t2 ON t2.item_id = i.id
WHERE t1.tag = 'laptops'
AND t2.tag = 'Core i7 processor'
ORDER BY i.rating DESC
LIMIT 10;
如果有 2000 台“笔记本电脑”,则可能是 20 个磁盘块(计为 20 个磁盘命中)。 150 'Core i7 处理器' 可能还有 2 次磁盘命中。如果这些导致 70 个“项目”,那么您将获得 70 个更多的磁盘命中,因为它们可能是随机定位的。是的,会有一个临时表(在 RAM 中)和一个文件排序(但没有物理“文件”)来对 70 个 id 进行排序并提供 10 个。
是的,您必须动态构造这个SELECT。
出于几个原因,我停止了(仅获取 i.id)...
Items 中还有什么?一些大的TEXT 列(description 等)?他们很讨厌;它们使工作变得混乱。
希望是说SELECT i.* 而不是SELECT i.id。让我们看看我们是否可以妥协。
假设你有INDEX(id, rating)。但是有充分的理由用 id 和 rating 制作一张桌子——你经常改变它,并且我们需要一张“小”桌子用于按评级查找和排序 70 个 id。
所以,让我们有两个表:Items -- id、描述等,以及 Ratings 仅:
CREATE TABLE Ratings (
item_id -- 1:1 with `Items.id`
rating ...,
PRIMARY KEY(id)
) ENGINE=InnoDB;
现在,将我的第一个查询更改为 FROM Ratings(而不是 FROM Items)。现在,我们仍然需要从该表中获取 70 行,但由于它更小,缓存的可能性更大,因此它可能涉及少于 70 次磁盘命中。
然后,我们需要仅从Items 中最终提取 10 个大件物品。
总计:20+2+70+10 = 102 次磁盘命中,或者在普通的旧旋转驱动器上大约 1 秒;在 SSD 上更快。 (另外希望最大的数字 70 会更低。)
回到文件排序... 通过只有“小”列(id、评级)和只有 70 个“行”要排序,文件排序将是一个内存结构,几乎不需要任何时间来执行。这就是我说忽略文件排序的原因。并且通过安排获取庞大的description稍后,我避免将其拖过 filsort,这几乎肯定会强制 tmp 表成为 MyISAM,而不是 MEMORY。
既然您提到了按评级或价格排序,也许这很好:
CREATE TABLE RatingsPrices (
item_id ..., -- 1:1 with `Items.id`
rating ...,
price ...,
INDEX(item_id, rating), -- covering index for the main query
INDEX(item_id, price), -- covering index for the main query
) ENGINE=InnoDB;
可能还有更多的细节和微妙之处,但我希望这些提示能让你朝着好的方向前进。
请记住:如果数据集太大以至于即使工作集也无法放入 RAM,请“计算磁盘命中数”。