1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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配下に新規作成し__varchar__の__limit__を__191__にしましょう。

この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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?