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の主担当変更」といった条件で検証した結果、クラスターのノードを落としてもサービスに影響なく処理を継続できることが確認された。