SQL Server のインメモリ OLTP を使う(CodeDefiner の改修案)
本体の標準機能ではありません
CodeDefiner が作るのはディスクベースの通常のテーブルだけで、メモリ最適化テーブルを作る設定はありません。このページは、SQL Server のインメモリ OLTP を一部のテーブルに使う場合の設計メモです。
前提にした現行実装
- SQL Server のテーブルは
CreateTable.sqlでcreate tableした後、CreatePk.sqlでprimary key clusteredを後から付けます(CreateTable.sql、CreatePk.sql)。インデックスもCreateIx.sqlで後から作ります。 - 列の構成が変わったテーブルは、新しいテーブルを作って
MigrateTable.sql/MigrateTableWithIdentity.sqlでデータを移し、名前を付け替えます(CodeDefiner)。 - 全テーブルに共通の
Comments列はnvarchar(max)です(_Bases_Comments.json)。 BinariesのBin・Thumbnail・Iconはimage型です(Binaries_Bin.json)。Items.FullTextとBinaries.Binには全文索引が張られます(CreateFullText.sql(SQL Server))。- テーブルは 1 種類につきベース・
_deleted・_historyの 3 つが作られます(データベースのテーブル構成)。
メモリ最適化テーブルの制約
SQL Server 2014 以降の機能で、Pleasanter の列定義(nvarchar(max) など)をそのまま使うには 2016 以降が前提です。
| 項目 | 通常のテーブル | メモリ最適化テーブル |
|---|---|---|
| 同時実行制御 | ロック・ラッチ | 楽観的(行のバージョン) |
| 主キー | クラスター化か非クラスター化 | 非クラスター化だけ(ハッシュか BW-tree) |
| インデックス | 後から create index できる | create table の中で定義する |
LOB(nvarchar(max) など) | 使える | 2016 以降で使える(行外に格納) |
image 型 | 使える | 使えない(varbinary(max) にする) |
| 全文索引 | 使える | 使えない |
alter table | 使える | 2016 以降で列の追加・削除・変更ができる |
| 永続性 | ― | SCHEMA_AND_DATA(永続)か SCHEMA_ONLY(再起動で消える) |
| ファイルグループ | 不要 | MEMORY_OPTIMIZED_DATA のファイルグループが必要 |
対象にするテーブル
全テーブルではなく、読み書きが多く、制約に当たりにくいテーブルに絞ります。
| 対象 | 理由 | 注意 |
|---|---|---|
| Sessions | リクエストのたびに読み書きされる(セッション管理の実装) | Value が nvarchar(max) |
| Permissions | アクセス権の判定のたびに読まれる | |
| Statuses | 状態の更新が多い | |
| Orders | 一覧の並び順で読まれる | Data が nvarchar(max) |
| Links | リンクの解決で読まれる | Subset が nvarchar(max) |
| LoginKeys | 認証のたびに読まれる | TenantNames が nvarchar(max) |
| 対象にしない | 理由 |
|---|---|
| Items | 全文索引が使えなくなる |
| Binaries | image 型があり、データが大きい |
| Sites | nvarchar(max) の列が多く、更新は少ない |
| SysLogs | INSERT が中心でデータ量が大きい |
| Results / Issues | 件数が多く、Body・Comments が nvarchar(max) |
| Quartz.NET のテーブル | 外部ライブラリが管理する |
_history / _deleted | 読まれる頻度が低い |
nvarchar(max) の列は行外に格納されるため、その列を読むときはメモリ最適化の効果が薄れます。
設定案
Rds.json に SQL Server 用の設定を足します。新しい Dbms の値は不要です。
json
{
"MemoryOptimized": {
"Enabled": false,
"Tables": ["Sessions", "Permissions", "Statuses", "Orders", "Links", "LoginKeys"],
"Durability": "SCHEMA_AND_DATA",
"IncludeDeletedTables": false,
"IncludeHistoryTables": false
}
}| 項目 | 内容 |
|---|---|
Enabled | インメモリ OLTP を使うか |
Tables | メモリ最適化にするテーブル(ベースの名前) |
Durability | SCHEMA_AND_DATA か SCHEMA_ONLY |
IncludeDeletedTables / IncludeHistoryTables | _deleted / _history も対象にするか |
改修箇所
| 箇所 | 改修内容 |
|---|---|
Implem.ParameterAccessor/Parts/Rds.cs | MemoryOptimized の設定クラスと、テーブル名と TableTypes から対象かを返す判定を足す |
App_Data/Definitions/Sqls/SQLServer/ | メモリ最適化テーブルを作る SQL(主キーとインデックスを create table 内に書き、with (memory_optimized = on, durability = ...) を付ける)と、ファイルグループを足す SQL を追加する |
Functions/Rds/RdsConfigurator.cs | DB の作成・更新時に MEMORY_OPTIMIZED_DATA のファイルグループを足す(Provider が Local のときだけ) |
Functions/Rds/TablesConfigurator.cs | テーブルを作るときに対象なら新しい SQL を使う |
Functions/Rds/Parts/Indexes.cs | インデックスを create table 内の定義として出力する |
Functions/Rds/Parts/Tables.cs | 既存のテーブルがメモリ最適化かを sys.tables.is_memory_optimized で調べ、設定と違えば作り直す |
ファイルグループの追加は次のような SQL です。
sql
alter database [Implem.Pleasanter]
add filegroup [InMemoryData] contains memory_optimized_data;
alter database [Implem.Pleasanter]
add file (name = N'InMemoryData', filename = N'C:\Data\InMemoryData')
to filegroup [InMemoryData];通常のテーブルとの切り替え
図を読み込み中…
どちらの向きも、新しいテーブルを作って insert ... select でデータを移し、sp_rename で名前を付け替えます。既存の MigrateTable.sql / MigrateTableWithIdentity.sql と同じ手順です。メモリ最適化テーブルの列の変更は、SQL Server 2016 以降なら alter table でもできます。
インデックスの種類
ハッシュインデックスの BUCKET_COUNT は行数の 1〜2 倍が目安ですが、行数は環境ごとに違います。範囲検索や order by が多いことも考えると、既定は BW-tree(非クラスター化)にし、ハッシュは設定で選べるようにするのが扱いやすくなります。
注意点
- Azure SQL Database では Premium か Business Critical の層でインメモリ OLTP が使え、ファイルグループは自動で管理されます。Amazon RDS for SQL Server にも制限があります。
ProviderがLocal以外のときはファイルグループの追加を飛ばします。 - メモリ最適化テーブルと通常のテーブルを 1 つのトランザクションで使う場合、メモリ最適化テーブル側で
REPEATABLE READとSERIALIZABLEは使えません。Pleasanter は分離レベルを指定していないので、既定のREAD COMMITTEDで動きます。 - PostgreSQL と MySQL には同じ機能がありません。CodeDefiner の
migrateはinsert ... selectでデータを移すので、移行元がメモリ最適化テーブルでもそのまま読めます。移行先では通常のテーブルとして作ります。