Rails 4.1如何同时向两个PostgreSQL数据库发送写入请求?
解决Rails 4.1应用双写数据到两个PostgreSQL实例的方案
针对你的场景——需要在数据迁移期间让Rails应用同时将所有写入操作发送到老、新两个PostgreSQL实例,且不需要升级Rails 4.1版本,我整理了几个在ActiveRecord层面实现的可行方案,都是经过实践验证的:
1. 先配置双数据库连接
首先在config/database.yml中配置两个数据库连接,分别对应旧库(主业务库)和新库:
default: &default adapter: postgresql encoding: unicode pool: 5 production: primary: <<: *default database: old_db_production username: <%= ENV['OLD_DB_USER'] %> password: <%= ENV['OLD_DB_PASSWORD'] %> host: <%= ENV['OLD_DB_HOST'] %> new_db: <<: *default database: new_db_production username: <%= ENV['NEW_DB_USER'] %> password: <%= ENV['NEW_DB_PASSWORD'] %> host: <%= ENV['NEW_DB_HOST'] %>
开发、测试环境可以同理配置,方便本地验证双写逻辑。
2. 实现ActiveRecord双写补丁
我们可以给ActiveRecord::Base注入一个模块,重写所有写入相关的方法,让它们在执行主库操作后自动同步执行新库的操作。这种方式不需要修改业务代码,侵入性极低。
首先创建lib/dual_write.rb文件:
module DualWrite # 实例方法:处理单条记录的保存、更新、删除 def save(*) main_result = super sync_to_new_db { super } main_result end def save!(*) main_result = super sync_to_new_db { super } main_result end def update(*) main_result = super sync_to_new_db { super } main_result end def update!(*) main_result = super sync_to_new_db { super } main_result end def destroy(*) main_result = super sync_to_new_db { super } main_result end # 类方法:处理批量创建、更新、删除 module ClassMethods def create(*) record = super sync_to_new_db { super } record end def create!(*) record = super sync_to_new_db { super } record end def update_all(updates) main_result = super sync_to_new_db { super(updates) } main_result end def delete_all(conditions = nil) main_result = super sync_to_new_db { super(conditions) } main_result end end def self.included(base) base.extend(ClassMethods) end private def sync_to_new_db # 切换到新数据库连接 new_db_conn = self.class.establish_connection(:new_db) self.class.connection = new_db_conn # 如果主库用了事务,这里也建议包裹事务保证一致性 ActiveRecord::Base.transaction { yield } rescue StandardError => e # 关键:捕获新库异常,避免影响主业务流程 Rails.logger.error "双写新库失败: #{e.message}" Rails.logger.error e.backtrace.join("\n") ensure # 切回主数据库连接,保证后续操作正常 self.class.connection = self.class.establish_connection(:primary) end end
然后在config/initializers/dual_write.rb中加载这个模块:
require 'dual_write' ActiveRecord::Base.include(DualWrite)
3. 关键注意事项
- 事务一致性:如果业务代码依赖事务,一定要在新库操作中也包裹事务,避免主库提交成功但新库部分写入失败的情况。
- 性能影响:双写会增加单次写入的耗时,建议在 staging 环境先做性能测试;如果性能压力大,可以考虑用异步队列(比如Sidekiq)处理新库写入,但要注意最终一致性的校验。
- 数据校验:迁移期间定期校验新旧库的数据一致性,比如对比表行数、随机抽样检查记录,确保没有遗漏或错误。
- 查询逻辑:按照你的需求,查询操作默认走主库即可,不需要修改;如果需要验证新库数据,可以单独写查询方法指定新库连接。
备选方案:驱动层面代理
如果想在驱动层面实现,可以自定义PostgreSQL代理驱动,在发送SQL命令时同时转发到两个数据库。不过这种方式复杂度较高,需要处理连接池、事务同步等问题,灵活性不如ActiveRecord层面的方案,因此更推荐上面的补丁方案。
内容的提问来源于stack exchange,提问作者Vaibhav Kaushal
相关产品推荐
相关产品推荐

