SQL Server を Azure の仮想マシンで実行した際に、接続したディスクを効果的に使用するために記憶域プール (ストレージプール / 記憶域スペース) を使用することがあるかと思います。
SQL Server を Azure VM で動作させた場合のストレージに対しての考慮事項については、
Azure の仮想マシンにおける SQL Server のパフォーマンスに関する考慮事項
Microsoft Azure Universal Storage for SQL Server 2014
Scaling-out SQL Server disks and data files on Windows Azure Virtual Machines…a real-world example
Azure 仮想マシンにおける SQL Server のパフォーマンス ガイダンス
などで紹介されており、基本的な考え方としては「複数のディスクに I/O を分散させて、IOPS の上限を緩和させる」ことになるかと思います。
先月、情報が更新された Azure の仮想マシンにおける SQL Server のパフォーマンスに関する考慮事項 に以下の記述がありました。
ディスク ストライピング:以下のガイドラインに従うことをお勧めします。
- Windows 8/Windows Server 2012 またはそれ以降については、ストレージ領域を使用してください。パーティションの不適切な配置がパフォーマンスに影響を及ぼすことがないように、ストライプのサイズを OLTP ワークロードの場合は 64 KB に、データ ウェアハウジング ワークロードの場合は 256 KB に設定します。さらに、
column count = number of physical disks
を設定します。ストレージ スペースを構成する方法の詳細については、「Windows PowerShell のストレージ領域コマンドレット」を参照してください。- Windows 2008 R2 またはそれ以前については、ダイナミック ディスク (OS ストライピング ボリューム) を使用することができ、ストライプのサイズは常に 64 KB となります。Windows 8/Windows Server 2012 の場合、このオプションはサポートされていません。詳細については、「仮想ディスク サービスは Windows Storage Management API に移行中である」のサポート ポリシーを参照してください。
Windows Server 2012 以降は記憶域プールの機能が追加されており、物理環境で利用しようとした場合にはいろいろと利用前提を満たす必要がありますが、仮想マシンで利用する場合には、接続された仮想ディスクを使用して構築することができるかと思います。
記憶域プールでは GUI から仮想ディスクを作成した場合、Column Count (NumberOfColumns) の上限が 8 となります。
そのため、16 本のディスクを接続した場合でも、データを分散させるディスク数 (列数) は 8 となり、I/O 効率に影響が出てきます。
以下は列数を 1 / 2/ 4/ 8/ 12 / 16 で設定した環境で CrystalDiskMark を実行した結果になります。
16 本のディスクを使用しているのですが列数が 1 の場合は低い値を示していますね。
以下の表が、16 本のディスクを列数を変更しながら SQLIO を実行した結果となります。
16 本のディスクを使用していても列数が低いとデータを分散させるディスク数が少ないことになりますので、I/O 効率が低いものとなっています。
GUI からデフォルトで作成した仮想ディスクでは、列数の上限が 8 になりますので、列数を考慮しないで作成した仮想ディスクでは、I/O 効率が低い可能性があるということになります。
列数 | 8KB | 64KB | |||||||
Seq Read | Seq Write | Rand Read | Rand Write | Seq Read | Seq Write | Rand Read | Rand Write | ||
1 | IOs/sec | 506.18 | 496.62 | 610.88 | 607.49 | 526.81 | 546.16 | 607.87 | 611.14 |
MBs/sec | 3.95 | 3.87 | 4.77 | 4.74 | 32.92 | 34.13 | 37.99 | 38.19 | |
2 | IOs/sec | 898.76 | 883.41 | 883.50 | 879.09 | 496.91 | 496.92 | 496.99 | 497.02 |
MBs/sec | 7.02 | 6.90 | 6.90 | 6.86 | 31.05 | 31.05 | 31.06 | 31.06 | |
4 | IOs/sec | 1,798.95 | 1,766.52 | 1,762.44 | 1,766.67 | 991.36 | 998.88 | 987.20 | 1,004.13 |
MBs/sec | 14.05 | < font style="font-size:11pt;" color="#000000">13.80 | 13.76 | 13.80 | 61.96 | 62.43 | 61.70 | 62.75 | |
8 | IOs/sec | 3,596.08 | 3,533.12 | 3,511.58 | 3,520.75 | 1,981.13 | 1,995.41 | 1,973.21 | 2,007.50 |
MBs/sec | 28.09 | 27.60 | 27.43 | 27.50 | 123.82 | 124.71 | 123.32 | 125.46 | |
12 | IOs/sec | 5,387.93 | 3,620.82 | 5,247.26 | 5,281.64 | 2,032.11 | 3,030.33 | 2,042.19 | 3,006.93 |
MBs/sec | 42.09 | 28.28 | 40.99 | 41.26 | 127.00 | 189.39 | 127.63 | 187.93 | |
16 | IOs/sec | 5,865.58 | 7,185.74 | 5,975.82 | 7,114.32 | 2,045.36 | 3,699.20 | 2,023.37 | 3,822.85 |
MBs/sec | 45.82 | 56.13 | 46.68 | 55.58 | 127.83 | 231.20 | 126.46 | 238.92 |
今回は記憶域プールを作成した後に以下のような PowerShell で仮想ディスクを作成しています。
$columns = 16 $interval = 64KB if((Get-VirtualDisk -FriendlyName "vDisk" -ErrorAction SilentlyContinue) -ne $null){ Remove-VirtualDisk -FriendlyName "vDisk" -Confirm:$false } New-VirtualDisk -FriendlyName "vDisk" -UseMaximumSize -StoragePoolFriendlyName "Storage Pool" -ResiliencySettingName simple ` -NumberOfColumns $columns -Interleave $interval $disk = Get-Disk | Where PartitionStyle -eq "RAW" $disk | Initialize-Disk $part = $disk | New-Partition -UseMaximumSize $part | Set-Partition -NewDriveLetter "E" Start-Sleep 5 $part | Format-Volume -FileSystem NTFS -Confirm:$false -AllocationUnitSize 64KB Get-VirtualDisk -FriendlyName "vDisk" | select NumberOfColumns
記憶域プールから仮想ディスクの作成は New-VirtualDisk で実行できますが、コマンドレットで仮想ディスクを作成する際には、[-NumberOfColumns] というオプションでデータをどれだけのディスクに分散させるかを指定することができます。
上限としては接続されているディスク数になるのですが、GUI から作成した場合には、16 本のディスクを追加していても 8 となるため、データの分割が 8 ディスク (Max 4,000 IOPS) となります。
また、ディスクに書き込むストライピングサイズに関してはデフォルトでは 256KB となっています。
ストライプのサイズを OLTP ワークロードの場合は 64 KB に、データ ウェアハウジング ワークロードの場合は 256 KB に設定します。
とのことから、OLTP を想定してストライプのサイズを 64KB にして仮想ディスクを作成するように設定しています。
本当はフォーマット時の AllocationUnitSize も 64 KB にしておくとよかったのでしょうが、計測した際には忘れていました…。
# 上記の PowerShell には 64KB でフォーマットするようにしています。
SQL Server 2014 ではデータファイルを Azure の BLOB ストレージに直接配置ができるようになり、データディスクを使用しないで、データベースを作成することが可能となっています。
ログファイルについても BLOB ストレージに直接配置ができますが、ログファイルについては複数のファイルで構成することによるディスク I/O の効率化を行うことができません。
ログファイルはシーケンシャルに書き込みが行われるため、複数のログファイルで構成しても、
- ログファイル 1 の領域がいっぱいになったらログファイル 2 を使用する
というような書き込みが行われます。
そのため、ログファイルの書き込みを効率化するためには「複数のディスクで構成された領域を使用してログファイルを配置する」必要が出てきます。
Windows Server 2012 以降の仮想マシンであれば、これを記憶域プールを使用することで実現することができます。
複数のデータベースを作成する必要がある場合は極論ですが、
- 16 本のデータディスクで作成された仮想ディスクに全データベースのログファイルを配置
- データベースごとに 4 本で作成した仮想ディスクを使用して、ログファイルを配置
というようなログファイルの効率的な配置方法も検討する必要が出てくるかと。
SQL Server 用の仮想マシンのイメージとして「DataWareHousing」というイメージがギャラリーに登録されています。
こちらですが、DawaWarehousing というエディションということではなく、DWH に最適化された構成で SQL Server を展開するためのイメージとなります。
詳細については Windows Azure の仮想マシンにおける SQL Server データ ウェアハウス で公開されています。
この中でも記憶域プールが使用されています。
展開時には、[C:Program FilesMicrosoft SQL ServerDwIaasConfigDwIaasVM.ps1] が実行されているようなのですが、
$NewStoragePoolPut = New-StoragePool -FriendlyName $DiskLabel -StorageSubsystemFriendlyName "Storage Spaces*" -PhysicalDisks $DisksToConfigure $NewSVirtualDiskOutPut = New-VirtualDisk -StoragePoolFriendlyName $DiskLabel -FriendlyName $DiskLabel -ResiliencySettingName Simple -UseMaximumSize
で記憶域プールが構成されているようでした。
列数指定していないけど、パフォーマンス的に良いのかがちょっと気になりますね。