2012-04-24 15 views
0

私はアプリケーションサーバー上のすべてのユーザーのアクセス許可をキャッシュすると考えています。すべてのユーザーにSqlCacheDependencyを使用することをお勧めしますか?これらのレコードのいずれかが、そのユーザのための私のキャッシュを消去するために、次に変更した場合ユーザーごとにSqlCacheDependencyを使用するのは良い考えですか?

クエリは、この

SELECT PermissionId, PermissionName From Permissions Where UserId = @UserId 

ように私が知っているその方法を見てしまいます。

答えて

5

how Query Notifications workを読むと、単一のクエリテンプレートを使用して多数の依存関係リクエストを作成するのが適切な理由がわかります。 であり、SqlDependencyではないことが暗示されているウェブアプリの場合、何をする予定はOKです。あなたがLinq2Sqlを使用する場合もLinqToCacheを試すことができます。

ファットクライアントアプリケーションのために
var queryUsers = from u in repository.Users 
     where u.UserId = currentUserId 
     select u; 
var user= queryUsers .AsCached("Users:" + currentUserId.ToString()); 

それはOKではないでしょう。ではないので、それ自体は、クエリのが、一般的にSqlDependencyので、接続しているクライアントの数が多い(そのブロック接続アプリケーションドメインあたりa worker thread)との問題がある:をASPで使用されるように設計された

SqlDependency。 NETまたは中間層 サービスは、データベースに対してアクティブである の依存関係を持つ比較的少数のサーバーが存在するサービスです。数百または数千のクライアント コンピュータで、単一の データベースサーバーに対してSqlDependencyオブジェクトが設定されているクライアントアプリケーションでは、 を使用するようには設計されていませんでした。ここで

を更新しました

@usrは彼のポストで行ったように同じテストです。完全なC#コード:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data.SqlClient; 
using DependencyMassTest.Properties; 
using System.Threading.Tasks; 
using System.Threading; 

namespace DependencyMassTest 
{ 
    class Program 
    { 
     static volatile int goal = 50000; 
     static volatile int running = 0; 
     static volatile int notified = 0; 
     static int workers = 50; 
     static SqlConnectionStringBuilder scsb; 
     static AutoResetEvent done = new AutoResetEvent(false); 

