hello-world
webエンジニアのメモ。とりあえずやってみる。

Mysql2::Error: Specified key was too long; max key length is 767 bytes limit

公開日時

railsでactive_adminのmigrationを行う際に以下のようなエラーが出ました

Mysql2::Error: Specified key was too long; max key length is 767 bytes limit

index対象のカラム量が767バイトを超えると出るエラーのようです。

MySQLのencodingがutf8の場合は3バイトのため、255 * 3 = 765バイト で767バイト以下に収まります。

しかし、utf8mb4の場合は4バイトのため、255 * 4 = 1,020 バイトとなり、767バイトを超えてしまうためエラーが発生します。

今回utf8mb4を使用していたため、上記エラーが発生しました。

対応方法としては、my.cnfに設定を書いてindexのkey lengthを拡張する、もしくはカラムのlimitを下げるというので対応できました。

方法1. indexのkey lengthを拡張する

/etc/my.cnf に以下を記述すればリミットを3072byteに拡張することができます。

[mysqld]
innodb_file_per_table
innodb_file_format=barracuda
innodb_large_prefix = 1

方法2. カラムのlimitを下げる

migrationファイル内のカラムに「limit: 191」を設定することで、191 * 4 = 764バイトとなり制限以内に抑えることができます。

t.string :email, limit: 191

もしくは config/initializers/mysqlpls.rb に初期化設定を書いて、一括で更新する方法もありました。

今回は開発環境だけ適用したかったので、以下のようにしました

require 'active_record/connection_adapters/abstract_mysql_adapter'

if Rails.env.development?
  module ActiveRecord
    module ConnectionAdapters
      class AbstractMysqlAdapter
        NATIVE_DATABASE_TYPES[:string] = { :name => "varchar", :limit => 191 }
      end
    end
  end
end

方法2の場合は、カラムの最大長が減ってしまうので、可能であれば方法1のようにmy.cnfに追記するやりかたで対応するのがよさそうです。

2014/11/3 追記 上記の設定を行った別環境で

bundle exec rake db:create

を実行しようとしたところ、再度

Mysql2::Error: Specified key was too long; max key length is 767 bytes limit

のエラーが出てしまいました。

調べてみるとmysql側のROW_FORMATがCOMPACT設定になっていたのが原因でした。

上記設定はROW_FORMATがDYNAMICかCOMPRESSEDのとき有効とのことなので、

ActiveRecordをutf8mb4で動かす」にあったactive_recordの設定を記述して、

# 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

再度 db:create を実行したらうまくDB作成できました。

参考


Related #MySQL

[mac]homebrewでmysqlをインストール

- インストール

RDSで日本語を使う場合の初期設定(utf8mb4)

RDSで日本語を使う場合、Parameter Groupsを作成し、

MySQLで時間毎のデータを集計する

ちょっとした分析レポートを作る機会があったのですが、「時間ごとに集計するSQLってどうやって書けばいいんだろう」と思って色々悩んでいました。