技術を愛する者たちが飲み会で交わす会話には、不思議な魅力が宿ります。お酒の力も相まって、「技術の真髄」や「泥臭い本音」がポロリとこぼれ落ちるからです。そんな濃密な時間は、何物にも代えがたい有益な学びの場となります。
連載企画【言語を肴に一献】では、特定の技術領域を極めた者同士が、お酒を飲みながらさまざまな観点で議論します。今回は、MySQL運用のスペシャリストである田中 翼(通称:yoku)さんと、PostgreSQLのコミッターである藤井 雅雄さんが対談。ユーモラスかつ深淵な会話に、耳を傾けてみましょう。
【一品目】対照的な二人。「広く浅く」の生存戦略と、「狭く深く」の探究
──今回はお集まりいただきありがとうございます。RDB界隈の著名人であるお二人が、どんな会話をされるのか楽しみです。それでは、まずは乾杯から始めましょうか!
yoku&藤井: かんぱーーーーい!!!!!

──この連載企画では、特定の「お題」に沿ってお二人に語り合っていただきます。まず、データベースに関する「お二人のキャリア」から話しましょう。yokuさんと言えば「MySQLの広範な知識を持つ方」という印象が強いです。なぜ、それほどまでにMySQLのあらゆる情報を網羅しているのでしょうか?
yoku: 確かに私の場合は幅広いんですけれど、一つひとつの領域を個別に見てみると、実は知識が「浅い」んですよね。MySQLストレージエンジンの中身をゴリゴリに知っているほどではない。かといって、その上で動くアプリケーションをバリバリ書いているわけでもない。要するに、上から下まで、どこか特定の場所が突き抜けて深いわけじゃないんです。
だからこそ、生存戦略としてどのレイヤーのことも少しずつ知っておこうとした結果が、今のスタイルですね。自分の向き不向きもあると思います。もともと器用貧乏なキャラというか(笑)。「五教科で満点は一つもないけれど、全科目の平均点は高い」みたいなポジションを目指して、全領域をカバーしていきたいんです。
藤井: なるほど。羨ましいですね。自分の場合は、詳しくない領域は本当に全くわからないですから。自分がコードを書いたことのあるレプリケーションやトランザクションログのことはわかっても、それ以外はあまり。基本的には狭い範囲を、どこまでも深く掘り進めていくスタイルなので、yokuさんとは真逆かもしれません。
yoku: でも、レプリケーションやトランザクションなんて、データベースの花形じゃないですか。藤井さんは、どうしてそれらの領域に詳しくなったんですか?
藤井: 私は新卒で、現在の職場でもあるNTTデータに入ったんですが、最初に配属されたのがデータベースの仕事で。しかも、「PostgreSQLを改造して並列分散DBをつくろう」というプロジェクトだったんですよ。
そこで最初に渡された仕事が、「PostgreSQLのコードを読んで、トランザクションログからリカバリする処理を分散化しろ」というものでした。
yoku: すごい(笑)。絶対に新卒がやる仕事じゃないですよ!
藤井: そこがスタートだったんですよね。そもそもSQLすらまともに読み書きしたことがないのに、ひたすらPostgreSQLのソースコードを読み込むところから始まりました(笑)。だから、新卒からの数年間は、SQLの文法もきちんと覚えていないのに、PostgreSQLのリカバリ処理についてはやたらと詳しいような、歪なエンジニアでした。
yoku: それは、だいぶ特殊なキャリアですね。面白いなあ。
【二品目】データが消える、インデックスが効かない。本当にあったデータ型の怖い話
──次は、データベースの「データ型」について扱ってみましょうか。
藤井: PostgreSQLはデータ型が本当に多種多様ですね。標準で使える型以外にも、PostGISを導入すれば地理空間情報なども扱えます。何より「ユーザー自身で自由にデータ型を定義できる」というのは、PostgreSQLの大きな特徴かもしれません。

