Rails多租户应用:从单Postgres库切换为子域名独立数据库方案咨询
多租户应用从单库到独立数据库的改造方案
核心改造步骤
- 数据库连接管理:要在客户登录或请求上下文初始化阶段切换到其专属数据库,结合Apartment gem的
Apartment::Tenant.switch方法实现。 - 模型层调整:移除所有模型中的
account_id字段,同时删除基于该字段的默认查询范围(default scope),因为数据已实现物理隔离,无需再通过字段过滤。 - 租户数据库初始化:为每个客户创建独立的Postgres数据库,并同步原数据库的表结构、索引、约束等schema。可借助Apartment的
apartment:migrate批量为所有租户执行迁移。 - 请求上下文隔离:在Rails中通过中间件自动处理租户切换——每个请求开始时,从当前登录用户信息中提取租户标识,调用
Apartment::Tenant.switch完成切换;请求结束后切回默认数据库。
登录时的数据库连接处理
是的,必须在用户登录后(或每个请求入口)切换到对应租户的数据库。基于Apartment的推荐做法:
- 登录验证通过后,将租户标识(如account唯一ID、专属域名)存入session或当前用户对象。
- 编写中间件,在请求进入时从session/用户对象中取出租户标识,执行
Apartment::Tenant.switch(tenant_identifier);若未找到租户标识,直接跳转登录页或返回权限错误。 - 无需手动管理连接池,Apartment会在切换租户时自动建立数据库连接,但要确保
database.yml中配置好租户数据库的连接模板,供Apartment创建新租户库时使用。
现有数据迁移的最优方式
1. 全量导出-导入(适合大数据量场景)
- 按
account_id分组导出原单库数据,用Postgres的COPY命令生成CSV:COPY (SELECT * FROM users WHERE account_id = 'account_123') TO '/tmp/users_account_123.csv' WITH CSV HEADER; - 为每个租户创建好数据库并同步schema后,再用
COPY导入对应数据:
这种方式速度快,能最大限度减少迁移耗时。COPY users FROM '/tmp/users_account_123.csv' WITH CSV HEADER;
2. 结合Apartment的Rake任务迁移(适合需数据清洗场景)
- 先为所有租户创建数据库并执行schema迁移。
- 编写Rake任务遍历所有account,切换到对应租户库后,从原库查询数据批量插入:
这种方式灵活度高,可在迁移过程中完成数据清洗或格式转换,但速度略慢于直接COPY。desc "Migrate data from single db to tenant dbs" task migrate_tenant_data: :environment do Account.all.each do |account| Apartment::Tenant.switch(account.tenant_identifier) do # 迁移用户表数据,剔除account_id字段 User.unscoped.where(account_id: account.id).find_in_batches do |batch| User.insert_all(batch.map { |u| u.attributes.except('account_id', 'id') }) end # 依次处理其他业务表 end end end
迁移注意事项
- 迁移前务必备份原数据库,避免数据丢失。
- 迁移过程中建议暂停应用写操作,或开启读写分离,保证数据一致性。
- 迁移完成后,对比原库与租户库的记录数,验证数据完整性。
Apartment gem使用优化建议
- 在
config/initializers/apartment.rb中配置租户库命名规则,并禁用schema模式(因为用的是独立数据库):Apartment.configure do |config| config.use_schemas = false config.database_names = lambda { Account.pluck(:tenant_identifier) } end - 利用
prepend_view_path功能,为不同租户提供独立视图(如有定制需求)。
内容的提问来源于stack exchange,提问作者suhel rana
相关产品推荐
相关产品推荐

