ごあいさつ

こんにちは、haruyan-hopemucciです。

本日はherokuにアプリケーションをデプロイする際のデータベース選定についてのナレッジです。

先に結論

herokuのアドオンにClearDBはもう使わない。JawsDBにしましょう。

本文

herokuとは

仮想コンテナ型のホスティングサービスで、PaaS(Platform as a Service)にあたります。

heroku内に用意されているgitリポジトリにpush、またはgithubから特定のリポジトリ、ブランチをpullすることでコンテナをビルドします。Ruby, Java, Python, PHPなどのプログラミング言語、またそれらを利用している数々のフレームワークのビルドに対応しています。

herokuの利点

herokuはポートフォリオのデプロイ先として大変人気があります。同じようなサービスにMicrosoft Azure や AWS などもございますが、 最低ラインで使い続ける限りは無料で、勝手に有料になることがないという部分がポートフォリオ用に選ばれやすい大きな理由だと思います。

他の、例えばAWSなどの競合サービスでは、仮想マシンの利用状況に応じて従量課金されます。毎月無料枠があるので慎ましく使えば実質無料ですが、無料枠を使い切ると通知も警告も何もなしに課金されていきます。herokuではリソースの無料枠を超過すると課金開始ではなく一旦サービスが停止されます。そこで有料プランへ移行するかどうか判断することになります。勝手に課金されないので一時的なアプリ置き場として、放置してもコストがかからずお財布に優しいのですね。

(完全余談)herokuという名称の由来

wikipediaによると、"Heroic"(英雄的)と"Haiku"(俳句)を掛け合わせた造語らしいです。読みも「ヘロク」ではなくHeroicに近い「ヒロク」と読むのが正式なんだそうな。「広く(普及させたい)」という思いとかけている、ということでHeroku開発者は大変な日本通みたいです。

また、Rubyの原作者であるまつもとひろゆきさんがHerokuの活動にコミットしているそうです。さすが城でコーディングする男は違いますね。(王宮とは言っていない)

heroku + MySQL が定番な理由

herokuがサポートしている標準データベースはPostgreSQLです。
にも拘わらずheroku標準のPostgreSQLはあまり使われず、もっぱらMySQLばかり使用される傾向があります。
MySQLも使用できますがサードパーティ製のアドオンという形で導入する必要があります。ひと手間以上かかるしherokuの外部に実態があるサービスなのでちょっと面倒です。

これは単なる私の想像ですが、XAMPPやMAMPを使ってPHP, Laravel で作ったポートフォリオを極力無料で外部公開したいという流れが出来上がっているからかのかなと思っています。
XAMPP、MAMPはWebサーバ、言語(PHP)、データベースをワンセットで導入、管理するにはとても楽ちんなエコシステムです。もちろんXAMPP、MAMPみたいなもの、またはそのものが導入されているレンタルサーバーも存在します。が当たり前ですが有料です。そこでWebサーバとアプリケーションは同様に構成されているherokuで、無理にでもMySQLを動かしたほうがデプロイしやすかろう、ということなんだろうかと考えます。

herokuで使うMySQLの定番:ClearDB

herokuアプリ上でアドオンとして使用できるMySQLはいろいろ存在しますが、その中でも無料で使えてお手軽ということで「ClearDB」というアドオンが現状最も使用されていると思います。
heroku + MySQLという構成でデプロイするブログ記事を検索するとほとんどがClearDBを使ったデプロイ方法を紹介しています。
ClearDBは無料プラン(Ignite)と有料プランがあり、無料プランはインスタンスが1つ、容量5MBとちょっと心もとないですが無理をしなければポートフォリオ用としては十分な選択肢です。

ClearDBの欠点