     static void Main(string[] args) 
     { 
      scsb = new SqlConnectionStringBuilder(Settings.Default.ConnString); 
      scsb.AsynchronousProcessing = true; 
      scsb.Pooling = true; 

      try 
      { 
       SqlDependency.Start(scsb.ConnectionString); 

       using (var conn = new SqlConnection(scsb.ConnectionString)) 
       { 
        conn.Open(); 

        using (SqlCommand cmd = new SqlCommand(@" 
if object_id('SqlDependencyTest') is not null 
    drop table SqlDependencyTest 

create table SqlDependencyTest (
    ID int not null identity, 
    SomeValue nvarchar(400), 
    primary key(ID) 
) 
", conn)) 
        { 
         cmd.ExecuteNonQuery(); 
        } 
       } 

       for (int i = 0; i < workers; ++i) 
       { 
        Task.Factory.StartNew(
         () => 
         { 
          RunTask(); 
         }); 
       } 
       done.WaitOne(); 
       Console.WriteLine("All dependencies subscribed. Waiting..."); 
       Console.ReadKey(); 
      } 
      catch (Exception e) 
      { 
       Console.Error.WriteLine(e); 
      } 
      finally 
      { 
       SqlDependency.Stop(scsb.ConnectionString); 
      } 
     } 

     static void RunTask() 
     { 
      Random rand = new Random(); 
      SqlConnection conn = new SqlConnection(scsb.ConnectionString); 
      conn.Open(); 

      SqlCommand cmd = new SqlCommand(
@"select SomeValue 
    from dbo.SqlDependencyTest 
    where ID = @id", conn); 
      cmd.Parameters.AddWithValue("@id", rand.Next(50000)); 

      SqlDependency dep = new SqlDependency(cmd); 
      dep.OnChange += new OnChangeEventHandler((ob, qnArgs) => 
      { 
       Console.WriteLine("Notified {3}: Info:{0}, Source:{1}, Type:{2}", qnArgs.Info, qnArgs.Source, qnArgs.Type, Interlocked.Increment(ref notified)); 
      }); 

      cmd.BeginExecuteReader(
       (ar) => 
       { 
        try 
        { 
         int crt = Interlocked.Increment(ref running); 
         if (crt % 1000 == 0) 
         { 
          Console.WriteLine("{0} running...", crt); 
         } 
         using (SqlDataReader rdr = cmd.EndExecuteReader(ar)) 
         { 
          while (rdr.Read()) 
          { 
          } 
         } 
        } 
        catch (Exception e) 
        { 
         Console.Error.WriteLine(e.Message); 
        } 
        finally 
        { 
         conn.Close(); 
         int left = Interlocked.Decrement(ref goal); 

         if (0 == left) 
         { 
          done.Set(); 
         } 
         else if (left > 0) 
         { 
          RunTask(); 
         } 
        } 
       }, null); 

     } 

    } 
} 

50Kサブスクリプションは、ここで、(約5分かかります)に設定された後の統計は、単一のインサートのioのです:1000行を挿入

set statistics time on 
insert into Test..SqlDependencyTest (SomeValue) values ('Foo'); 

SQL Server parse and compile time: 
    CPU time = 0 ms, elapsed time = 0 ms. 

SQL Server Execution Times: 
    CPU time = 16 ms, elapsed time = 16 ms. 

は約7秒かかり、これ数百の通知を発します。 CPU使用率は約11%です。これは私のT420s ThinkPadにあります。

set nocount on; 
go 

begin transaction 
go 
insert into Test..SqlDependencyTest (SomeValue) values ('Foo'); 
go 1000 

commit 
go 
+0

私は、接続されていないユーザーであっても、そのユーザーデータをすべてキャッシュするのは大変だと思います。 –

+0

SqlCacheDependencyにはキャッシュエビクションポリシーがあります。 –

+0

これは基本的に私のテストで示されたのと同じパフォーマンス結果です。依存関係の作成はマルチスレッド化されているため、より高速です。おそらく4倍になるでしょうか? 1000個のインサートの7sは*恐ろしい*です。 100%で11%cpu = 1コア。 – usr

0

ドキュメントは言う:

SqlDependencyは、ASP.NETやデータベースに対して活性 依存関係を持つサーバの比較的少数がある中間層 サービスで使用するように設計されました。クライアントアプリケーションで を使用するように設計されていませんでした。数百または数千のクライアント コンピュータでは、単一の データベースサーバーに対してSqlDependencyオブジェクトがセットアップされていました。

これは、数千のキャッシュ依存関係を開かないように指示します。 SQL Server上でリソースの問題が発生する可能性があります。

いくつかの選択肢があります:

  1. テーブルごとの依存関係を持っているが、テーブルごとに100の依存関係、行のすべてのパーセントのための1つを持っています。これはSQL Serverの許容数である必要がありますが、キャッシュの1%のみを無効にする必要があります。
  2. すべての変更行のIDをロギングテーブルに出力するようにトリガーを設定します。そのテーブルに依存関係を作成し、IDを読み取ります。これにより、変更された行が正確にわかります。

     static void SqlDependencyMassTest() 
         { 
          var connectionString = "Data Source=(local); Initial Catalog=Test; Integrated Security=true;"; 
          using (var dependencyConnection = new SqlConnection(connectionString)) 
          { 
           dependencyConnection.EnsureIsOpen(); 
    
           dependencyConnection.ExecuteNonQuery(@" 
    if object_id('SqlDependencyTest') is not null 
        drop table SqlDependencyTest 
    
    create table SqlDependencyTest (
        ID int not null identity, 
        SomeValue nvarchar(400), 
        primary key(ID) 
    ) 
    
    --ALTER DATABASE Test SET ENABLE_BROKER with rollback immediate 
    "); 
    
           SqlDependency.Start(connectionString); 
    
           for (int i = 0; i < 1000 * 1000; i++) 
           { 
            using (var sqlCommand = new SqlCommand("select ID from dbo.SqlDependencyTest where ID = @id", dependencyConnection)) 
            { 
             sqlCommand.AddCommandParameters(new { id = StaticRandom.ThreadLocal.GetInt32() }); 
             CreateSqlDependency(sqlCommand, args => 
              { 
              }); 
            } 
    
            if (i % 1000 == 0) 
             Console.WriteLine(i); 
           } 
          } 
         } 
    

    はあなたがコンソールを介してスクロールを作成し、依存関係の量を見ることができます:SqlDependencyは私がベンチマークをした大量の使用に適しているかどうかを調べるために

。それは非常に速く遅くなる。その点を証明する必要はないので、私は正式な測定をしませんでした。

また、テーブルへの単純な挿入の実行計画は、コストの99%が50kの依存関係を維持することに関連していることを示しています。

結論:実稼働環境ではまったく動作しません。 30分後に55kの依存関係が作成されました。常に100%CPUでマシンを稼働させる。

+0

何百もの依存性!=何百ものコンピュータ* –

+0

私はまだすべての依存関係に共有SQL Serverの永続メモリコストがあると思います。多分それらの多くを開くのは良い考えではないでしょう。 – usr

+0

http://rusanu.com/2006/06/17/the-mysterious-notification/をお読みください。私は、QNがいかにして私に語っているのかを正確に知っています。まず、同じクエリテンプレートのインスタンスをさまざまなパラメータで開きます。 OKです。 –

関連する問題