🍋

アプリケーションはMySQLとどのように接続するのか【MySQLプロトコル】

2023/07/07に公開

この記事について

アプリケーションがMySQLと接続する際、基本的には何らかのORMを使用することだと思います。
ORMではMySQLへの通信は隠蔽されており、実装の詳細を気にしなくてもよくしてくれています。この記事ではその隠蔽された中身の仕様について調べ、その実装を解析してみました。

MySQLプロトコル

MySQLは独自の通信プロトコルを持っており、そのまま「MySQLプロトコル」と名付けられています。
このプロトコルはアプリケーションレイヤーに位置しており、下のレイヤーとしてTCP/IPやUnixDomainSocketなどを使用できます。

このプロトコルは2つの段階に分かれています。
1つ目のフェーズは通信の開始を行う「ConnectionPhase」2つ目のフェーズはSQLの実行を行う「Command Phase」です。[1]

ConnectionPhase

ConnectionPhaseでは、通信の確立暗号鍵の交換認証を行います。
詳細な概要は下の図のようになります。[2]
img1

まず、通信開始にあたってサーバーとクライアントがハンドシェイクを行います。
ハンドシェイクが成功すると、暗号化用の鍵の交換を行い、盗聴に対する耐性をつけます。暗号化はSSLを使用しています。
その後、ユーザー・パスワード等の認証を行います。

認証が成功すると「CommandPhase」に移行します。

CommandPhase

CommandPhaseでは、DBに対する操作を行います。
SQLやパラメーターなどを送信し、実際にデータのCRUDの実行を要請します。

例えば、以下のようなデータを送信します。
図A

この例ではコマンド「16」に対応する「COM_STMT_PREPARE」を実行します。
また、実行するコマンドの種類に応じてレスポンスが返されます。

JavaScriptでのMySQLClient

JavaScriptでのMySQLへの接続にはライブラリを使用します。
以下のライブラリが最も有名だと思われます。typeormもこのライブラリに依存しているようです。

https://github.com/mysqljs/mysql/tree/master

このライブラリでは先に説明した「MySQLプロトコル」がそのまま実装されていました。
ライブラリは「net」や「tls」といったネットワーク系の基幹ライブラリに依存しており、コード中でそのままバイナリを操作していました。

(例)
switch (byte) {
  case 0x00: return Packets.OkPacket;
  case 0xfb: return Packets.LocalInfileRequestPacket;
  case 0xff: return Packets.ErrorPacket;
  default:   return Packets.ResultSetHeaderPacket;
}

まとめ

  • MySQLには独自のプロトコルがあり、その実装は「ConnectionPhase」と「CommandPhase」の2段階に分かれていた。
  • ライブラリはMySQLプロトコルを隠蔽しており、意識しなくても利用できるようになっている。

感想

ORM周りのエラーが発生した時にMySQLのエラーが発生することがあるのは、このようにMySQLのクライアントライブラリ・MySQLサーバーと何重かの隠蔽が挟まっているから何だろうなと感じました。
素人ながら、ORMを書こうとするとDBごとの仕様を把握しなくてはいけので大変そうだなぁと感じました。

補足

JavaScriptで有名なORMにはtypeormprismaが存在します。前者はJavaScriptで、後者はRustで書かれています。

脚注
  1. https://dev.mysql.com/doc/dev/mysql-server/latest/PAGE_PROTOCOL.html#protocol_overview ↩︎

  2. 引用元 https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase.html ↩︎

Discussion