読者です 読者をやめる 読者になる 読者になる

SQL Server 2014 メモリ最適化テーブル(Hekaton)でクエリ通知を試してみる (動かなかった)

SQL Server .NET

SQL Server のクエリ通知 をメモリ最適化テーブルで使えるか試してみます。
クエリ通知は、ざっくり言うと、データの変更を検知し、SQL Server からクライアントに通知を行ってくれる機能です。
まず DB を作ります。

CREATE DATABASE [db1]
  ON  PRIMARY
  ( NAME = N'db1',
    FILENAME = N'C:\DATA\db1.mdf',
    SIZE = 5120KB , FILEGROWTH = 1024KB ),
  FILEGROUP [MemoryOptimizedFG] CONTAINS MEMORY_OPTIMIZED_DATA
  ( NAME = N'MemoryOptimizedFile',
    FILENAME = N'C:\DATA\MemoryOptimizedFile' )
  LOG ON
  ( NAME = N'db1_log',
    FILENAME = N'C:\DATA\db1_log.ldf',
    SIZE = 1024KB , FILEGROWTH = 10%)

Service Broker を有効にします。

alter database db1 set enable_BROKER with rollback immediate

で、メモリ最適化テーブルを用意します。

CREATE TABLE [dbo].[MemTable] (
  [Id] int NOT NULL,
  [Name] nvarchar(20) NOT NULL
    PRIMARY KEY NONCLUSTERED HASH ([Id]) WITH (BUCKET_COUNT = 10000000)
) WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY)

クエリ通知の検証用に通常のテーブルも作っときます。

CREATE TABLE [dbo].[NormalTable] (
  [Id] int not null primary key, 
  [Name] nvarchar(20) not null 
)

クエリ通知を有効化するための設定
クエリ通知の有効化

CREATE QUEUE ChangeData;

CREATE SERVICE DataChangeNotifications
  ON QUEUE ChangeData
([http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]);

通知されるアプリはこちら。

using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Threading.Tasks;

namespace QueryNotify {
  class Subscriber : IDisposable {
    private const string ConnStr = @"Data Source=(local)\MSSQL2014;Initial Catalog=db1;Integrated Security=True";
    private const string Query = @"select [Id], [Name] from [dbo].[MemTable] order by [Id]";

    public void Initialize() {
      SqlDependency.Stop(ConnStr);
      SqlDependency.Start(ConnStr);  
    }

    public async Task WriteData() {
      using (var conn = new SqlConnection(ConnStr))
      using (var cmd = new SqlCommand(Query, conn)) {
        var dependency = new SqlDependency(cmd);
        dependency.OnChange += DependencyOnOnChange;
        await conn.OpenAsync();

        using (var reader = await cmd.ExecuteReaderAsync()) {
          while (await reader.ReadAsync()) {
            Console.WriteLine("Id:{0} Name;{1}", await reader.GetFieldValueAsync<object>(0), await reader.GetFieldValueAsync<object>(1));
          }
        }
      }
    }

    private void DependencyOnOnChange(object sender, SqlNotificationEventArgs e) {
      var dependency = (SqlDependency)sender;
      dependency.OnChange -= DependencyOnOnChange;
      Debug.WriteLine("Info:{0} Source:{1} Type:{2}", e.Info, e.Source, e.Type);
      WriteData().Wait();
    }

    public void Dispose() {
      SqlDependency.Stop(ConnStr);
    }
  }

  class Program {
    static void Main(string[] args) {
      using (var s = new Subscriber()) {
        s.Initialize();
        s.WriteData().Wait();
        Console.ReadKey();
      }
    }
  }
}

de:code でジニアスのセッションを見たので async/await 使ってみました。
それ以外は何てことないクエリ通知のサンプルです。
でこれを動かすと、エラーは出ないものの通知を検知してくれません。

ためしに、通常のテーブルに変更する*1とちゃんと通知してくれます。

メモリ最適化テーブルは、SCHEMA_ONLY/SCHEMA_AND_DATA 両方試しましたがダメでした。
SCHEMA_ONLY なメモリ最適化テーブルで使えると面白そうだったんですけどねー。

クエリ通知の注意事項はこちら
クエリ通知を使用するときの特別な注意事項 (ADO.NET)
SQL Server がローカルシステムアカウントで動いてたり、クライアントが Win95/98 だとダメだったりします。
他にも利用出来るクエリに制限があります。

3年ほど前ですが、クエリ通知の発表してたみたいです。

*1:Query の from を MemTable -> NormalTable