はじめに
こんにちは、技術本部SRE部ZOZOSREチームの堀口です。普段はZOZOTOWNのオンプレミスとクラウドの構築・運用に携わっています。またDBREとしてZOZOTOWNのデータベース全般の運用・保守も兼務しております。
ZOZOTOWNではSQL Serverを中心とした各種DBMSが稼働しています。その中でZOZOTOWNサービスの根幹となるいわゆる基幹データベース(以下、基幹DBと呼ぶ)を5年ぶりにリプレースしました。
基幹DB群は、商品情報、在庫情報、注文情報、会員様情報、ブランド様情報、配送管理、キャンペーン情報、分析系情報などZOZOTOWNサービスにおけるほぼ全ての情報を管理しているものとなります。
リプレースのモチベーションは5年のハードウェア(以下、HWと呼ぶ)保守期限終了およびSQL ServerのEnd Of Life(以下、EOLと呼ぶ)を迎えるため、HWの更改/SQL Server、OSのバージョンアップが必要となったことです。
本記事では、どのようにこれら基幹DBリプレースを推進したか、ZOZOTOWNサービスを引き続き快適に利用して頂けるよう案件を推進したかという内容を紹介させて頂きます。
目次
- はじめに
- 目次
- 基幹DBのリプレース方針
- 機器選定
- A社 or B社?
- 機器スペック選定
- OS/SQL Server/各種ミドルウェアのバージョン選定
- 切替計画
- SQL Server2012と2019のクエリ互換性
- 基幹DBの理解
- 設計・構築
- テスト
- アプリケーションテスト
- 性能テスト対象クエリの抽出方針
- 負荷ツールの選定
- 性能テスト結果
- 障害テスト
- 運用テスト
- 品質管理テスト
- 切替リハーサル
- 切替リハーサル結果
- 本番切替
- 終わりに
基幹DBのリプレース方針
基幹DB群はZOZOTOWN創業時から存在すること、サービスにおける絶大な影響を持つことから、いかに安全かつ革新的にこのリプレース案件を成功させるかをずっと考えていました。
その結果、以下の方針でリプレースすることにしました。
- 改善したいところはあるものの、手を出さずにオンプレミスサーバ⇒オンプレミスサーバへの単純リプレースとする
- ベンダー依存の体制から脱却する
- 各種連携システム(主に人)と完璧に情報を共有し「漏れ」をなくす
1つ目の単純リプレースとした理由は「安全にリプレースする」ことを最優先としたためです。改善したかった所としては以下のものがありましたが、本プロジェクトのスコープからは除外しました。
- 不要オブジェクトやデータ連携ルートの削除、既存オブジェクトの適切なDBへの移動
- オンプレミスサーバに存在する理由のないテーブルや処理は「Amazon RDS」,「Amazon Aurora」,「Google Cloud BigQuery」などに部分移行
2つ目のベンダー依存に関しては、前例と実績を重視するあまり機器選定からリリースへ至るまで(言い方は少々悪いですが)ベンダー任せにしてしまっているところがありました。機器選定時、適切な検証と選択を行なわず、また運用に関しても自社エンジニアでできることはほとんどなくベンダーに頼り切りとなっている体質があったため、今回はこれも是正したいと強く思っていました。
3つ目に、基幹DBと連携するサーバやシステムが多数あるため、リリース時には複数システムで切替作業が必要となります。この方針決めの時点では全容は把握できていませんでしたが、日々のDBREとしての活動の中で容易に想像できました。
以降、上記方針に基づいてあらゆる事を判断していくこととなります。
機器選定
対象となるHW機器は大きく分けて以下の3つです。
- サーバ製品
- ストレージ製品
- スイッチ製品
という事を念頭に置いて選定を行なっていくことになります。また選定した機器と委託するベンダーとは関連があり、選定した機器に対してそれを得意とするベンダーを構築担当としてお願いすることになります。そのため、機器選定=ベンダー選定である
機器選定においては、A社とB社の製品を比較・検討していくことにしました。まず実施したことはストレージ製品の性能比較です。性能比較にあたっては、以下の方針を立てました。
- 同一条件で同一処理を実行し、ストレージ単体の秒間スループット、IOPS、レイテンシを比較する
- 同様にSQL Server経由での性能を比較する
- 読み込み/書き込み、ブロックサイズ(8K、64K、256K)、スレッド数(1、32)、sequential/randomの全ての組み合わせパターンを比較する
- SQL Serverを経由したselect/insert/update/deleteのパターンを比較する
- 製品間の性能差を計測するものであり本番環境の性能を保証するレベルのテストではない(≒限界値テストは行わない)
この検証をするための検証環境は各ベンダーに用意して頂きました。
ディスク単体の性能計測はMicrosoft社(以下、MS社と呼ぶ)製のベンチマークツールである「DiskSpd」を使用しました。これは弊社内でノウハウがあったことと、オプションが多数あり細かい動作を指定できること、outputが見やすいことから採用しました。
DISKSPD を使用してワークロード ストレージのパフォーマンスをテストするを参考に実行したDISKSPDコマンド例は次のとおりです。
Diskspd.exe -c1G -b8K -t1 -o1 -L -h -si -d120 T:\test.dat
- c1G:テスト用ファイルサイズ1GB
- b8K:ブロックサイズ8KB
- t1:同時スレッド数1
- o1:キューデプス1
- L:レイテンシー統計出力
- h:ソフトウェア/ハードウェアキャッシュを無効化する
- si:シーケンシャルアクセス
- d120:120秒間実行
- T:\test.dat:テストファイル名
上記のコマンドを実行するとこのように細かく性能値を出力してくれます(少々長いです)。
Diskspd結果
Command Line: Diskspd.exe -c1G -b8K -t1 -o1 -L -h -si -d120 T:\test.dat
Input parameters:
timespan: 1
-------------
duration: 120s
warm up time: 5s
cool down time: 0s
measuring latency
random seed: 0
path: 'T:\test.dat'
think time: 0ms
burst size: 0
software cache disabled
hardware write cache disabled, writethrough on
performing read test
block size: 8KiB
using interlocked sequential I/O (stride: 8KiB)
number of outstanding I/O operations per thread: 1
threads per file: 1
IO priority: normal
System information:
computer name: PE640-SP01
start time: 2021/12/13 12:26:03 UTC
Results for timespan 1:
*******************************************************************************
actual test time: 120.01s
thread count: 1
proc count: 32
CPU | Usage | User | Kernel | Idle
-------------------------------------------
0| 20.71%| 0.65%| 20.06%| 79.29%
1| 0.03%| 0.01%| 0.01%| 99.97%
2| 0.01%| 0.00%| 0.01%| 99.99%
3| 0.03%| 0.00%| 0.03%| 99.97%
4| 0.01%| 0.01%| 0.00%| 99.99%
5| 0.74%| 0.34%| 0.40%| 99.26%
6| 0.14%| 0.05%| 0.09%| 99.86%
7| 0.18%| 0.08%| 0.10%| 99.82%
8| 0.13%| 0.07%| 0.07%| 99.87%
9| 0.04%| 0.03%| 0.01%| 99.96%
10| 0.12%| 0.09%| 0.03%| 99.88%
11| 0.08%| 0.04%| 0.04%| 99.92%
12| 0.13%| 0.12%| 0.01%| 99.87%
13| 0.00%| 0.00%| 0.00%| 100.00%
14| 0.33%| 0.10%| 0.22%| 99.67%
15| 0.03%| 0.00%| 0.03%| 99.97%
16| 1.76%| 0.00%| 1.76%| 98.24%
17| 0.04%| 0.03%| 0.01%| 99.96%
18| 0.03%| 0.00%| 0.03%| 99.97%
19| 0.00%| 0.00%| 0.00%| 100.00%
20| 0.04%| 0.03%| 0.01%| 99.96%
21| 0.04%| 0.03%| 0.01%| 99.96%
22| 0.03%| 0.01%| 0.01%| 99.97%
23| 0.00%| 0.00%| 0.00%| 100.00%
24| 1.05%| 0.00%| 1.05%| 98.95%
25| 0.00%| 0.00%| 0.00%| 100.00%
26| 0.01%| 0.00%| 0.01%| 99.99%
27| 0.00%| 0.00%| 0.00%| 100.00%
28| 0.07%| 0.05%| 0.01%| 99.93%
29| 0.00%| 0.00%| 0.00%| 100.00%
30| 0.00%| 0.00%| 0.00%| 100.00%
31| 0.05%| 0.03%| 0.03%| 99.95%
-------------------------------------------
avg.| 0.81%| 0.05%| 0.75%| 99.19%
Total IO
thread | bytes | I/Os | MiB/s | I/O per s | AvgLat | LatStdDev | file
-----------------------------------------------------------------------------------------------------
0 | 6798966784 | 829952 | 54.03 | 6915.95 | 0.144 | 0.115 | T:\test.dat (1GiB)
-----------------------------------------------------------------------------------------------------
total: 6798966784 | 829952 | 54.03 | 6915.95 | 0.144 | 0.115
Read IO
thread | bytes | I/Os | MiB/s | I/O per s | AvgLat | LatStdDev | file
-----------------------------------------------------------------------------------------------------
0 | 6798966784 | 829952 | 54.03 | 6915.95 | 0.144 | 0.115 | T:\test.dat (1GiB)
-----------------------------------------------------------------------------------------------------
total: 6798966784 | 829952 | 54.03 | 6915.95 | 0.144 | 0.115
Write IO
thread | bytes | I/Os | MiB/s | I/O per s | AvgLat | LatStdDev | file
-----------------------------------------------------------------------------------------------------
0 | 0 | 0 | 0.00 | 0.00 | 0.000 | N/A | T:\test.dat (1GiB)
-----------------------------------------------------------------------------------------------------
total: 0 | 0 | 0.00 | 0.00 | 0.000 | N/A
total:
%-ile | Read (ms) | Write (ms) | Total (ms)
----------------------------------------------
min | 0.111 | N/A | 0.111
25th | 0.131 | N/A | 0.131
50th | 0.137 | N/A | 0.137
75th | 0.147 | N/A | 0.147
90th | 0.161 | N/A | 0.161
95th | 0.185 | N/A | 0.185
99th | 0.226 | N/A | 0.226
3-nines | 0.478 | N/A | 0.478
4-nines | 0.960 | N/A | 0.960
5-nines | 2.050 | N/A | 2.050
6-nines | 100.823 | N/A | 100.823
7-nines | 100.823 | N/A | 100.823
8-nines | 100.823 | N/A | 100.823
9-nines | 100.823 | N/A | 100.823
max | 100.823 | N/A | 100.823
これらの結果から重要なメトリクスを抽出し、一覧化し比較しました。
結果より、ストレージへの純粋なアクセス性能においては、ほぼ全てのパターンでA社の方がB社より優秀 ということがわかりました。
次にSQL Serverを経由したアクセス性能を検証しました。 対象クエリについてはOLTP/バッチ処理を想定した本番環境に実際に発行されている「select/update/insert/delete」文の中から重いと判断したものを抽出しました。それをテスト用クエリとしシングル/並列スレッドでSSMSから実行しました。
また検証用サーバ機は調達の問題により両社でCPUのスペック差が発生していたため、以下の設定をSQL Serverに行うことで条件を統一しました。
- SQL Serverが使用するCPUについて、A社のCPUのコア数をB社の32コアに合わせ32コアへ縮小する
- 関係マスクを自動→固定に変更(両社とも)
観察すべきメトリクスは色々ありますが、レスポンス時間、リソース使用状況を取得するため、以下の取得設定を事前に仕込み、性能測定に使用しました。
- クエリストア
- パフォーマンスモニタ
- DMV情報
またオプティマイザによって作成される実行プランに違いが出てしまうと純粋な比較とならないため、実行プランが同じであることは項目毎に確認していきました。テスト結果はDiskSpdと同様に一覧化し比較しました。結果的としては、DiskSpdの結果と同様にA社製品の方が性能的に”上”という結果となりました。
レスポンス
リソース状況
A社 or B社?
上で書いた通り機器選定=ベンダー選定でもあり、システム構築や5年後までを見据えた保守サポートなど長いお付き合いとなります。また当然ですがコストについても比較しなければ決定するには不十分です。基幹DBのワークロードにおける性能比較はA社の方に分がありましたが、果たしてその結果だけで評価してよいものか考えました。
全体の評価には、以下の評価項目について両社を比較し合計点の大きい方を採択するという方針を決めました。
なるべく公平な評価を目指した結果このような点数制になりました。これでも主観が入ってしまうことは否めませんが、最終的にはこの点数を根拠として、本リプレース案件はA社製品を採択することになりました。
5年前のリプレース以前からZOZOTOWNが採用してきた基幹DBのHWベンダーを今回の基幹DBリプレースから一新することになりました。
機器スペック選定
基幹DBはオンプレミスなサーバ群のため当然ですがクラウド環境のように容易なスペック変更はできません。そのためこの時点でスペック不足とならないよう慎重にCPU/メモリ/ディスクサイズを決定する必要があります。選定の方針としては以下の2つです。
- 現行で稼働する基幹DBのスペックとリソース使用率を基準に将来的な成長率を考慮し決定する
- 多少のオーバースペックは許容する
ここについては、HWベンダーのアセスメントサービスを利用して最終的なスペックを提案して頂き決定しました。
OS/SQL Server/各種ミドルウェアのバージョン選定
基本方針は以下の通りです。
- 原則最新バージョンで構成する
- SQL Serverについては連携するシステムとの互換性に注意する
SQL Serverのバージョンは最新である2019を採用することを軸に検討しました。また互換性レベルも150として現行の100から大幅に引き上げることにしました。
基幹DBは各種システム(サーバ)とのデータ連携にSQL Serverのトランザクションレプリケーション機能を利用してます。パブリッシャー側とサブスクライバー側のSQL Serverバージョンは仕様上、互換性の制約があり3世代以上のバージョンの隔たりは許容されていません。このため最新のSQL Server2019にバージョンアップするDBとSQL Server2016を採用せざるを得ないDBが混在することになりました。このバージョン互換性問題は後述するシステム切替方式にも影響することになります。
OS/ミドルウェアについては特段問題なかったので最新バージョンを選択しました。
切替計画
切替計画を立てる際、一番に考えたのはZOZOTOWNサービスの無停止での切替ができないかでした。現行DBサーバの横に新DBサーバ群を構築しテストし、切替当日クライアント側で向き先を一気に切り替える方式はほぼ決定していましたが、以下の理由で無停止での切替は断念しました。
- 新旧サーバ間のデータ同期にDBリストアを行う必要があるが、その後レプリケーションを再作成する必要があり、一時的にテーブルのDROP/CREATEが走ること
- クライアント側が点で切り変わらない限り一部のクライアントは新DB、一部のクライアントは旧DBを更新することになりデータの不整合が発生すること
そこで以下の全体方針を立てました。
- 切り替え当日は、レプリケーションの張り直し作業が必要となるため、数時間のZOZOTOWNのサービス停止が必要となる
- 1日のサービス停止時間の削減および切り戻しの容易さを考慮し、切り替えは2段階で行う(以降、フェーズ1、フェーズ2と呼ぶ)
- フェーズ1およびフェーズ2の間の過渡期ではSQL Server2019(FrontDB/BackDB)とSQL Server2012(ReportDB/BatchDB)が両立しサービスすることになる
- SQL Server2019と2012はレプリケーションの互換性がないため、互換性を保つため一時的に2016のSQL Serverを中継用に挟むことにより対応する
ZOZOTOWNを停止することは避けられないとしても、停止時間が9時間になるのと18時間になるのとではZOZOTOWNユーザーへの影響度が変わってきます。また一度に変更する範囲を狭めることで問題が発生した際の切り分けや対応範囲が限定され解決しやすくなるという理由から、FrontDB/BackDBの切替とReporDB/BatchDBの切替を別日になるよう日程を2つに分けて切替を行うことにしました。
本番環境の状態遷移を図にしてみます。上段が現行DB、下段が新DBとなります。矢印はレプリケーションの線になります。
FrontDB、BackDBだけ切り替えた状態がフェーズ1完了時となります。この時、SQL Serverのバージョン互換性の問題で新FrontDB/新BackDBと旧ReportDB/旧BatchDBとの間で直接レプリケーションを張ることができません。このため新ReportDB上に中間となるバージョンを持った中継用SQL Serverインスタンスを暫定的に挟むことでバージョン互換性の問題をクリアしました。
フェーズ2完了時点では中間インスタンスを削除し、新DBのみで稼働することになります。
レプリケーションの互換性の詳細はSQL Serverマニュアル(レプリケーションの下位互換性) をご参照ください。 フェーズ1完了時は、「SQL Server 2019」→「SQL Server 2016」→「SQL Server 2012」という構成でデータ連携することにしました。
SQL Server2012と2019のクエリ互換性
バージョンアップにおける既存クエリの互換性のチェックについてはMS社が提供する「Data Migration Assistant(DMA)」を利用しました。このサービスの詳細は弊社テックブログ(Data Migration Assistant)を参照頂くとして、簡単に言うとクエリを渡すと異なるSQL Serverのバージョン間で互換性があるか、つまり動くかをチェックしてくれるものです。
本案件では、下記の両方についてDMAを利用してチェックをかけました。
続きはこちら