我想要一个查询在一个字段上进行全文搜索,然后在另一个字段上进行排序(想象一下搜索一些文本文档并按发布日期排序)。该表有大约17M行,它们在日期中或多或少均匀分布。这将在webapp请求/响应周期中使用,因此查询必须在最多200ms内完成。
示意性地:
SELECT * FROM table WHERE MATCH(text) AGAINST('query') ORDER BY date=my_date DESC LIMIT 10;
一种可能性是在文本字段上使用全文索引,在发布日期使用btree:
ALTER TABLE table ADD FULLTEXT index_name(text);
CREATE INDEX index_name ON table (date);
在我的情况下,这不是很好。会发生什么是MySQL评估两个执行路径。一种是使用全文索引来查找相关行,一旦选中它们,使用FILESORT对这些行进行排序。第二种是使用BTREE索引对整个表进行排序,然后使用FULL TABLE SCAN查找匹配项。他们都很糟糕。在我的情况下,MySQL选择前者。问题是,第一步可以选择一些30k的结果然后必须排序,这意味着整个查询可能需要10秒的订单。
所以我在想:FULLTEXT + BTREE的复合索引是否存在?如果您知道FULLTEXT索引的工作原理,它首先将您要编制索引的列标记化,然后为该标记构建索引。对于我来说,想象一个复合索引似乎是合理的,这样第二个索引在每个令牌的日期中都是BTREE。这是否存在于MySQL中,如果是,那么语法是什么?
奖金问题:如果MySQL中不存在,那么PostgreSQL会在这种情况下表现更好吗?
答案 0 :(得分:1)
使用IN BOOLEAN MODE
。
日期索引无效。无法合并这两个索引。
请注意,如果用户搜索出现在30K行中的内容,则查询速度会很慢。周围没有直接的方法。
我怀疑桌子上有TEXT
列?如果是这样,那就有希望了。我们不是盲目地SELECT *
,而是首先找到ID并应用LIMIT
,然后执行*
。
SELECT a.*
FROM tbl AS a
JOIN ( SELECT date, id
FROM tbl
WHERE MATCH(...) AGAINST (...)
ORDER BY date DESC
LIMIT 10 ) AS x
USING(date, id)
ORDER BY date DESC;
与
一起PRIMARY KEY(date, id),
INDEX(id),
FULLTEXT(...)
这种表述和索引应该像这样工作:
FULLTEXT
查找30K行,然后发送PK。date
排序30K行。date, id
更多(回应过多评论):
我重新制定的目标是避免获取 30K 行的所有列。相反,它只提取PRIMARY KEY
,然后将其降至10,然后只提取10行*
。铲掉的东西少得多。
关于InnoDB表上的COUNT
:
SELECT COUNT(*)
或SELECT COUNT(col)
而不使用WHERE
。INDEX(col),
SELECT COUNT(*)will use the "smallest" index; but
SELECT COUNT(col)`需要表扫描。关于FULLTEXT
的另一件事是单词前面的+
- 说每个单词必须存在,否则就没有匹配。这可能会减少30K。
FULLTEXT
索引将传递date, id
随机顺序,而不是PK顺序。无论如何,这是错误的'假设任何排序,因此它是正确的#39;要添加ORDER BY
,如果知道它是多余的,请让优化器抛弃它。有时,优化工具可以利用ORDER BY
(不是您的情况)。
在许多情况下,只删除ORDER BY
会使查询运行得更快。这是因为它可以避免获取30K行并对其进行排序。相反,它只是提供"任何" 10行。
(我没有使用过Postgres,所以我无法解决这个问题。)