在Rails中迭代多个数据库的安全方法

时间:2011-02-24 00:58:58

标签: mysql ruby-on-rails ruby multithreading connection

我在一台MySQL服务器上运行了几个Rails应用程序。它们都运行相同的应用程序,并且所有数据库都具有相同的架构,但每个数据库属于不同的客户。

从概念上讲,这就是我想要做的事情:

   Customer.all.each do |customer|
      connection.execute("use #{customer.database}")
      customer.do_some_complex_stuff_with_multiple_models
   end

此方法不起作用,因为当在Web请求中运行此方法时,基础模型类将缓存来自A / R连接池的不同数据库连接。因此,我执行“use”语句的连接可能不是模型使用的连接,在这种情况下,它查询错误的数据库。

我通读了Rails A / R代码(版本3.0.3),并提出了在循环中执行的代码,而不是“use”语句:

ActiveRecord::Base.clear_active_connections!
ActiveRecord::Base.establish_connection(each_customer_database_config)

我认为连接池是每个线程的,所以看起来这会破坏连接池并仅为Web请求所在的一个线程重新建立它。但是,如果以某种方式共享连接,我没有看到,我不希望该代码在同一个应用程序中对其他活动Web请求造成严重破坏。

在正在运行的网络应用中这样做是否安全?还有其他办法吗?

4 个答案:

答案 0 :(得分:1)

IMO切换到不同请求的新数据库连接是一项非常昂贵的操作。 AR维持有限的连接池。

我想你应该转到PostgreSQL,你有模式的概念。

在理想的SQL世界中,这是数据库的结构

database --> schemas --> tables

在MYSQL中,数据库和模式是一回事。 Postgres具有单独的模式,可以为不同的客户保存表格。您可以通过设置

动态切换架构而无需更改AR连接
ActiveRecord::Base.connection.set_schema_search_path("CUSTOMER's SCHEMA")

开发它需要一些黑客攻击。

答案 1 :(得分:1)

通过连接/断开来切换数据库非常慢,并且由于AR连接池是内部缓存而无法工作。尝试使用ActiveRecord::Base.table_name_prefix = "customer_"并保持数据库不变。

答案 2 :(得分:0)

现在,ActiveRecord中的连接可以是每个级别的。它看起来是每个线程的基础,因为在1.9 ruby​​线程被吸入之前,所以实现使用进程而不是线程,但它可能不长实。

但是因为AR每个模型使用一个线程。您可以为每个数据库创建不同的模拟模型。所以使用this question中给出的答案。

代码看起来像这样。 (我没有测试过它)

Customer.all.each do |customer|
     c_class = Class.new(ActiveRecord::Base)
     c_class.establish_connection(each_customer_database_config)
     c_class.table_name = customor.table_name()
     c_class.do_something_on_diff_models_using_cutomer_from_diff_conn(customer.id)
     c_class.clear_active_connections!
end

答案 3 :(得分:0)

为什么不保留相同的数据库和表格,只让每个模型属于客户?然后,您可以通过以下方式找到该客户的所有模型:

Customer.all.each do |customer|
  customer.widgets
  customer.wodgets
  # etc
end