我有一个庞大的项目数据库,数十亿条目:
t_item
itemId ...
每个项都标有多个标记:
t_tag
tagId tagName
t_item_tag
itemTagId [AI] itemId tagId
最终用户的用例场景很简单:
选择标有特定标签的所有项目,并按某些属性对其进行排序。 E.g:
- 使用 Core i7处理器(另一个标签)选择所有笔记本电脑(标签),并按评级(t_item表)排序;
- 选择皮革(标签)制作的所有黄色(标签) 包(标签)并按价格(t_item)对其进行排序表)
目前我在MySQL上运行该系统,但它开始达到它的极限。主要关注的是最终排序已完成使用临时;使用filesort ,这有点慢。
我可以使用哪些其他架构来处理我的用例和卷?
答案 0 :(得分:1)
“使用temp;使用filesort”不是恶棍,架构和索引都是。
这是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'核心i7处理器'可能还有2个磁盘命中率。如果那些导致70'项目',那么你将获得70多个磁盘命中,因为它们可能是随机定位的。是的,会有一个临时表(在RAM中)和一个filesort(但没有物理“文件”)来排序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,会更低。)
回到filesort ...通过只有'小'列(id,rating)并且只有70'行'进行排序,filesort将是一个内存结构,几乎没有时间执行。这就是为什么我说要忽略filesort。通过安排稍后获取庞大的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,“计算磁盘命中数”。