0

我在 PostgreSQL 中有两个表:urls(带有索引页面的表,主机是索引列,3000 万行)主机(带有主机信息的表,主机是索引列,100 万行)

我的应用程序中最常见的 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 表中选择数据时不必检查主机表,那么一切都会很容易。我听说 MongoDB 不支持连接,所以我的问题是如何解决上述问题?我可以将有关主机的信息放在 urls 集合中,但是字段 hosts.is_spam 可以由用户更新,我必须更新整个 urls 集合。我不知道这是正确的解决方案。

我会很感激任何建议。

4

4 回答 4

2

如果您不使用连接,那么关系数据库也可以很快工作。我认为,在这种情况下,您需要为了性能而进行非规范化。

选项1

is_spam列复制到 urls 表。当主机的这个值发生变化时,更新所有相关的 url。如果您不经常这样做,这没关系。

选项 2

我不知道您的应用程序,但我认为垃圾邮件主机的数量相对较少。在这种情况下,您可以将他们的 id 放入内存存储(memcached,redis,...),查询所有 url 并过滤掉应用程序中的垃圾邮件 url。这样你的分页就会有点破损,但有时这是一个可行的选择。

于 2012-07-09T21:18:00.227 回答
0

确实,MongoDB 不支持连接。在这种情况下,我会像这样构建我的urls收藏

urls : {
    name,
    some_other_property,
    host
}

然后,您可以获取特定 URL 的主机,并在集合中检查is_spam它的字段。hosts请注意,这需要由查询数据库的客户端完成,并且不能像使用 JOIN 那样在数据库本身完成。

于 2012-07-09T21:10:46.440 回答
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 文档更大)并且您只想获取尽可能少的数据,然后在本地进行过滤,然后批量获取完整的最终 url 文档。

第二个示例只是获取完整的 url 文档以启动,并在本地过滤它们。

于 2012-07-09T21:36:01.327 回答
0

你是对的,问题是连接,但我的猜测是它只是错误的连接。正如 Frank H. 所提到的,PostgreSQL 应该能够根据hosts.is_spam. 您可能希望对urls表进行集群id以优化 order by-limit 阶段。由于您只关心urls.*您可以通过创建部分索引来最小化磁盘 io,hosts.host以便is_spam is not null轻松获取要避免的主机的简短列表。

试试这个:

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。如果 URL 的主机为空或无效,则结果可能与您的查询不同。

于 2012-07-10T08:40:46.790 回答