【问题标题】:How to efficiently search for last record matching a condition in Rails and PostgreSQL?如何有效地搜索匹配 Rails 和 PostgreSQL 中条件的最后一条记录?
【发布时间】:2011-11-21 18:02:49
【问题描述】:

假设您要查找输入数据库的最后一条记录(最高 ID) 匹配字符串:Model.where(:name => 'Joe')。有 100,000 多条记录。有很多匹配项(比如数千个)。

最有效的方法是什么? PostgreSQL 需要查找所有记录,还是只查找最后一条?这是一个特别慢的查询吗?

在 Rails 3.0.7、Ruby 1.9.2 和 PostgreSQL 8.3 中工作。

【问题讨论】:

  • 正确设置索引应该不会很慢。
  • 那么,这意味着只是name 上的索引?什么样的索引重要吗?

标签: ruby-on-rails ruby-on-rails-3 postgresql activerecord rails-postgresql


【解决方案1】:

这里的重要部分是要有一个匹配索引。你可以试试这个小测试设置:

创建架构x用于测试:

-- DROP SCHEMA x CASCADE;  -- to wipe it all for a retest or when done.
CREATE SCHEMA x;
CREATE TABLE x.tbl(id serial, name text);

插入 10000 个随机行:

INSERT INTO x.tbl(name) SELECT 'x' || generate_series(1,10000);

插入另外 10000 行重复名称:

INSERT INTO x.tbl(name) SELECT 'y' || generate_series(1,10000)%20;

删除随机 10% 使其更真实:

DELETE FROM x.tbl WHERE random() < 0.1;

ANALYZE x.tbl;

查询可能如下所示:

SELECT *
FROM   x.tbl
WHERE  name = 'y17'
ORDER  BY id DESC
LIMIT  1;

--> 总运行时间:5.535 毫秒

CREATE INDEX tbl_name_idx on x.tbl(name);

--> 总运行时间:1.228 毫秒

DROP INDEX x.tbl_name_idx;
CREATE INDEX tbl_name_id_idx on x.tbl(name, id);

--> 总运行时间:0.053 毫秒

DROP INDEX x.tbl_name_id_idx;
CREATE INDEX tbl_name_id_idx on x.tbl(name, id DESC);

--> 总运行时间:0.048 毫秒

DROP INDEX x.tbl_name_id_idx;
CREATE INDEX tbl_name_idx on x.tbl(name);
CLUSTER x.tbl using tbl_name_idx;

--> 总运行时间:1.144 毫秒

DROP INDEX x.tbl_name_id_idx;
CREATE INDEX tbl_name_id_idx on x.tbl(name, id DESC);
CLUSTER x.tbl using tbl_name_id_idx;

--> 总运行时间:0.047 毫秒

结论

使用合适的索引,查询的执行速度快了 100 倍以上
表现最佳的是多列索引,过滤列在前,排序列在后。
在这种情况下,匹配索引中的排序顺序会有所帮助。

聚类有助于简单的索引,因为仍然需要从表中读取许多列,并且可以在聚类后在相邻块中找到这些列。在这种情况下,它对多列索引没有帮助,因为只需从表中获取一条记录。
阅读有关multicolumn indexes in the manual 的更多信息。

所有这些影响都随着表格的大小而增长。 10000 行的两个小列只是一个非常小的测试用例。

【讨论】:

    【解决方案2】:

    您可以将查询放在 Rails 中,ORM 将编写正确的 SQL:

    Model.where(:name=>"Joe").order('created_at DESC').first
    

    这不会导致检索所有模型记录,甚至不会导致表扫描。

    【讨论】:

    • 您声称这不应导致表扫描的依据是什么?如果没有合适的索引,它将总是导致表扫描。
    • 假设 SELECT 会避免它。
    • 这不是 PostgreSQL(或任何其他 RDBMS)的工作方式。没有合适的索引意味着表扫描。没有其他办法。 (不过,主键列会自动编制索引。)
    • 我相信where子句会限制结果并避免表扫描。
    【解决方案3】:

    这可能是最简单的:

    SELECT [columns] FROM [table] WHERE [criteria] ORDER BY [id column] DESC LIMIT 1
    

    注意:索引在这里很重要。如果您没有以正确的方式建立索引,那么无论您如何搜索一个巨大的数据库都会很慢。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多