藤井雅雄さん
──PostgreSQLはなぜ、そこまでデータ型を豊富に持とうとするのでしょうか?
藤井: 一つは、バリデーションをアプリケーション側で個別に実装するのではなく、データベース側の型定義で完結させたいという狙いがあります。例えば、JSONデータをテキストとして保存してしまうと、アプリ側でその整合性をチェックしなければなりません。でも、最初からJSON型として持っておけば、PostgreSQL側で不適切な形式を弾いてくれます。
もう一つは格納効率です。なんでもテキストで持つより、データの特性に合わせたバイナリ形式で持つ方がサイズを抑えられます。それが結果として、パフォーマンスの向上にも直結するんです。
yoku: 一方のMySQLは、そもそも数値の「1」と文字列の「1」を比較できてしまうくらい、型に関しては「ゆるい」です。
例を挙げると、C言語の実装に近い挙動なんですが、「1Q84」という文字列を数値と比較しようとすると、暗黙的なキャストが行われます。前から順に数値変換していって、数値ではない文字が出た時点で変換をやめる。つまり、MySQLはこれを数字の「1」として扱ってしまうんです。
──かなり独特な挙動ですね。
yoku: これが実務で事故を招くこともあります。以前、(データベース界隈で有名な)そーだいさんが経験された話なんですけれど、16進数の文字列が入ったカラムを、うっかりINT型と比較するWHERE句を書いて、SQLを実行してしまったらしいんですよ。
すると、先頭が「1から9」で始まる文字列はそのまま数値としてキャストされますが、先頭が「0」や「AからF」のものは全て数値の「0」としてキャストされてしまう。結果として、意図せず「0」扱いになったデータがごっそり消えてしまった……という恐ろしい話があります。
──怖いなんてもんじゃないですね……。
yoku: 他の例として、暗黙的なキャストのせいでインデックスが効かなくなるケースもあります。
例えば、正の整数しか入らないはずのIDカラムに対して、WHERE ID = -1というクエリを投げたとします。この「-1」は符号付き整数(signed)なので、型が違う。すると、どちらかの型に合わせようとキャストを行うのですが、そうなると元の型のルールでつくられていたインデックスが全く使えなくなってしまうんです。
似たような話で、テーブル結合の際、片方のIDカラムは「bigint」だけれど、もう一方は「integer」のままだった、という場合も同じことが起こります。この状態でジョインすると、インデックスが効かずにフルスキャンになってしまう。
藤井: 厄介なのは、データが少ない環境で検証していると、フルスキャンでもそこそこの性能が出てしまって気づけないことですよね。本番環境でデータ量が増えてきたタイミングで、ようやく「なぜか重い!」と気づくことになるという。
【三品目】インデックス崩壊・寿司ビール問題。「並び順」はかくも難しい
──データの並び順や比較のルール、いわゆる「照合順序(Collation)」に関連するお話も伺いたいです。
藤井: PostgreSQLはデフォルトで、OSのlibc系ライブラリ(Linuxだと多くの場合glibc)を利用して、ソート順や文字の照合(ロケール)を処理しています。すると何が起きるかというと、OSのアップデートなどでlibcのバージョンが変わったとき、ソート順が変わってしまう可能性があるんです。
yoku: ええっ、怖い。
藤井: 単にORDER BYの結果が変わるだけならまだしも、深刻なのはインデックスです。「アップデート前のソート順」を前提につくられたインデックスは、アップデート後の照合順序と整合しなくなって、事実上インデックスが壊れたような状態になってしまうんですよ。
他にも、レンジパーティションを切っている場合などは、文字列の比較結果が変わることで、パーティション境界の判定に影響が出る可能性もあります。だから「libc系ロケールを使っているときは、OSのアップデートに細心の注意を払う」というのがPostgreSQL界隈では昔からよく言われてきました。
yoku: ちなみに、その問題の回避策はあるんですか?
藤井: 昔は主にOSのlibc系ロケールを使うしかなかったんですが、この問題を受けてICUというライブラリのロケールも使えるようになりました。これならOSのロケール更新とはある程度切り離して管理できます。さらに、2024年にリリースされたバージョン17からは、glibcやICUのような外部ライブラリに依存しないbuilt-inロケールプロバイダーも新しく導入されました。MySQLはどうですか?

