mixi2のアーキテクチャとNewSQLを検討した理由
姜氏はMIXIのSREグループでサーバーサイドとインフラ開発を担当している。冒頭、mixi2の概要について「短文テキスト型のSNSで、2023年3月頃に4名の開発者で開発が始まり、2024年12月16日にリリースしました」と説明した。

mixi2では「フォロー」タイムラインのほか、日本におけるSNSの先駆けである初代 mixiにあったコミュニティ機能や、よりリアルタイムなコミュニケーション向けのイベント機能を提供。実装言語はサーバーサイドにGo、モバイルクライアントにFlutterを採用している。
システム構成は、モバイルクライアントとサーバー間の通信がgRPCで行われ、データベースにはTiDBクラスターを採用。キャッシュとタイムラインのデータを保持するためにRedisのクラスタ構成を使用し、Webブラウザ向けにはConnect RPC用サーバーを運用している。通知機能にはRedis Streamsを、検索機能にはOpenSearchを採用。また一部の非同期処理はAmazon SQSとAmazon ECSのワーカーで実装されている。

姜氏はSNSを開発するにあたって求めた要件として、「リクエスト、ユーザーの増加に対応できるスケーラビリティ」「障害発生時のフェイルオーバー」「サービスとして許容できるレイテンシー」「トランザクション」「メンテナンスオペレーションの容易さ」「コスト」を挙げた。MIXIでは従来、RDBのシャーディングを使った開発運用の経験があったが、今回はこれらの要件をより満たせるデータベースとしてNewSQLの導入を検討した。
姜氏は「NewSQLとは、SQLインターフェースを持ち、従来のリレーショナルデータベースが持つトランザクションと強い整合性を保証する分散データベースです」と説明。TiDB、Spanner、CockroachDB、YugabyteDBなどが該当する。
NewSQLとRDBシャーディングを比較すると、トランザクション機能はどちらもあるが、分散トランザクションはRDBシャーディングでは限定的。レイテンシーは分散している分、NewSQLの方が大きくなる傾向があるが、耐障害性はNewSQLの方が強い。開発コストや運用コストも含めると、RDBシャーディングの場合は実装側でシャーディングを考慮する必要があり、不利だと姜氏は指摘した。

「インフラ費用については、少ない台数で始められるのでRDBシャーディングの方が有利ですが、NewSQLの場合はストレージリソースと計算リソースを独立して調整できるので、場合によってはNewSQLの方が安くなることもあります」
NewSQL「TiDB」の特徴と選定理由
検討の結果、MIXIではTiDBを採用した。TiDBは役割ごとに「SQLレイヤーのTiDB」「キーバリューストアのTiKV」「クラスタのデータ配置などを管理するPD(プレースメントドライバー)」という3つのコンポーネントが分散配置されたアーキテクチャを持っている。
TiDBサーバーは最低2台、TiKVサーバーとPDサーバーは最低3台必要だ。各コンポーネントは水平方向に拡張することでパフォーマンスと耐障害性を高めることができる。
ストレージについて姜氏は「データを格納するのはTiKVコンポーネントです。データはリージョンという単位で自動的に複数のレプリカで管理され、同じデータが3つコピーされます」と説明。障害時に自動でフェイルオーバーし、容量が足りなくなった場合はノードを追加するだけで容量を増やせる利点がある。
「RDBで行っていたデータ肥大化に伴うシャーディング、マスター冗長化などが不要になります。また、複数台で構成されるので、メンテナンスも非常に容易なことが想像できました」
バックアップとリストア機能も重視された。TiDBのバックアップ機能は、スナップショットによるフルバックアップと差分バックアップが取得可能で、Point-in-Timeリカバリー機能も備えている。バックアップデータから解析用データベースの構築も可能なため、運用負荷を軽減できる。
また、開発期間中はクラスターの構築やスケールアウト、スケールイン、バックアップなどの操作がすべて「tiup」コマンドで実行でき、ドキュメントも豊富だった点が高く評価された。「パフォーマンスと可用性が主な決め手となって、TiDBを採用することにしました。開発期間中は、提供元のPingCAP社の皆様と隔週でミーティングをしながら進めました」と姜氏は振り返る。
タイムラインの実装と性能検証
mixi2ではタイムライン機能が重要な要素となっている。姜氏は、タイムラインについて「ユーザーの投稿したポストをまとめるユーザータイムラインと、フォローしたユーザーの投稿が流れてくるホームタイムラインがあります」と説明した。特にホームタイムラインを表示するには、投稿をできる限り早くフォロワーのタイムラインに届ける必要がある。そのため、事前にホームタイムラインを作成するアプローチを採用した。
投稿APIでは、投稿者の全てのフォロワーを見つけ、それぞれのタイムラインテーブルに新しい投稿をインサートする。この処理はmixi2のアーキテクチャでは非同期で行われる。Redisへの書き込み処理はパイプライン化され、最大1000件まで同時に処理できるように最適化されている。

