`
holin
  • 浏览: 17895 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

慎用establish_connection

阅读更多

在一个Rails应用中连接多个数据库,我们常常这么做:

class Cookie < ActiveRecord::Base
    establish_connection :monitor_spider
    ...
end

这样,在使用Cookie.find等操作的时候,就会连接到database.yml中monitor_spider配置的数据库上操作。

以前一直都这么用,没发现什么不妥。最近一个项目,由于启动的进程比较多,老是碰到数据库连接池链接获取超时的错误。通过MySQL Client用命令:show processlist;

 

发现数据库连接数量一直居高不下,轻轻松松就上2k+的连接。通过读Rails框架的connection_pool.rb文件代码,发现在各模型中用establish_connection连接数据库会造成很大的问题。文件中类ConnectionHandler的establish_connection方法代码如下:

def establish_connection(name, spec)
        @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
end
 对应上面Cookie模型的配置,这里name的值会是Cookie。也就是说,有10个模型中使用了establish_connection配置,@connection_pools中就会初始化10个ConnectionPool,如果每个ConnectionPool的pool size是20,那么应用运行的时候,将需要200(10 × 20)个数据库连接。而我的应用中有两类模型(连接到两个数据库),模型总数40+。按establish_connection的方式设置数据库连接,那么mysql的连接轻易就被消耗完了。

那要怎么处理呢?

 

我现在的思路是自己来维护数据库连接池。

 

首先,对ConnectionAdapters进行一下改造,将如下代码放到initializers中:

module ActiveRecord  
  module ConnectionAdapters 
    class ConnectionHandler
      def specified_establish_connection(name, spec)
        @connection_pools[name] = ConnectionAdapters::ConnectionPool.new(spec)
      end
       
      def retrieve_connection_pool(klass)
        if klass.respond_to?(:pool_name)
          pool = @connection_pools[klass.pool_name]
          # ActiveRecord::Base.logger.info "pool: #{pool.inspect}"
          return pool if pool 
        end
        pool = @connection_pools[klass.name]
        # puts pool.inspect
        return pool if pool
        return nil if ActiveRecord::Base == klass
        retrieve_connection_pool klass.superclass
      end
    end
  end
end

 

在environment.rb中事先连接建立数据库连接池:

#specified pool
config =  YAML::load(File.open("config/database.yml"))
%w(monitor_center monitor_spider).each do |pool_name|
  spec = config[pool_name]
  adapter_method = "#{spec["adapter"]}_connection"
  ActiveRecord::Base.connection_handler.specified_establish_connection(pool_name, ActiveRecord::Base::ConnectionSpecification.new(spec, adapter_method)) 
end
 在模型中,定义方法pool_name:

class TaskStat < ActiveRecord::Base
  def self.pool_name;"monitor_spider";end
  ...
end 


class TaskInfo < ActiveRecord::Base
  def self.pool_name;"monitor_center";end
  ...
end

 

这样,TaskStat就会用monitor_spider pool,TaskInfo会用monitor_center pool。同时整个应用就只有三个ConnectionPool(外加默认的ActiveRecord::Base的)

通过这样的改造之后,效果明显,现在show processlist一般只有200~300的连接数。

 

首发:http://www.holin.info/posts/4c94cf27ff1f0146ef000002

 

分享到:
评论
7 楼 hiveer 2013-10-14  
如果能讲的更细些就好了,看不大懂
6 楼 holin 2010-10-11  
xds2000 写道
一个疑问想问问楼主的场景:
  database.yml中是有pool关键字的,类如:
development:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: flintstone_development
  pool: 5
  username: root
  password:
  socket: /var/lib/mysql/mysql.sock

楼主的案例场景说发现很多链接,想问问这pool有没起作用。或者说并发限制有没受database.yml限制?


pool参数就是设置的pool的大小。我这个案例场景里会实例化很多pool,所以导致链接过多。

ps: 我的应用是在Rails2.3版本下的,Rails3下并采用mysql2驱动我还不了解情况。
5 楼 xds2000 2010-10-10  
一个疑问想问问楼主的场景:
  database.yml中是有pool关键字的,类如:
development:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: flintstone_development
  pool: 5
  username: root
  password:
  socket: /var/lib/mysql/mysql.sock

楼主的案例场景说发现很多链接,想问问这pool有没起作用。或者说并发限制有没受database.yml限制?
4 楼 holin 2010-10-03  
sword0607 写道
不需要这么麻烦吧,你建立一个继承自ActiveRecord::Base的ExternalTable类,在ExternalTable类中使用establish_connection方法,其他需要使用外部表的model类都继承此ExternalTable类,就可以避免多个model连接数据库的问题了。

请参考RailsRecipe中的Recipe 15


的确是这样方便,之前没有看到这个内容,走了冤枉路。
3 楼 飞语001 2010-10-01  
学习学习。
2 楼 下一站,火星 2010-10-01  
这种的方案不错,还以为是楼主自创的……,不过分享还是的挺好的
1 楼 sword0607 2010-09-30  
不需要这么麻烦吧,你建立一个继承自ActiveRecord::Base的ExternalTable类,在ExternalTable类中使用establish_connection方法,其他需要使用外部表的model类都继承此ExternalTable类,就可以避免多个model连接数据库的问题了。

请参考RailsRecipe中的Recipe 15

相关推荐

Global site tag (gtag.js) - Google Analytics