単一インスタンスのMySQLで直面する壁
ある夜、フラッシュセールの最中にメインデータベースのCPU使用率が95%に達したことを覚えています。秒間3,000クエリを処理していましたが、かつては20ミリ秒と軽快だったレスポンスタイムが、4秒近くまで膨れ上がっていました。私たちは標準的な手順に従いました。インスタンスサイズをアップグレードし、バッファプールを調整し、リードレプリカを追加しました。しかし、垂直スケーリングは行き止まりです。最終的には、どれほど資金を投じても解決できないハードウェアの天井に突き当たります。
シャディング(大規模なデータセットを管理可能な小さな断片に分割するプロセス)は、通常、データベースエンジニアリングにおける「最終ボス」です。MySQLやPostgreSQLでの手作業によるシャディングに苦労してきた私にとって、それはしばしばアプリケーション層のロジックが絡み合った脆弱な混乱状態でした. Vitessはそれを変えます。これはMySQLの上層に位置するクラスタリングレイヤーとして機能し、シャディングの複雑さを吸収します。そのため、アプリケーションからは100個のノードを単一のデータベースのように扱うことができます。
なぜVitessなのか?
YouTubeは爆発的な成長に対応するためにVitessを開発しました。現在、Vitessは何十億行ものデータを含むデータベースを管理しています。VitessはMySQLプロトコルを流暢に話すプロキシレイヤーを提供します。コード側で特定のレコードがどのシャードにあるかを知る必要はありません。Vitessはコネクションプーリング、クエリルーティング、そしてアプリケーションをオフラインにすることなくシャード間でデータを移動させる「リシャアリング」を処理します。これは手作業と自動組み立てラインほどの違いがあります。
環境のセットアップ
今日のVitessデプロイの標準は、Kubernetes用のVitess Operatorです。このウォークスルーでは、K8sクラスター(ローカルテストならMinikubeで十分です)とhelmが必要です。開始前に、環境に少なくとも4 vCPUと8GB of RAMがあることを確認してください。Vitessは効率的ですが、初期のオーケストレーションにはある程度のオーバーヘッドが必要です。
まず、リポジトリを追加してオペレーターをデプロイします分析:
# VitessのHelmリポジトリを追加
helm repo add vitess https://vitess.io/helm
helm repo update
# Vitess Operatorをインストール
helm install vitess-operator vitess/vitess-operator
オペレーターはクラスターの「脳」として機能します。カスタムリソース定義(CRD)を監視し、MySQLインスタンスとVitessコンポーネントが健全で同期された状態に保たれるようにします。
最初のキースペースの設定
Vitessの世界では、スタンドアロンの「データベース」という概念を超えて、キースペース(Keyspace)について話します。キースペースは、複数のシャードに分割できる論理的なグルーピングです。まだシャディングを行っていない場合、キースペースは標準的なMySQLスキーマとまったく同じように動作します。
commerceという名前のキースペースを持つクラスターを定義してみましょう。水平分割を実演するために、2つのシャードから開始します。この設定をvttest.yamlとして保存してください:
apiVersion: vitess.io/v2
kind: VitessCluster
metadata:
name: production-cluster
spec:
cells:
- name: zone1
gateway:
replicas: 1
keyspaces:
- name: commerce
turndownPolicy: Immediate
partitionings:
- equal:
parts: 2
shardTemplate:
tabletPools:
- cell: zone1
type: replica
replicas: 2
設定をクラスターにデプロイします:
kubectl apply -f vttest.yaml
このコマンドにより、オペレーターは2つの独立したシャードを起動します。各シャードは、MySQLインスタンスと、基盤となるデータベースプロセスを管理するサイドカーであるVTTabletで構成されます。
アーキテクチャの分解
Podが初期化されている間に、主要な構成要素を見てみましょう。3つの主要なコンポーネントがあることに気づくでしょう:
- VTGate: トラフィックコントローラーです。アプリケーションはここに接続します。SQLを解析し、正しいシャードにルーティングします。
- VTTablet: ガーディアン(守護者)です。各MySQLインスタンスの隣に1つのVTTabletが配置され、コネクションプールを管理し、データベースを「死のクエリ(queries of death)」から保護します。
- Topology Service: 通常はetcdによって支えられており、すべてのシャードとタブレットがどこに存在するかを示す信頼できる情報源(Source of Truth)です。
VSchemaの力
Vitessはどのユーザーをどのシャードに割り当てるかをどのように判断するのでしょうか?このロジックはVSchema内にあります。VSchemaがなければ、Vitessは単なるプロキシにすぎません。これがあることで、シャディングエンジンとなります。
usersテーブルがある場合、「Vindex」(Vitess Index)を使用して行をシャードにマッピングします。以下は、ハッシュベースの分散を使用した実用的なVSchemaのスニペットです:
{
"sharded": true,
"vindexes": {
"hash_vindex": {
"type": "hash"
}
},
"tables": {
"users": {
"column_vindexes": [
{
"column": "user_id",
"name": "hash_vindex"
}
]
}
}
}
ほとんどのマルチテナント・ワークロードにはhash vindexをお勧めします。データを均等に分散し、特定のサーバーがトラフィックの90%を処理して他のサーバーがアイドル状態になるような「ホットスポット」を防ぎます。
検証とアプリケーションの統合
PodがRunningステータスになったら、接続の準備は完了です。VTGateは標準的なMySQLサーバーと同一に見えるポートを公開します。既存のORMやデータベースクライアントは違いを認識しません。
ゲートウェイをローカル環境にポートフォワードします:
kubectl port-forward svc/production-cluster-zone1-vtgate 3306:3306
次に、標準的なクライアントを使用して接続します:
mysql -h 127.0.0.1 -P 3306 -u user commerce
ここでのエンジニアリング上の利点は「透過性(invisibility)」です。SELECT * FROM usersを実行すると、Vitessが両方のシャードにクエリを投げ、結果をマージして、単一のセットとして返します。開発者は複雑なデータルーティングロジックを書く代わりに、機能開発に集中できます。
モニタリングと高可用性
本番環境で状況を把握せずに運用(フライング・ブラインド)することは、災難への入り口です。Vitessには、クラスターの健全性を可視化するvtctldというウェブダッシュボードが含まれています。
# vtctldダッシュボードにアクセス
kubectl port-forward svc/production-cluster-zone1-vtctld 15000:15000
http://localhost:15000にアクセスして、レプリカを監視します。プライマリノードに障害が発生した場合、Vitess Operatorは自動的にレプリカをプライマリに昇格させます。私は実際にノード障害が発生した際に、アプリケーションの書き込み中断が10秒未満で済み、読み取りのダウンタイムがゼロだったのを目の当たりにしました。
深いオブザーバビリティ(観測性)を得るために、私は常に/metricsエンドポイントをPrometheusにスクレイピングしています。vttablet_query_countやvttablet_query_error_countなどのメトリクスを追跡することで、パフォーマンスの低下がユーザーに影響を与える前に察知できます。
最後に
Vitessの導入は戦略的な転換です。新しいアーキテクチャコンポーネントが導入されますが、データ成長に対する厳しい制限が取り払われます。2TBのストレージ制限や秒間5,000クエリのボトルネックを恐れる必要がなくなれば、自信を持ってシステムを構築できます。水平スケーリングは、YAMLファイルを更新し、オペレーターに重労働を任せるのと同じくらい簡単になります。

