在Rails中使用gh-ost执行列删除操作时遇到的异常问题
我之前也碰到过一模一样的问题——用gh-ost做在线迁移来解耦应用代码和数据库变更,大部分场景都跑的很顺畅,唯独在删除列之后,Rails会毫无征兆地抛出异常,哪怕代码里完全没用到那个被删除的列。
核心原因其实就是Rails的schema缓存机制:Rails启动时会把数据库的schema结构缓存起来(生产环境默认开启),当你用gh-ost直接在数据库层面删除列后,Rails的缓存里还保留着那个已不存在的列信息,导致ActiveRecord在处理模型对象时,依然试图去访问这个列,最终触发异常。
下面是几个经过验证的解决方案,按推荐程度排序:
方案一:手动刷新schema缓存(无需重启应用)
Rails提供了直接操作schema缓存的方法,你可以在应用控制台执行以下代码,或者封装成一个rake任务批量执行:Rails.application.eager_load! ActiveRecord::Base.connection.schema_cache.clear! ActiveRecord::Base.connection.schema_cache.reload!这个操作会强制Rails从数据库重新读取最新的schema结构,覆盖旧的缓存。如果是多服务器部署,记得要在每台应用服务器上都执行一遍,或者通过部署脚本自动触发。
方案二:在部署流程中自动刷新缓存
如果你用Capistrano这类部署工具,可以在部署脚本里添加一个任务,每次部署后自动刷新schema缓存:after 'deploy:published', 'deploy:refresh_schema_cache' namespace :deploy do desc 'Refresh Rails schema cache' task :refresh_schema_cache do on roles(:app) do within release_path do with rails_env: fetch(:rails_env) do execute :rake, 'db:schema:cache:clear' execute :rake, 'db:schema:cache:dump' end end end end end这样每次部署完成后,都会重新生成schema缓存文件,确保应用使用的是最新的数据库结构。
方案三:临时禁用schema缓存(仅测试环境可用)
如果只是在测试环境临时排查问题,可以在config/environments/production.rb(或者对应环境的配置文件)里关闭schema缓存:config.active_record.schema_cache_dump = false注意:这个方法会让Rails每次请求都去读取数据库schema,严重影响生产环境的性能,所以绝对不要在生产环境使用。
额外提醒:执行gh-ost删列操作前,一定要确保所有应用实例都已经完成了代码变更——彻底移除了对该列的所有显性和隐性引用,比如序列化属性、默认scope、甚至第三方gem的自动加载逻辑,最好先做一次全面的代码检查,避免后续出现其他隐性问题。
内容的提问来源于stack exchange,提问作者Paul Holden

