每个用户使用SqlCacheDependency是个好主意吗?

Dan*_*ite 0 sql-server ado.net

我正在考虑为我们的应用程序服务器上的每个用户缓存权限.SqlCacheDependency为每个用户使用a是个好主意吗?

查询看起来像这样

SELECT PermissionId, PermissionName From Permissions Where UserId = @UserId
Run Code Online (Sandbox Code Playgroud)

这样我知道是否有任何记录改变,以清除该用户的缓存.

Rem*_*anu 5

如果您阅读查询通知的工作原理,您将会看到为什么使用单个查询模板创建许多依赖项请求是一种很好的做法.对于一个Web应用程序,你使用SqlCacheDependency和不使用的事实暗示SqlDependency,你打算做什么应该没问题.如果你使用Linq2Sql,你也可以尝试LinqToCache:

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

对于胖客户端应用程序,它可能不会.不是因为查询本身,而是因为SqlDependency一般来说连接大量客户端会有问题(它会阻止每个应用域连接的工作线程):

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

        }

    }
}
Run Code Online (Sandbox Code Playgroud)

设置50k订阅(大约需要5分钟)后,这里是单个插入的统计信息:

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.
Run Code Online (Sandbox Code Playgroud)

插入1000行大约需要7秒,其中包括触发数百个通知.CPU利用率约为11%.这一切都在我的T420s ThinkPad上.

set nocount on;
go

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

commit
go
Run Code Online (Sandbox Code Playgroud)