LoginSignup
77
69

More than 3 years have passed since last update.

【MySQL】Mysql2::Error: Specified key was too long; max key length is 767 bytes

Posted at

はじめに

Railsアプリケーション作成中、MySQLで以下エラーが発生したため解決した方法を残します。

エラー内容

rails db:migrateを実行したときに発生。

Mysql2::Error: Specified key was too long; max key length is 767 bytes: <キー名>

key長の制限が最大767バイトになっているのに、それを超えてしまったことで発生したようです。

解決法は大きく2つ

  1. 767バイトを超えないように何かを削る
  2. 767バイトを超えてもいいようにする

今回は1.で対応してみます。

環境

OS: macOS Catalina 10.15.3
Ruby: 2.6.5
Rails: 6.0.2.1
MySQL: 5.7

結論:解決法

以下のようなmysql.rbconfig/initializer配下に新規作成します。

config/initializer/mysql.rb
require 'active_record/connection_adapters/abstract_mysql_adapter'

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter
      NATIVE_DATABASE_TYPES[:string] = { :name => "varchar", :limit => 191 }
    end
  end
end

なぜ解決するのか?

解決はしたのですが、理由を理解しておきたかったので深堀ります。

前提:文字コードutf8utf8mb4の違い

こちらの記事で詳しく解説されていました。

RailsやCakePHPのデフォルトで生成される255文字のVARCHARでは、3バイトのutf8では255×3=765でほぼぴったり収まります。一方、utf8mb4ではオーバーしてしまうので、エラーとなりました。

今回自分の環境でもutf8mb4を使用していました。

utf8mb4では1文字4バイト、
そのため、255 × 4 = 1020バイトとなり、冒頭の767バイトを超えてエラーが発生したということですね。

その他、utf8utf8mb4の違いについては以下記事もわかりやすく解説をされていました。

MySQL で utf8 と utf8mb4 の混在で起きること - @tmtms のメモ

では、これを踏まえて今回のmysql.rbでなぜ解決したのかを確認します。

今回作成したファイルで変わる設定

今回のmysql.rbActiveRecord内の
NATIVE_DATABASE_TYPES
という設定を上書きするファイルになっています。

該当のソースはこちら

ソースから当記事に関連する部分だけ抜き出すと、

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter < AbstractAdapter

      #...略

      NATIVE_DATABASE_TYPES = {
        primary_key: "bigint auto_increment PRIMARY KEY",
        string:      { name: "varchar", limit: 255 }, #ここ
        text:        { name: "text" },
        integer:     { name: "int", limit: 4 },
        float:       { name: "float", limit: 24 },
        decimal:     { name: "decimal" },
        datetime:    { name: "datetime" },
        timestamp:   { name: "timestamp" },
        time:        { name: "time" },
        date:        { name: "date" },
        binary:      { name: "blob" },
        blob:        { name: "blob" },
        boolean:     { name: "tinyint", limit: 1 },
        json:        { name: "json" },
      }

      #...略
      #その他、change_columnメソッドのソース(どんなSQLを発行するかの定義)などが記載
    end
  end
end

先程の、mysql.rbではここの一行を上書きしていることになります。

string:      { name: "varchar", limit: 255 }

つまり、Rails

$ rails g model User name:string

などでString型を設定したらMySQLでは自動的にVARCHAR(255)で設定されるということです。

これをmysql.rbを追加することで、VARCHAR(191)に変更が出来ます。

つまり、767バイト ÷ 4バイト = 191.75文字を超えないので、エラー解決となります。

では、設定変更が出来ているかを確認します。

確認

rails consoleにて確認します。

Before(mysql.rb作成前)
[1] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
 :string=>{:name=>"varchar", :limit=>255}, #limitが255になっている
#...以下略

After(mysql.rb作成後)
[2] pry(main)> ActiveRecord::Base.connection.native_database_types
=> {:primary_key=>"bigint auto_increment PRIMARY KEY",
 :string=>{:name=>"varchar", :limit=>191}, #ここがmysql.rbで設定した内容に変わる
#...以下略

mysql.rbによって設定が変更されていることが確認出来ました!

以上です!

おわりに

最後まで読んで頂きありがとうございました:bow_tone1:

どなたかの参考になれば幸いです:relaxed:

参考にさせて頂いたサイト(いつもありがとうございます)

77
69
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
77
69