我在一台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请求造成严重破坏。
在正在运行的网络应用中这样做是否安全?还有其他办法吗?
答案 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