2012-04-24 93 views
0

我在爲應用服務器上的每個用戶緩存權限。爲每個用戶使用SqlCacheDependency是一個好主意嗎?爲每個用戶使用SqlCacheDependency是個好主意嗎?

查詢應該是這樣的

SELECT PermissionId, PermissionName From Permissions Where UserId = @UserId 

這樣,我知道,如果任何這些記錄更改,然後清除我的緩存爲該用戶。

回答

5

如果您閱讀how Query Notifications work,您會看到爲什麼使用單個查詢模板創建多個依賴項請求是一種很好的做法。對於使用SqlCacheDependency而不是SqlDependency這一事實暗示的網絡應用程序,您打算執行的操作應該是正常的。如果你使用LINQ2SQL你也可以嘗試LinqToCache

var queryUsers = from u in repository.Users 
     where u.UserId = currentUserId 
     select u; 
var user= queryUsers .AsCached("Users:" + currentUserId.ToString()); 

對於胖客戶端應用程序將不會確定。由於每本身的查詢,但因爲一般SqlDependency是具有大量連接的客戶端(它的塊a worker thread每個應用正在訪問的連接)有問題的不:

的SqlDependency被設計在ASP使用。 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的單個插入物的統計:

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. 

插入1000行需要大約7秒,這包括髮射數百個通知。 CPU利用率約爲11%。所有這些都在我的T420 ThinkPad上。

set nocount on; 
go 

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

commit 
go 
+0

即使對於斷開連接的用戶,我恐怕仍然會緩存所有的用戶數據可能太多了,還有其他選擇嗎? – 2012-04-24 17:17:49

+0

SqlCacheDependency具有緩存逐出策略。 – 2012-04-24 17:19:53

+0

這與我的測試顯示的性能基本相同。您的依賴創建是多線程的,因此速度更快。可能是4倍?對於1000插入7s *是可怕的*。 11%cpu = 1核心,100%。 – usr 2012-04-25 10:55:19

0

的文件說:

的SqlDependency被設計在ASP.NET或中間層 服務使用不存在具有 依賴對數據庫活動服務器的數量相對較少。它不是爲在客戶端應用程序中使用 而設計的,其中數百或數千個客戶端計算機將爲單個 數據庫服務器設置SqlDependency對象。

它告訴我們不要打開數以千計的緩存依賴關係。這很可能會導致SQL Server上的資源問題。

有幾個備選方案:

  1. 有每個表的依賴
  2. 有每桌100間的相關性,一個是行的每一個個百分點。對於SQL Server,這應該是可以接受的數字,但只需要使緩存的1%無效。
  3. 通過觸發器將所有更改行的標識輸出到日誌記錄表中。在該表上創建一個依賴關係並讀取這些ID。這會告訴你確切的哪些行已經改變。

爲了找出如果是的SqlDependency適合大衆使用我做了一個標杆:

 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); 
       } 
      } 
     } 

你可以看到通過控制檯創建滾動依賴量。它變得非常快。我沒有做正式的測量,因爲沒有必要證明這一點。

此外,簡單插入表中的執行計劃顯示99%的成本與維護50k依賴關係相關。

結論:根本不適用於生產用途。 30分鐘後,我創建了55k個依賴項。機器一直處於100%CPU。

+0

數百個依賴項!=數百個*計算機* – 2012-04-24 17:23:09

+0

我仍然認爲每個依賴項對共享SQL Server都有永久的內存開銷。打開其中許多可能不是一個好主意。 – usr 2012-04-24 17:25:08

+0

閱讀http://rusanu.com/2006/06/17/the-mysterious-notification/。我確切知道QN是如何反應的,我告訴你:繼續,用不同的參數打開同一個查詢模板的許多實例。好的。 – 2012-04-24 17:29:22

相關問題