连接Rails 3.1与多个数据库

77

在ShowNearby,我们一直在从PHP迁移到RoR 3.1,并面临着许多问题,可能有些是您之前解决过的。

我们拥有大量的数据,并决定将其分成几个数据库,以便我们可以单独处理。例如,我们的帐户、地点、日志等都分成了几个数据库。

我们需要让迁移、固定装置和模型协调工作,到目前为止还相当混乱。我们希望解决方案满足以下一些要求:

  • 一个模型应该与一个数据库中的一个表相关联。
  • rake db:drop - 应该删除我们在database.yml中指定的所有数据库环境。
  • rake db:create - 应该创建我们在database.yml中指定的所有数据库环境。
  • rake db:migrate - 应该对各个数据库运行迁移。
  • rake db:test - 应该获取固定装置并将其放入各个数据库和测试单元/函数/等中。

我们正在考虑为每个数据库设置单独的Rails项目,并使用ActiveResource将它们连接起来,但我们感觉这不太有效率。您有没有遇到过类似的问题?


我们正在考虑从 PHP 应用程序升级到 Rails,你有这方面的经验吗? - user3720516
嗨@Tommyixi:那是很久以前的事了,现在已经发生了很多变化。回顾过去,我认为现在将它们聚合到一个数据库中比将其拆分成多个数据库更好。 - Fer Martin
6个回答

142

对于Wukerplank的答案,你也可以像平常一样在database.yml中使用名称存储连接细节,如下所示:

log_database_production:
  adapter: mysql
  host: other_host
  username: logmein
  password: supersecret
  database: logs

然后在你的特殊模型中:

class AccessLog < ActiveRecord::Base
  establish_connection "log_database_#{Rails.env}".to_sym
end

为了避免在应用程序代码中出现这些讨厌的凭据。

编辑:如果您想在多个模型中重用此连接,应创建一个新的抽象类并从中继承,因为连接与类紧密耦合(如此处所述:这里这里这里),每个类将创建新的连接。

如果是这种情况,请按以下方式设置:

class LogDatabase < ActiveRecord::Base
  self.abstract_class = true
  establish_connection "log_database_#{Rails.env}".to_sym
end

class AccessLog < LogDatabase
end

class CheckoutLog < LogDatabase
end

@AzizLight establish_connection "log_database_#{Rails.env}" 请连接到名称为"log_database_#{Rails.env}"的数据库。 - Unixmonkey
12
请注意,使用这种方法似乎会让额外数据库的连接保持打开状态而不进行重用。在高负载下,这将导致您的应用程序变得非常缓慢。 - Altonymous
10
不错的观点。我想你指的是这种行为:https://github.com/rails/rails/issues/7019 连接与类耦合在一起;因此,如果您需要重用该连接,则应在抽象类上建立它并继承它,而不是继承AR::Base。我更新了我的答案以反映这一点。 - Unixmonkey
没错,完美的答案。如果你没来得及回答,我也会很快回答的。 :P - Altonymous
如果有人被搞糊涂了,我来解释一下:抽象类可能不会自动加载。我需要在每个模型文件的顶部手动require它所在的文件。 - user1618143
显示剩余2条评论

18

连接到不同的数据库非常容易:

# model in the "default" database from database.yml
class Person < ActiveRecord::Base

  # ... your stuff here

end

# model in a different database
class Place < ActiveRecord::Base

  establish_connection (
    :adapter  => "mysql",
    :host     => "other_host",
    :username => "username",
    :password => "password",
    :database => "other_db"
  )

end

如果你设置多个Rails项目,你会给控制器的数据检索增加很多开销,这可能会使事情变慢,因此我会对此保持谨慎。

至于你关于迁移、夹具、模型等方面的问题:我不认为有简单的方法,所以请单独发布问题,并尽可能具体。

将数据库合并为一个选项不是一个选择吗?这会让你的生活变得更容易!


2
问题在于以上示例中连接池无法正确使用。 - Sam Saffron

11

我找到了一篇很好的文章,可以指引他人正确地执行此操作,请查看http://blog.bitmelt.com/2008/10/connecting-to-multiple-database-in-ruby.html

设置方法如下:

database.yml (数据库配置文件)

support_development:
    adapter: blah
    database: blah
    username: blah
    password: blah

support_base.rb(一个模型文件)

class SupportBase < ActiveRecord::Base
    self.abstract_class = true #important!
    establish_connection("support_development")
end

tst_test.rb(一个模型文件)

class TstTest < SupportBase 
    #SupportBase not ActiveRecord is important!

    self.table_name = 'tst_test'

    def self.get_test_name(id)
        if id = nil
            return ''
        else
            query = "select tst_name from tst_test where tst_id = \'#{id}\'"
            tst = connection.select_all(query) #select_all is important!
            return tst[0].fetch('tst_name')
        end
    end
end

顺便说一下,这并没有涉及迁移,我认为你不能使用rake在多个数据库上进行迁移(虽然我不确定这是否是一个严格的“不能做”,可能是可行的)。这只是一种连接和查询其他你无法控制的数据库的好方法。


5
您可能还想附加Rails环境,这样您的开发和测试数据库就不会相同。
establish_connection "legacy_#{Rails.env}"

3

以下文章建议定义新的Rake任务,以实现针对多个数据库的迁移。每个任务都设置自己的连接,然后使用该连接和特定的数据库文件夹执行迁移。

它还定义了一个熟悉的db:migrate,调用另外两个任务。

为防止链接失效,在此处包含该文章:

desc "Migrate the database through scripts in db/migrate directory."

namespace :db do
  task :migrate do
    Rake::Task["db:migrate_db1"].invoke
    Rake::Task["db:migrate_db2"].invoke
  end

  task :migrate_db1 do
    ActiveRecord::Base.establish_connection DB1_CONF
    ActiveRecord::Migrator.migrate("db/migrate/db1/")
  end

  task :migrate_db2 do
    ActiveRecord::Base.establish_connection DB2_CONF
    ActiveRecord::Migrator.migrate("db/migrate/db2/")
  end
end

来源:Ruby on Rails连接到多个数据库和迁移


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接