無料故の欠点、とは言えない運用上問題になる欠点がClearDBにはいくつか存在します。

  • バージョンが古い
    2022年2月現在、MySQLの現行バージョンは8.0です。対してClearDBはデフォルトのバージョンが5.6(ちょっと前まではデフォルトバージョンが5.5だった気がします)、アドオン作成時に明示すれば5.7が使えますが、8.0は使用できません。
    対してローカルで開発しているであろうMySQLは8.0であると思う(XAMPPはMariaDBですがMySQLの8.0相当と考えてよい)ので、8.0から導入された共通テーブル式等は使用できません。

  • Auto-Incrementが特殊な設定になっている
    MySQLにはテーブル作成時にカラムに対して自動採番機能(auto-increment)を設定できます。insert時に何も値をセットしなくても自動的にかぶらない番号を連番でセットしてくれます。主にIDの付与などに使います。またLaravelなどのフレームワークでもModelには明示しなくてもエンティティごとにIDを持っていて、自動採番機能を活用して番号を振っています。
    通常この自動採番設定は「初期値が1、増分が1」なのですが、ClearDBでは「初期値が4、増分が10」みたいにIDが1ずつ増えていかない仕様になっています。(初期値は4以外になることもあります)これが特にテストデータの投入、seedやFakerを使ったダミーデータ作成時に非常にネックになってしまいます。Model同士にリレーションがある場合、IDを合わせないといけないのですが自動採番の初期値、増分を考慮に入れないとID番号違いでリレーションの整合性が取れないのでデータがinsertできない、という事態になってしまいます。
    この仕様はユーザー側から変更できないし、今後も変更することはないとClearDBアドオン提供側から明言されているようです。

  • Laravelでmigrateがエラーになる
    これもClearDBの仕様、MySQLの仕様、Laravelの設定がうまくかみ合って起こる問題です。
    MySQLではvarchar型カラムの定義に入れる数値は「バイト」ではなく「文字数」です。255バイトじゃなくて255文字。
    そして文字は文字コードによって1文字につき何バイト使用するか(文字によっても)異なります。
    shift-jisなら1バイトか2バイト。utf-8であれば1バイトから最長4バイトまで使用します。
    ところでutf-8は当初1文字につき最長3バイトでした。MySQLのutf-8対応はちょうどこのころだったので、varcharの最大文字数255文字は255*3=765バイトまで扱えればいいよね、ということでvarcharと、そのvarcharを索引列としたインデックスの最長バイト数が767バイト(残りの2バイトはインデックスで使うための領域)で決め打ちされたようです。
    その後、utf-8は3バイトまででは文字が足りなくなり(主犯は絵文字です)4バイト目の領域を使う必要ができてしまいました。この4バイト目を使うutf-8の形式をMySQLでは"utf8mb4"という文字コードに分けました。utf8mb4は1文字最大4バイトで、255文字分で1020バイトです。
    しかしこのころのMySQLはインデックスの最長バイトが767バイトのままで、utf8mb4で定義したカラムを索引にしようとするとインデックスの最長767バイト制限を超えてしまうのでチェックが働きテーブルそのものがCreateできないようになっています。
    よって、utf8mb4で767バイトを超えない最長の文字列長は191文字(724バイト)ということになります。
    そして、Laravelのデータベース設定のデフォルト文字コードがutf8mb4になっています。database.phpを見てください。
    なので、例えばLaravelのアプリをherokuにデプロイする際に、

public function boot()
{
    Schema::defaultStringLength(191);
}

なんて記述を書け、というherokuデプロイ解説記事が沢山出回ることになります。大抵理由は書かれていません。
デフォルト文字コードをutf8にすれば従来通り1文字最大3バイトの扱いになるのでエラーは起こらないはずですが、最近のWebアプリは絵文字もテキストとして保存したいのでどうしてもutf8mb4にしたいのです。

この767バイト制限は、バージョン5.7から拡張されてutf8mb4でも収まるようになったそうです。しかしClearDBのデフォルトバージョンは5.6です…

ClearDBに変わるMySQLアドオン:JawsDB

上記の欠点を解消でき、さらに最小構成で無料で使えるアドオンがもう一つ存在します。それがJawsDBです。
herokuダッシュボード上のアドオン追加欄でmysqlで検索すると表示されます。

image

JawsDBは、

  • MySQL 8.0
  • 自動採番の初期値、増分はデフォルトと同じ1,1
  • もちろん191バイト制限もない

ということで、ClearDBに対応するためにしなければならないheroku用特殊設定をわざわざ実装しなくてもよくなります。
また、ClearDBと同様に容量5MBまでのプランが無料で、完全にClearDBをかぶせに来ています。
我々としてはこのビッグウェーブに乗るしかないです。ClearDBを使って変な設定や仕様で時間を消耗するより、JawsDBへ乗り換えたほうが手っ取り早くて良いと思います。Lavaral使いはお試しあれ。

読み方

Jawsはおそらく「ジョーズ」です。昔のサメ系パニック映画の名前ですね。アイコンもサメの背びれみたいな絵になっています。
なんでJawsかというと、このDBがaws上にホストされているからのようです。awsの先頭にJをつけてJawsなんですね。粋なネーミングセンスだと思います。

最後に

ローカルではMAMP/XAMPPを使ってphp/laravelアプリを開発し、herokuへデプロイするならClearDBより完全上位互換のJawsDBの方がお勧めです。
もしかしたら今後無料枠はなくなるかもしれませんが、使えるうちには使っておきましょう。