Rails(ActiveRecord)とMySQLでutf8mb4を扱う設定

  • 25
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

ActiveRecordからutf8mb4を扱えない主な理由

ActiveRecordのstring型カラムがvarchar(255)で定義されるので、utf8mb4ではインデックスのキープレフィックスが767byteを超えてしまう。

ERROR 1071 (42000): Specified key was too long; max key length is 767 bytes

この問題をMySQLの設定とRailsへのパッチで解決する。

MySQLの設定

文字コードをutf8mb4で運用するために、インデックスのキープレフィックスを拡張する。

  1. innodb_large_prefixenableにする
    • 1を有効にするために、innodb_file_formatBarracudaにする
    • 1を有効にするために、innodb_file_per_tableenableにする
  2. innodb_default_row_formatDYNAMICにする # >= 5.7.9

my.cnfのオプション

my.cnf
innodb_file_per_table = 1
innodb_file_format = Barracuda
innodb_large_prefix = 1
innodb_default_row_format = DYNAMIC # >= 5.7.9

MySQL5.6

いくつかのパラメータを変更する。

  • innodb_file_per_tableのデフォルトはenable (>= 5.6.6)
  • innodb_file_formatのデフォルトはAntelopeなので、Barracudaへの変更が必要
  • innodb_large_prefixのデフォルトはdisableなので、enableに変更する
  • innodb_default_row_formatバージョン5.6系にこのオプションは無い
    • デフォルトがCOMPACTであり、かつコンフィグから指定できない
    • テーブル作成時のオプションで指定してやる必要がある

MySQL5.7

5.7.9以上であるなら、デフォルト設定でよい。

  • innodb_file_per_tableのデフォルトはenable
  • innodb_file_formatのデフォルトはBarracuda (>= 5.7.7)
  • innodb_large_prefixのデフォルトはenable (>= 5.7.7)
  • innodb_default_row_formatのデフォルトはDYNAMIC (>= 5.7.9)

Railsの設定

MySQL5.7.9以上を利用しているならdatabase.ymlにおいてutf8mb4を設定すればよく、とくべつなことをする必要はない。

database.ymlの設定

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_general_ci

MySQL5.7未満では、テーブル作成時にROW_FORMAT=DYNAMICを渡してやらなければならない。
もしくはカラムオプションかインデックスオプションにlimit: 191を付けてやる。

add_index :table1, :column1, length: 191

Rails4

kamipo神のモンキーパッチを使って、テーブル作成時のオプションにROW_FORMAT=DYNAMICを追加してやる。
テーブルのCREATEオプションとしてENGINE=InnoDB ROW_FORMAT=DYNAMICを追加してくれる。

config/initializers/ar_innodb_row_format.rb
ActiveSupport.on_load :active_record do
  module ActiveRecord::ConnectionAdapters
    class AbstractMysqlAdapter
      def create_table_with_innodb_row_format(table_name, options = {})
        table_options = options.merge(:options => 'ENGINE=InnoDB ROW_FORMAT=DYNAMIC')
        create_table_without_innodb_row_format(table_name, table_options) do |td|
          yield td if block_given?
        end
      end
      alias_method_chain :create_table, :innodb_row_format
    end
  end
end

参考: http://qiita.com/kamipo/items/101aaf8159cf1470d823

しかし、db/schema.rbにダンプされるテーブルオプションにはENGINE=InnoDB ROW_FORMAT=DYNAMICがついていない。
これもまたkamipo神によるactiverecord-mysql-awesomeをGemfileに追加しておくと、オプションを正しくダンプしてくれる。

RailsからMySQLのutf8mb4を扱えるようになるまでの道のりは、kamipoさんのおかげで切り開かれたと言っても過言ではないのでとっても感謝です。

余談でRails5

beta3ではデフォの状態だとRails4と同様にまだコケる。
kamipoさんのPRがたくさんマージされているので、そのうち。