LoginSignup
1
0

More than 3 years have passed since last update.

【Mysqlエラー】Mysql2::Error: Specified key was too long; max key length is 767 bytes

Last updated at Posted at 2021-01-23

はじめに

アプリ開発中に起こったMySQLのエラーについて忘備録も兼ねて記録しておこうと思います。

エラー文

rails g devise userでuserモデルを作成し、rails db:migrateを行うと下記のエラー文が表示。


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

直訳すると「指定したキーは長すぎます。キーの最大範囲は767バイトまでです」

意味は「Mysql2に格納できる最大文字データ量(767バイト)に対してオーバーしてしまったためエラーになった」ということのようです。

エラー解決に向けての予備知識

utf8

文字コードの中で世界で最も普及している文字コード。
通常は1~4バイトで文字を表現するが、MySQLでは3バイトの文字までしか扱えない。
1文字当たり3バイト。

utf8mb4

データベースMySQLで扱うための文字コード。
4バイトの文字を扱えるため、絵文字を扱いたい場合はこちらを使用する
→絵文字は4バイトなので、3バイトまでしか扱えないutf8では不可能なため。
1文字当たり4バイト。

VARCHAR型

・可変長(長さを変えられる)文字列のこと

・varchar(m)という形で指定する。(mはバイト数。0~65535まで。)

例) varchar(255) varchar(191)

・char型と異なり、末尾に空白は付かない。
・末尾に空白が付いた文字列はそのまま格納される。
↑の場合、取得時も空白が付いたままだが、WHERE句での比較時には削除された状態で比較が行われる。

エラー考察

前提条件として

MySQLのインデックスは最大767バイト

Railsのstringにおけるデフォルト値が255文字(MySQLで保存できるデータ量)。

  • 3バイトのutf8では

 3バイト × 255文字 = 765バイト < 767バイト

  • 4バイトのutf8mb4では

 4バイト × 255文字 = 1020バイト > 767バイト

つまり、上記のように文字コードutf8mb4では、
4 × 255 = 1020バイトとなって、MySQLのインデックスの最大数である767バイトを上回ってしまいエラーが発生してしまったということです。

解決方法

解決方法は大きく2つあるようで、

①767バイトを超えないように最大文字数を削る
②767バイトを超えてもOKにする

今回は簡単そうな①でエラー解決していきます。



結論 : 文字制限をかけるファイルを新規作成する

現在の入力できる最大文字数、255文字『varchar(255)』191文字『varchar(191)』変更できるファイルを新規作成し、767バイトを超えないようにします。



191文字『varchar(191)』の理由は

MySQLのインデックスは最大767バイト
utf8mb4は1文字当たり4バイト必要なので

767バイト ÷ 4バイト = 191.75文字 

実際のコード

mysql.rbconfig/initializer配下に新規作成しvarcharlimit191にしましょう。

このmysql.rbというファイルによってActiveRecord内のNATIVE_DATABASE_TYPESという設定を上書きすることができます。

ソースはこちら

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

mysql.rb作成前

rails consoleにて確認することができます。

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

mysql.rb作成後

:string=>{:name=>"varchar", :limit=>191}と最大文字数が191になっているのがわかりますね。

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

これで文字のデータ量を767バイト以内に納めることができました。

この後rails db:migrateを入力すると上手く作動しエラーが解消されました

ちなみに

今回のように、最大文字数を255文字から191文字にする方法ではなく、
最初にconfig/database.ymlに記載した文字コードを途中から変更(今回であればutf8mb4からutf8に変更してマイグレーション)する方法を取ると、もっと面倒臭いエラーになるようです。

rails db:migrateをすると消えない謎のファイルが現れ、モデルの作り直しになってしまうとか。

database.ymlの記載された情報は、迂闊に手を出せないので慎重に取り扱う必要がありますね。

参考記事

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

Qiita記事 utf8mb4で巻き起こったトラブル

Qiita記事 【SQL】char型とvarchar型の違いについてまとめてみた

MySQL 5.5 / Redmine 3.4.3 / Ruby23-x64 --- Mysql2::Error: Specified key was too long; max key length is 767 bytes

MySQLでutf8とutfmb4の混在で起きること

1
0
0

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
1
0