【问题标题】:Ruby partially retrieve large amount of records and iterate over themRuby 部分检索大量记录并对其进行迭代
【发布时间】:2013-05-11 12:26:16
【问题描述】:

我是 Ruby 的新手,但我在其他编程语言方面有很多经验。我需要迭代大量记录(来自数据库或任何持久存储)。存储引擎允许我按范围检索部分记录。在 PHP 中,我通常编写自定义迭代器来加载记录范围,并在需要时加载记录的下一部分并忘记前一部分。脚本内存使用和存储请求计数之间的一些权衡。像这样的东西(复制自 cmets here):

class Database_Result_Iterator {
...
private $_db_resource = null;
private $_loaded = false;
private $_valid = false;

function rewind() {
    if ($this->_db_resource) {
        mysql_free($this->_db_resource);
        $this->_db_resource = null;
    }
    $this->_loaded = false;
    $this->_valid = false;
}

function valid() {
    if ($this->_loaded) {
        $this->load();
    }
    return $this->_valid;
}

private function load() {
    $this->_db_resource = mysql_query(...);
    $this->_loaded = true;
    $this->next(); // Sets _valid
}

}

这种方法在 Ruby 中是如何转变的? IE。我有一些类 Voter 和方法 get_votes 返回所有 votes 属于当前选民对象。可以检索的不是包含所有选票的数组,而是可以迭代的选票集合。我应该如何实现它?

更新

请不要将 ActiveRecord 和 RDBMS 视为仅一种可能的存储方式。 Redis 作为存储和 LRANGE 之类的命令又如何呢?我对在 Ruby 中解决此类问题的通用代码模式很感兴趣。

【问题讨论】:

  • 您可以在此处查看有关活动记录的文档:guides.rubyonrails.org/active_record_querying.html .. 关键字:例如限制和偏移量..
  • 那么 Redis 作为存储呢?我对在 Ruby 中解决此类问题的通用代码模式感兴趣。
  • 如果你使用redis,我会推荐使用redis gem..github.com/redis/redis-rb
  • 据我所知,gem 只实现了标准的 Redis 协议。但我正在寻找封装迭代逻辑的集合实现。基本上,无论什么样的存储引擎都会在这样的集合背后。
  • 如何部分检索记录?它们被完全检索或根本不检索。也许您的意思是要“检索部分记录”?

标签: ruby-on-rails ruby collections iterator persistent-storage


【解决方案1】:

来自 Ruby on Rails 上的 guides

User.all.each do |user|
  NewsLetter.weekly_deliver(user)
end

非常低效。首先,您可能希望在数据库中进行大部分过滤。 ActiveRecord 为此提供了一个名为find_each 的方法:

User.find_each(:batch_size => 5000) do |user|
  NewsLetter.weekly_deliver(user)
end

:batch_size 参数允许获取数据切片而不是获取整个结果集。在大多数情况下非常有帮助。

但是,您可能一开始就不想对所有记录进行操作:

User.with_newsletter.each do |user| 
   NewsLetter.weekly_deliver(user)
end

其中with_newsletter 是所谓的scope

【讨论】:

  • 谢谢,我看到了指南和这个例子。但我正在寻找用于手动实现的通用代码模式。或者决定不这样做=)
【解决方案2】:

我真的不明白这个问题的意义。 AR 是用于查询 RDBMS 的 API,这就是您在 AR 中的操作方式。

如果你想做 redis,你必须自己在驱动程序级别编写它,或者为 Redis 找到与 AR 类似的抽象......我认为 DataMapper 有一个 redis 适配器。 如果有一种通用的方法可以对任何数据存储执行此操作,它很可能在 DataMapper 中,但在创建自己的存储时要遵循的基本模式是查看 AR 如何实现 find_each/find_in_batches 并为您选择的存储执行此操作。

【讨论】:

  • 太棒了!我已经看过 find_each 的实现了。感谢您提及 DataMapper。
【解决方案3】:

听起来您想使用find_each (http://apidock.com/rails/ActiveRecord/Batches/ClassMethods/find_each)。这使您可以通过加载少量数据、迭代它们、然后加载另一个批次等方式来迭代大型数据集。

User.find_each do |user|
  user.do_some_stuff
end

将遍历所有用户,而不会一次将大量用户加载到内存中。

【讨论】:

  • 对数据库来说足够好,但不是常见的解决方案不是吗?
猜你喜欢
  • 1970-01-01
  • 2021-03-31
  • 2014-04-05
  • 1970-01-01
  • 1970-01-01
  • 2019-03-04
  • 2015-12-31
  • 2012-06-18
  • 2011-11-05
相关资源
最近更新 更多