了解 ActiveRecord 中的 establish_connection 如何工作

36

这段代码摘自ActiveRecord 2.3.14的gem类ConnectionHandler

def establish_connection(name, spec)
  @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
end
每次 Ruby 在模型上调用 `establish_connection` 时,似乎都会创建一个新的连接池。
我的问题是:
如果我有 5 个使用 `establish_connection` 连接到同一数据库的模型,Rails 是否足够聪明以选择已经存在的连接池,而不是使用相同的连接凭证创建新的连接池呢?如果我的 5 个模型是某个抽象类的子类,并且该抽象类使用 `establish_connection`,这是否也会发生?它是否总是从 `@connection_pools` 中获取现有的连接?
更新1:
我想谈论一个具体的例子。您有 5 个带有 5 种不同连接的模型,每次 Rails 使用一个模型时都会执行 `establish_connection`。查看 ActiveRecord 中的代码,当执行 `establish_connection` 时,它会创建一个到该特定连接的连接池。我想知道的是,每次 Rails 调用模型的 `establish_connection` 时,它是否会创建一个新的连接池还是获取现有的连接池。
例如:您来到我的网站并查看产品列表。您刚刚执行了 `Product.all`,它执行了 `establish_connection` 连接到亚马逊上的某个数据库。然后,我来到产品列表,会发生什么?我会获取已建立的连接还是会创建一个新的连接池?
更新2:
我的猜测是,第一次 Rails 加载我的模型时,它会创建具有不同连接的连接池。随后,当我使用一些 `Model.method` 时,它只需获取与该模型相关联的连接并执行该方法。
我不确定当两个模型具有相等的连接时会发生什么情况(不在抽象类中而是在自身类中)。这是否会产生两个相同的连接池,或者 ActiveRecord 足够聪明以捕获此情况?

4
顺便说一句,非常好的问题。 - bor1s
3个回答

19

AR的调用仅为ActiveRecord::Base建立一次连接,所有子类都使用同一个连接。

您可以在某些子类上手动调用establish_connection。这对于同时使用两个数据库非常方便,例如:

class MyMainUser < ActiveRecord::Base; end 
class MyOtherDb < ActiveRecord::Base; end
class MyOtherUser < MyOtherDb; end

MyOtherDb.establish_connection ...

MyMainUser.first # uses default db
MyOtherUser.first # uses other db

你无法执行跨数据库的查询。


1
我不得不在MyOtherDb类中添加self.abstract_class = true才能运行插入操作。否则它会尝试获取my_other_dbs表,但该表不存在。 - iamprem

10

你真的不需要在每个模型上调用establish_connection。你可以采取以下简单方式:

ActiveRecord::Base.establish_connection(
 { :adapter => 'mysql2',
   :database => 'some_database',
   :host => 'localhost',
   :username => 'root',
   :password => "" }
)

你将可以访问连接。(这段代码已从真实代码中提取(除了数据库名称 :))。
但根据API,我认为Rails不会从其他模型中获取现有的连接(如果我错了请纠正)。
此外,这里有一个文档链接,你可以在那里了解更多关于连接的内容。
希望对你有所帮助。


当调用模型方法时,它会抓取,但在初始化状态下,当它仅加载模型并执行establish_connection时,查看代码,它只是创建一个池,无论它是否已存在。@connection_pool哈希中的键不同,但值相同。 - Filip
1
如果两个不同的模型建立连接,它们将进入池中(在某些模型中建立连接,然后键入 ActiveRecord::Base.connection_handler)。我认为连接只是收集,每个连接都可以单独连接到数据库。 - bor1s

3
这条评论:
# Check-out a database connection from the pool, indicating that you want
# to use it. You should call #checkin when you no longer need this.
#
# This is done by either returning an existing connection, or by creating
# a new connection. If the maximum number of connections for this pool has
# already been reached, but the pool is empty (i.e. they're all being used),
# then this method will wait until a thread has checked in a connection.
# The wait time is bounded however: if no connection can be checked out
# within the timeout specified for this pool, then a ConnectionTimeoutError
# exception will be raised.

来源:https://github.com/rails/rails/blob/dd944cbf5879e675fff541d1be7c7eb6c3382d01/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L242-251 应该解释一下情况。

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