0 - Packet
クライアント-サーバー間のやり取りは下記の形式で行われます。
標準的なパケット
標準的なMySQL/MariaDBのパケットは4バイトのヘッダー+パケットbodyから成ります。
パケット長とは、パケットbodyの長さのことです。 パケット長は最大で3バイトです。実際のパケット長は3バイトからパケット長 = byte[0] + (byte[1]<<8) + (byte[2]<<16)で計算されます。パケット長の最大値(全て3バイト0xff)は16777215バイト、または 2^24 - 1、惜しくは16MB - 1バイトになります。
シーケンス番号は交換番号を意味します。クライエントがクエリを送信するたび、シーケンス番号はまず0にセットされます。そしてクライエントがパケットを分割する必要がある場合、シーケンス番号はインクリメントされます。 より複雑な状況として、クライアントとサーバーが複数パケットを交換するケース(例. 認証用ハンドシェイク)では、シーケンス番号(nr) = 受信したサーバーのパケットの最後のシーケンス番号(seq.nr) + 1にセットします。
例: COM_PING パケットを COM_PING 本体に1バイト (0x10)だけ送信する場合:
01 00 00 00 10
サーバーは OK_Packet を返します。その時のシーケンス番号は1です。
パケットの分割
前述の通り、パケット長は3バイトで、最大で2^24 - 1バイトもしくは16Mバイト - 1バイトです。 しかし、このプロトコルでは、より大きなデータの送受信が可能です。この場合、クライアントは同じデータに対して多数のパケットを送信します。その際、各パケットのシーケンス番号をインクリメントしていきます。
原則として、データを16Mバイトのチャンクで分割します。サーバーが長さ0xffffffのパケットを受信すると、次のパケットの読み取りを続けます。データがちょうど16MBになった時、空のパケットはシーケンスを強制終了する必要があります。
例 max_allowed_packet が40Mバイトに設定されており、40Mバイトのパケットbodyを送信する場合:
最初のパケット:
ff ff ff 00 ...
2番目のパケット:
ff ff ff 01 ...
3番目のパケット:
02 00 80 02 ...
クライアント側はmax_allowed_packet の設定値を認識している必要があります。サーバー側には、mac_allowed_packetの設定値に対応したサイズで、パケットbodyを保持するためのバッファがあります。仮にクライアント側がmax_allowed_packetの設定値よりも大きなデータを送った場合は、ソケットがクローズされます。
2^24 - 1バイトのデータは2パケットで送信する必要があることに注意してください。最初のパケットは長さプレフィックス0xffffffで、2番目のパケットは長さ0(0x000000バイト, seqno インクリメント)です。一般的に、データ長は2^24 - 1の倍数であり、空のパケットが付属します。
パケットの圧縮
接続に時間がかかる場合、パケットは圧縮されます。 圧縮は ハンドシェイク・レスポンス・パケット の後、クライアント側がCOMPRESSを設定し、サーバー側が圧縮機能を備えている場合に有効になります。
圧縮が有効になると、パケットはヘッダー+データで7バイトになります。圧縮アルゴリズムにはZLIBが利用されます。ZLIBは幅広く利用されており、多くの言語やランタイムでサポートされています。
圧縮bodyには多くの標準パケットが含まれるため、圧縮シーケンス番号はシーケンス番号とは別にインクリメントされます。
小さなパケットに対しては圧縮は有効ではありません。そのためクライアント側は非圧縮データによる送信を選択することが可能です。
非圧縮データを送信するためには、圧縮されたパケット長を実際の長さに設定し、圧縮されていないパケット長を0に設定します。(データは圧縮されていない必要があります。)
例: COMPRESSが有効な状態で COM_PING パケットの COM_PING 本体を送信する場合。 1バイトのデータのため、圧縮する理由はありません。
01 00 00 00 00 00 00 01 00 00 00 10
サーバー側はOK_Packetを返します。レスポンスには圧縮シーケンス番号1とシーケンス番号1 が付いています。
圧縮パケットの分割
サーバー側はデータを解凍し、送信側と同じパケット(圧縮されていないパケット)を受け取る必要があります。データサイズを分割する必要がある場合、圧縮パケットを分割することが推奨されます。