ホームタイムラインのデータはRedisに保存され、全て時刻でソートされる。ポストのIDのみを保存し、それ以外のデータはTiDBに保存されている。ユーザーがホーム画面を開くと、これらの情報を組み合わせてタイムラインを表示する。運用上の工夫について姜氏は「ユーザーの多くは過去の投稿には関心を示さないことが多いので、一定の件数以上の過去の投稿は定期的に削除しています」と紹介した。
アプリケーションの設計実装がある程度終わった後、サービス全体の検証が行われた。姜氏は「今回TiDBを初めて使用するので、データベースのパフォーマンスの確認を重点的に行いました」と説明。mixi2というサービス名から、ある程度のユーザー流入が予想されたため、厳しめの目標設定で検証を行った。
APIのレイテンシー目標は100ms、書き込み性能は代表的なRPCで10,000RPS(1秒あたりのリクエスト数)を目標に設定。特に投稿がフォロワーのタイムラインに表示されるまでの時間を5〜10秒と設定し、チューニングを実施した。
負荷テストは専用環境でLocustを使用。並列度を大きくするため、Go製のworker実装「boomer」を採用し、Kubernetes上で動作させた。シナリオはユーザー作成から認証、フォロー、投稿、いいねなどの基本操作を網羅したものだった。
「負荷検証の結果、データベースの動きは非常に予想しやすく、書き込み性能は特に台数を増やした分だけ向上しました」と姜氏。一部の重い処理(検索インデックスの書き込みやFCM呼び出しなど)はAPIから非同期ワーカーに移動し、パフォーマンスを確保した。
高負荷時に問題となったのは、複数のフォロワーが同時にフォロー/いいね操作をした際のロック待ちタイムアウトだった。姜氏は「TiDBは分散トランザクションなので、トランザクションのオーバーヘッドが高く、RDBより不利に作用する可能性があります」と指摘。この問題に対しては、ロック競合が発生する処理をRedis側に移動させることで回避した。
負荷テスト中には、TiDBの各コンポーネントを意図的に停止させる障害テストも実施。「TiKVのインスタンスを停止させ、システムに設定した時間経過後にリージョンレプリカがリバランスすること」「クラスターのTiKV台数を減らしたときにリバランスすること」「PDの主担当変更」といった条件で検証した結果、クラスターのノードを落としてもサービスに影響なく処理を継続できることが確認された。
リリース後に発生した問題と対応
2024年12月16日のリリース時には想定を上回るリクエストが来たが、各コンポーネントをスケールアウトすることで無事に稼働を続けることができた。お正月などの一時的な負荷が見込まれる場合も、コンポーネントをスケールアウトすることで問題なく運用を継続している。姜氏は「リリースして期間は短いですが、今のところサービスを無停止で運用できています。一度だけTiDBインスタンスのノード障害が発生しましたが、そのときもサービス影響なく対応することができました」と報告した。

リリース後に発生したSlow Queryについても言及。Secondary Indexを使ったクエリで、Indexの先頭カラムで対象を絞り、2番目のカラムでソートする場面で問題が発生した。「IN句の要素が一つの場合はソートが効いたのですが、複数要素が指定されるとソートが効かなくなりました。対象が数十万件になることもあるため、Slow Queryになってしまいました」と姜氏。この問題は調査中だが、現状ではアプリケーション側でループ処理を行うことで回避している。
また、TiDBのMVCC(多版型同時実行制御)機能に関連した別のSlow Queryも発生。短期間で更新を繰り返すことで想定より多くのバージョンが生成され、検索処理が重くなる問題だ。「total_keysが過去のバージョンを含むキーの合計数で、total_process_keysが実際に処理されたキー数。この割合が大きいと読み込み処理のコストが高くなります」と説明した。
リリース後のTiDBクラスター運用は、tiupコマンドを使って実施されている。スケールアウト/スケールイン、バックアップ、データダンプなどの操作が問題なく行えているという。データベースのダンプ取得は、tiup dumplingコマンドを使用してAmazon S3にエクスポートし、BigQueryに登録。この一連の操作はAmazon ECSのスケジュールドタスクとして自動化されている。
バックアップも同様に、tiup brコマンドでフルバックアップとログバックアップを行い、Amazon ECSのタスクとして定期実行している。なお、日々の運用監視はTiDB DashboardとGrafanaで実施。Slow Queryの調査にはTiDB Dashboardを活用し、基本的なメトリクス監視に加え、リージョンの状態やキーのスキップカウントなどの監視はGrafanaで行われている。
姜氏は「少ない人数の開発チームですが、TiDB Clusterを導入し、今もサービス開発と運用を行っています」と振り返った。TiDBは無停止で運用を継続でき、急激な負荷にもスケールアウトで対応することができた点を評価。監視ツールが一通り付属することも、少人数チームにとって大きなメリットだったという。
最後に今後の展望として、「TiCDCを使ってチェンジイベントをメッセージとして送信し、非同期処理に使いたいと考えています。現在のシステムではSQSのパブリッシュに失敗した場合のトランザクション実装が一部複雑になっているため、ここを単純化できると考えています」とコメントした。
mixi2のケースは、NewSQLを採用してSNSを構築・運用した貴重な事例だ。特に小規模チームでも高いスケーラビリティと可用性を実現できることを示した点は、同様の課題を抱える多くの開発者にとって参考になるだろう。