Rails迁移回滚时如何高效截断超长String字段数据?
解决Rails迁移回滚时数据过长导致崩溃的问题
我正在执行一项增大String字段长度限制的任务,已编写如下Rails迁移文件:
class IncreaseCompanyNumberLimit < ActiveRecord::Migration[6.1] def up if Company.columns.find { |c| c.name == 'company_number' }.limit < 25 change_column :company, :company_number, :string, limit: 25 end end def down change_column :company, :company_number, :string, limit: 20 end end
遇到的问题:执行up迁移后,若有用户输入了长度超过20的company_number,执行down迁移时会因现有数据过长而崩溃。需要寻找一种高效的方法截断超出down方法设置的长度限制的数据,且涉及的表数据量较大。
解决方案:先截断数据再修改字段长度
由于表数据量大,避免用ActiveRecord的Ruby层面操作(比如find_each循环更新),直接用原生SQL处理是最高效的方式。修改down方法如下:
1. 基础版(适合数据量较大但未到超大规模)
class IncreaseCompanyNumberLimit < ActiveRecord::Migration[6.1] def up if Company.columns.find { |c| c.name == 'company_number' }.limit < 25 change_column :companies, :company_number, :string, limit: 25 end end def down # 根据数据库类型选择对应的字符串截断函数 # MySQL/MariaDB 用 LEFT (注意区分字节数/字符数:CHAR_LENGTH是字符数,LENGTH是字节数) execute <<-SQL UPDATE companies SET company_number = LEFT(company_number, 20) WHERE CHAR_LENGTH(company_number) > 20; SQL # PostgreSQL 用 SUBSTRING 或 LEFT # execute <<-SQL # UPDATE companies # SET company_number = SUBSTRING(company_number FROM 1 FOR 20) # WHERE LENGTH(company_number) > 20; # SQL # 执行字段长度修改 change_column :companies, :company_number, :string, limit: 20 end end
2. 分批更新版(适合超大规模数据,避免锁表超时)
如果表中数据量极大(数百万级以上),单次UPDATE可能会锁表时间过长影响业务,此时可以分批执行更新:
class IncreaseCompanyNumberLimit < ActiveRecord::Migration[6.1] def up if Company.columns.find { |c| c.name == 'company_number' }.limit < 25 change_column :companies, :company_number, :string, limit: 25 end end def down batch_size = 1000 last_id = 0 loop do # MySQL/MariaDB 版本 result = execute <<-SQL UPDATE companies SET company_number = LEFT(company_number, 20) WHERE id > #{last_id} AND CHAR_LENGTH(company_number) > 20 LIMIT #{batch_size}; SQL break if result.affected_rows == 0 # 更新last_id,获取当前批次的最大ID last_id = execute("SELECT MAX(id) FROM companies WHERE id > #{last_id};").first["MAX(id)"] || last_id + batch_size end # 修改字段长度 change_column :companies, :company_number, :string, limit: 20 end end
关键注意事项
- 执行迁移前务必备份数据,避免截断操作导致的不可逆数据丢失;
- 根据使用的数据库调整SQL函数(比如字符长度判断、截断函数的差异);
- 分批更新时,可根据数据库性能调整
batch_size的大小,平衡执行效率和锁表时间。
内容的提问来源于stack exchange,提问作者Charles Dayman
相关产品推荐
相关产品推荐

