如何处理Mongodb中的连接?

时间:2012-07-09 21:06:09

标签: mongodb postgresql join database

我在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集合。我不知道这是正确的解决方案。

我会很满意任何建议。

4 个答案:

答案 0 :(得分:2)

如果你不使用连接,那么关系数据库也可以很快地工作。我认为,出现这种情况需要进行非规范化。

选项1

is_spam列复制到网址表。当主机的此值更改时,更新所有相关的URL。如果你不经常这样做,这是可以的。

选项2

我不知道您的应用,但我认为垃圾邮件主机的数量相对较少。在这种情况下,您可以将其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。如果存在具有空主机或无效主机的网址,则结果可能与您的查询不同。