问题描述:
标签(标签)可以通过联结表(tagged_as)与任意对象相关联。对于特定对象类型(specific_object),选择与一系列标记关联的所有对象的并集或交集,按对象上的数字列对结果进行排序,并将结果限制为分页目的。
Contrived Schema:
CREATE TABLE tags (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(45) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE specific_object(
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(45) NOT NULL,
vote_sum INT NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
CREATE TABLE tagged_as(
id INT NOT NULL AUTO_INCREMENT,
tag_id INT NOT NULL,
content_type_id INT NOT NULL,
object_id INT NOT NULL,
PRIMARY KEY (id)
);
出于本示例的目的,我省略了specific_object表中的许多其他列。
表格行数:
标签:12,297
tagged_as:46,642,064
specific_object:2,444,944
天真的MySQL解决方案:
SELECT
specific_object.*
FROM
specific_object
JOIN
tagged_as
ON
specific_object.id = tagged_as.object_id
AND
tagged_as.content_type_id = <SPECIFIC_OBJECT_CONTENT_TYPE_ID>
WHERE
tagged_as.tag_id = <TAG_ONE_ID>
AND
tagged_as.tag_id = <TAG_TWO_ID>
...
ORDER BY specific_object.vote_sum DESC
LIMIT 50
此解决方案的问题是MySQL无法利用索引来解析ORDER BY子句,因为用于获取行的&#34;键与ORDER BY&#34;中使用的键不同。 (http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html)。执行时间:20+秒
天真的Redis解决方案:
for each specific object: SET specfic_object:<ID> <ID>
for each tagged as: SADD tag:<TAG ID> specific_object:<ID>
specific_object_ids = SUNION tag:<TAG_ONE_ID> tag:<TAG_TWO_ID> ...
specific_object_ids = SINTER tag:<TAG_ONE_ID> tag:<TAG_TWO_ID> ...
SELECT * FROM specific_object WHERE id IN (<specific_object_ids>) ORDER BY vote_sum DESC
此解决方案的问题在于,ORDER BY仍然必须由MySQL完成。此外,标签可能与数十万个特定对象相关联,这些对象是要移动的大量数据。执行时间:较大标签的20秒以上
可能的解决方案我还没试过
非规范化
也许将vote_sum列移动到tagged_as表中。不需要连接来执行订单。这可能与天真的解决方案有同样的问题。
Redis排序集
for each specific object: SET specific_object:<ID> <ID>
for each specific object: SET specific_object_weight:<ID> <VOTE_SUM>
for each tagged as: SADD tag:<TAG_ID> specific_object:<ID>
SINTERSTORE result:<timestamp> <TAG_ONE_ID> <TAG_TWO_ID> ...
SORT result:<timestamp> BY specific_object_weight_* LIMIT 0 50
specific_object_ids = SMEMBERS result:<timestamp>
DEL result:<timestamp>
SELECT * FROM specific_object WHERE id IN (<specific_object_ids>)
将所有排序移至Redis。这增加了额外的复杂性,因为现在您还必须在Redis中维护vote_sum值。不确定这是否足够快。
问题:
可能的解决方案是否可行?是否有其他解决方案或不同技术可以提供帮助?我愿意接受相当大的改变来解决这个问题。
答案 0 :(得分:0)
当问题出现在DESC排序中时,我过去所做的就是解决问题的方法是将-1*vote_sum
的值存储在一个单独的列中,然后ORDER BY该列ASC 。我已经能够让MySQL使用索引对该列进行排序。
您可以存储冗余列(vote_sum
和neg_vote_sum
,也可以只存储负值,只需将其乘以-1即可将其作为正值返回
但我怀疑你的性能问题的根源是排序操作。当您执行ORDER BY vote_sum ASC
时,语句的性能如何作为测试进行比较?