【发布时间】:2013-04-24 14:09:09
【问题描述】:
是否有任何多线程缓存机制可以在 SQL CLR 函数中工作而无需将程序集注册为“不安全”?
正如in this post 所述,简单地使用lock 语句将在安全程序集上引发异常:
System.Security.HostProtectionException:
Attempted to perform an operation that was forbidden by the CLR host.
The protected resources (only available with full trust) were: All
The demanded resources were: Synchronization, ExternalThreading
我希望对我的函数的任何调用都以线程安全的方式使用相同的内部缓存,以便许多操作可以同时进行缓存读取和写入。本质上-我需要一个ConcurrentDictionary,它将在SQLCLR“安全”程序集中工作。不幸的是,使用ConcurrentDictionary 本身会产生与上述相同的异常。
SQLCLR 或 SQL Server 是否有内置的东西来处理这个问题?还是我误解了SQLCLR的线程模型?
我已尽我所能阅读有关 SQLCLR 安全限制的内容。特别是,以下文章可能有助于理解我在说什么:
- SQL Server CLR Integration Part 1: Security
- Deploy/Use assemblies which require Unsafe/External Access with CLR and T-SQL
此代码最终将成为分发给其他人的库的一部分,因此我真的不想被要求以“不安全”的方式运行它。
我正在考虑的一个选项(由 Spender 在下面的 cmets 中提出)是从 SQLCLR 代码中访问 tempdb 并将其用作缓存。 但我不太确定该怎么做。我也不确定它是否会像内存缓存一样具有性能。 请参阅下面的更新。
我对任何其他可用的替代品感兴趣。谢谢。
示例
以下代码使用静态并发字典作为缓存,并通过 SQL CLR 用户定义函数访问该缓存。所有对函数的调用都将使用相同的缓存。但除非程序集被注册为“不安全”,否则这将不起作用。
public class UserDefinedFunctions
{
private static readonly ConcurrentDictionary<string,string> Cache =
new ConcurrentDictionary<string, string>();
[SqlFunction]
public static SqlString GetFromCache(string key)
{
string value;
if (Cache.TryGetValue(key, out value))
return new SqlString(value);
return SqlString.Null;
}
[SqlProcedure]
public static void AddToCache(string key, string value)
{
Cache.TryAdd(key, value);
}
}
它们位于名为 SqlClrTest 的程序集中,并使用以下 SQL 包装器:
CREATE FUNCTION [dbo].[GetFromCache](@key nvarchar(4000))
RETURNS nvarchar(4000) WITH EXECUTE AS CALLER
AS EXTERNAL NAME [SqlClrTest].[SqlClrTest.UserDefinedFunctions].[GetFromCache]
GO
CREATE PROCEDURE [dbo].[AddToCache](@key nvarchar(4000), @value nvarchar(4000))
WITH EXECUTE AS CALLER
AS EXTERNAL NAME [SqlClrTest].[SqlClrTest.UserDefinedFunctions].[AddToCache]
GO
然后它们在数据库中的使用是这样的:
EXEC dbo.AddToCache 'foo', 'bar'
SELECT dbo.GetFromCache('foo')
更新
我想出了如何使用Context Connection 从 SQLCLR 访问数据库。代码in this Gist 显示了ConcurrentDictionary 方法和tempdb 方法。然后我进行了一些测试,从客户统计数据(平均 10 次试验)测得以下结果:
Concurrent Dictionary Cache
10,000 Writes: 363ms
10,000 Reads : 81ms
TempDB Cache
10,000 Writes: 3546ms
10,000 Reads : 1199ms
这样就放弃了使用 tempdb 表的想法。真的没有别的我可以尝试的吗?
【问题讨论】:
-
不完全清楚你在问她什么 9 虽然这可能只是我 :])。什么代码抛出了这个异常,你想做什么?
-
@Killercam - 添加了代码示例。
-
也许更好的缓存位置是在数据库本身作为表?届时您将免费获得同步。
-
@spender - 我想你可能正在做点什么。我可以在 SQLCLR 函数中管理我自己的 tempdb 数据吗?知道它是否会像内存缓存一样执行吗?如果您可以通过代码示例显示答案,那就太棒了。
-
@StrayCatDBA 然后我得到一个缓存未命中,下一次加载需要更长的时间,同时操作再次执行,但它会再次缓存,直到下一次卸载。当您要执行 1000 次某事时,请执行一次并缓存它。如果你回来的时候缓存已经不见了,所以你必须再做一次,那仍然是你可以使用缓存的 999 次。大多数缓存都遵循这一原则。
标签: c# sql-server multithreading caching sqlclr