在默认情况,Rails的migration会为每个语句单独生成一条SQL来修改表结构。这对新建的表来说是无所谓的,一个create table 或 alter table都是瞬间完成。但是,对于已经存在大量数据的表来说,这样效率极低。刚开始没有注意这样的问题,order表的写法:

rename_column :orders, "shipping_first_name", "origin_shipping_first_name"
rename_column :orders, "shipping_last_name", "origin_shipping_last_name"
remove_column :orders, "coupon_value"
...

结果就是,每一个rename_column和remove_column执行时间大约4分钟,一共20多个这样的语句,migrate总计执行了近两个小时。因为alter table时mysql需要把整个order表copy to tmp table,而order表是非常庞大的。

解决办法是把上面的所有修改order的migration合并成一个,并且设置bulk 选项。

class RenameOldOrderAddressRelatedColumn < ActiveRecord::Migration
  def up
    change_table(:orders, :bulk => true) do |t|
      t.remove :coupon_code
      t.remove :coupon_value
      t.remove :order_promotion_id
      t.rename :order_status_code_id, :state
      t.remove :in_process
      [:first_name,
       :last_name,
       :phone,
       :street_address,
       :city,
       :zip,
       :company,
       :street_address_2,
       :country,
       :state].each do |column|
        t.rename "shipping_#{column}", "origin_shipping_#{column}"
        t.rename "billing_#{column}", "origin_billing_#{column}"
      end
    end
  end
end

这样,上面的migration只生成一条SQL语句:

ALTER TABLE `orders` DROP `coupon_code`, DROP `coupon_value`, DROP `order_promotion_id`, CHANGE `shipping_first_name` `origin_shipping_first_name` varchar(50) DEFAULT NULL, CHANGE `billing_first_name` `origin_billing_first_name` varchar(50) DEFAULT NULL, CHANGE `shipping_last_name` `origin_shipping_last_name` varchar(50) DEFAULT NULL, CHANGE `billing_last_name` `origin_billing_last_name` varchar(50) DEFAULT NULL, CHANGE `shipping_phone` `origin_shipping_phone` varchar(20) DEFAULT NULL, CHANGE `billing_phone` `origin_billing_phone` varchar(20) DEFAULT NULL, CHANGE `shipping_street_address` `origin_shipping_street_address` varchar(255) DEFAULT NULL, CHANGE `billing_street_address` `origin_billing_street_address` varchar(255) DEFAULT NULL, CHANGE `shipping_city` `origin_shipping_city` varchar(255) DEFAULT NULL, CHANGE `billing_city` `origin_billing_city` varchar(255) DEFAULT NULL, CHANGE `shipping_zip` `origin_shipping_zip` varchar(20) DEFAULT NULL, CHANGE `billing_zip` `origin_billing_zip` varchar(20) DEFAULT NULL, CHANGE `shipping_company` `origin_shipping_company` varchar(255) DEFAULT NULL, CHANGE `billing_company` `origin_billing_company` varchar(255) DEFAULT NULL, CHANGE `shipping_street_address_2` `origin_shipping_street_address_2` varchar(255) DEFAULT NULL, CHANGE `billing_street_address_2` `origin_billing_street_address_2` varchar(255) DEFAULT NULL, CHANGE `shipping_country` `origin_shipping_country` varchar(255) DEFAULT NULL, CHANGE `billing_country` `origin_billing_country` varchar(255) DEFAULT NULL, CHANGE `shipping_state` `origin_shipping_state` varchar(255) DEFAULT NULL, CHANGE `billing_state` `origin_billing_state` varchar(255) DEFAULT NULL

通过上面修改,整个migration执行时间降到了十分钟以内。