我在PostgreSQL中有两个表: urls(带有索引页面的表,主机是索引列,30行行) hosts(包含主机信息的表,主机是索引列,1mln行)
我的应用程序中最常见的SELECT之一是:
SELECT urls.*
FROM urls
JOIN hosts ON urls.host = hosts.host
WHERE urls.projects_id = ?
AND hosts.is_spam IS NULL
ORDER by urls.id DESC, LIMIT ?
在urls表中行数超过100 000的项目中,查询执行速度非常慢。
由于表已经增长,查询的执行速度越来越慢。我已经阅读了很多关于NoSQL数据库(如MongoDB)的内容,这些数据库旨在处理如此庞大的表格,并考虑将我的数据移动到MongoDB。如果我不必在从urls表中选择数据时检查hosts表,那么一切都会很简单。我听说MongoDB不支持连接,所以我的问题是如何解决上述问题?我可以在urls集合中放置有关host的信息,但是hosts.is_spam字段可以由用户更新,我将不得不更新整个urls集合。我不知道这是正确的解决方案。
我会很满意任何建议。
答案 0 :(得分:2)
如果你不使用连接,那么关系数据库也可以很快地工作。我认为,出现这种情况需要进行非规范化。
将is_spam
列复制到网址表。当主机的此值更改时,更新所有相关的URL。如果你不经常这样做,这是可以的。
我不知道您的应用,但我认为垃圾邮件主机的数量相对较少。在这种情况下,您可以将其ID放入内存存储(memcached,redis,...),查询所有网址并过滤掉应用中的垃圾网址。这样你的分页就会有点破碎,但有时这是一个可行的选择。
答案 1 :(得分:0)
MongoDB确实不支持连接。在这种情况下,我会像这样构建我的urls
集合
urls : {
name,
some_other_property,
host
}
然后,您可以获取特定网址的主机,并在is_spam
集合中查看hosts
字段。请注意,这需要由客户端查询数据库来完成,而不能像在JOIN中那样在数据库本身完成。
答案 2 :(得分:0)
与@xbones的答案相似,但具体的例子
在host_id
文档中放置urls
字段是一种可行的方法。它将要求您首先提取url文档的结果,然后提取垃圾邮件主机的结果,然后在您的客户端代码中本地过滤
大致是:
var urls = db.urls.find({projects_id:'ID'}, {_id: 1, host_id: 1});
var hosts = db.hosts.find({is_spam: 1}, {_id: 1});
# psuedocode
ids_array = _id for _id in urls if host_id is not in hosts
urls = db.urls.find({_id: {$in: ids_array}});
或者:
var urls = db.urls.find({projects_id:'ID'});
var hosts = db.hosts.find({is_spam: 1}, {_id: 1});
# psuedocode
urls = url for url in urls if host_id is not in hosts
第一个示例假设project_id
查询的结果可能很大(并且您的网址文档较大),您只想获取尽可能少的数据,然后在本地过滤,然后批量获取完整的最终网址文件。
第二个示例只是获取完整的url文档,并在本地过滤掉它们。
答案 3 :(得分:0)
你的问题是连接是正确的,但我的猜测是它只是错误的连接类型。正如Frank H.所提到的,PostgreSQL应该能够轻松地处理这种类型的查询,具体取决于hosts.is_spam
的频率。您可能希望在urls
上对id
表进行聚类,以优化订单限制阶段。由于您只关心urls.*
,因此可以通过在hosts.host
is_spam is not null
上创建部分索引来最小化磁盘io,以便轻松获取要避免的主机短列表。
试试这个:
select urls.*
from urls
left join hosts
on urls.host = hosts.host
and hosts.is_spam is not null
where urls.projects_id = ?
and hosts.host is null
或者这个:
select *
from urls
where urls.projects_id = ?
and not exists (
select 1
from hosts
where hosts.host = urls.hosts
and hosts.is_spam is not null
)
这将允许PostgreSQL使用反连接来仅提取未映射到已知垃圾邮件主机的URL。如果存在具有空主机或无效主机的网址,则结果可能与您的查询不同。