田中翼(yoku)さん
yoku: MySQLは、基本的にOSのレイヤーを信用していません(笑)。ソートの仕組みに関しても、すべて自分たちの中にビルトインで持っていますね。
藤井: それは素晴らしいですね。
yoku: いや、それも実は良し悪しなんですよ。自分たちで独自のソート順をつくってしまった結果、世界で唯一の、MySQL独自の並び順が誕生してしまった。そのせいで、違う文字なのに同じだと判定されてしまう、有名ないわゆる「寿司ビール問題」が起きるようになってしまいました。
これはつまり、意図せぬところで文字列比較に不整合が生じるケースがあるということです。OSや外部ライブラリなど、他の誰かが保証してくれないソートの仕組みを自分たちで抱えているというのは、それはそれで危ういですよ。
【「寿司ビール問題」とは】
MySQLにおいて、絵文字の🍣(寿司)と🍺(ビール)が同じ文字として扱われてしまう文字コードのバグ・仕様問題です。データベースの照合順序(Collation)の設定が原因で発生します。検索文字列に絵文字を使用した際に、全く別の絵文字同士が「同じ」と判定されてしまうなど、データ処理上の不具合を引き起こします。
MySQL Bugs: #76553: Sushi-Beer issue of MySQL with utf8mb4
【締め】どれほど学んでも底が見えない、データベースの世界
──最後は「私たちはなぜこれほどリレーショナルデータベースに惹かれるのか」というお話で締められればと思います。
yoku: その前に確認なんですけれど……。藤井さん、そもそもリレーショナルデータベースって好きですか?
藤井: えっ!? まあ、まあ……好き……だと思いますね。好きな気がします。
yoku: 実は私、リレーショナルデータベースそのものが好きというよりは、「MySQLが好き」というだけなんです。ただ、リレーショナルデータベースという文脈で一番面白いのは、その「構造的な矛盾」だと思っています。
リレーショナルデータベースには「One Fact in One Place(一つの事実は一つの場に)」という原則がありますよね。正規化して、データを重複させるなということ。集合論としてはそれが当たり前なんです。
でも、インデックスとはすなわち、データをソートしてつくったサブセット(コピー)ですよね。つまり、インデックスをつくった時点で、データは二箇所に書かれているんです。
純粋な数学の世界なら、実行時間なんて考えずに美しさだけを追求すればいい。でも、現実世界では「一定の時間内にレスポンスを返さなければならない」という絶対的な制約があります。その制約のために、わざわざデータを二箇所に書く。最初から自己矛盾を抱えているんですよ。理想とする「数学的な美しさ」と「泥臭い現実」が、インデックスという基本的な仕組みの時点で乖離している。
そんな矛盾を抱えながら頑張っているこいつ(リレーショナルデータベース)は、なんて可愛いやつなんだと思うんですよ。「お前、自己矛盾しちゃって……バカだなあ」っていう。どこか子どもを愛でるような感覚に近いですね(笑)。
藤井: 面白い視点ですね!
yoku: 藤井さんはどうですか?
藤井: 自分の場合、もともと「複雑な仕組みを解き明かして、課題を解決する」とか「自分でもその仕組みをつくる」というプロセス自体に喜びを感じるタイプです。
だから、最初に触れたのが仮にカーネルや他のソフトウェアでも、同じようにのめり込んでいた可能性はあります。ただ、最初にPostgreSQLに触れて、その開発コミュニティが非常にオープンで居心地が良かった。それが大きかったですね。
それに、PostgreSQLはとにかく使われている現場が多いです。自分が手を動かしてつくったものを活かす場がたくさんありますし、現場からまた新しいネタ(改善案)を拾ってくることもできる。自分の志向と、PostgreSQLが持つ広がりが、ちょうどうまく合致したんだと思います。あとは、やはり底が見えない。コミッターとして深く開発に関わっていても、いまだに「え、ここってこんな実装になってたの!?」という驚きがあります(笑)。
yoku: トッププレイヤーであっても、いまだに良い意味での驚きがある。それは希望のある話ですね。
藤井: そうなんです。ずっと触っていても、まだまだ目の前に未開の地が広がっている。それが、飽きずに続けていられる一番の理由かもしれません。
yoku: 今日はお話しできて本当に楽しかったです。ありがとうございました!
藤井: ありがとうございました!

撮影:山辺恵美子

