Yak*_*MIK 6 c# sql asp.net logging caching
想象一下这种情况:你有一个WCF网络服务,每天可以达到一百万次.每个匹配包含一个"帐户ID"标识符.WCF服务托管在分布式ASP.NET群集中,并且您没有对服务器的远程桌面访问权限.
您的目标是将每个帐户ID的"每小时点击次数"保存到SQL数据库中.结果应如下所示:
[Time], [AccountID], [NumberOfHits]
1 PM, Account ID (Bob), 10 hits
2 PM, Account ID (Bob), 10 hits
1 PM, Account ID (Jane), 5 hits
Run Code Online (Sandbox Code Playgroud)
问题是:如果不在每次点击时连接到SQL服务器数据库,如何才能做到这一点?
这是我想到的一个解决方案:将临时结果存储在System.Web.Cache对象中,监听其过期,并在Cache Expiration上,在Cache过期时将所有累积数据写入数据库.
有关更好方法的任何想法?
事实上,延迟更新是关键,并且您的本地缓存方法走在正确的道路上。只要您不需要在每次访问时显示上次更新计数,解决方案很简单:更新 account_id->count 的本地缓存并定期扫描此缓存,将计数替换为 0 并添加数据库中总数的计数。如果ASP.Net 进程丢失,并且显示的点击计数不准确(ASP 场中的节点 1 返回其纬度计数,节点 2 返回其自己的本地计数,与节点 1 不同),则可能会丢失一些访问计数。
如果您必须准确显示每个返回结果的计数(无论这是页面返回还是服务返回,都无关紧要),那么它很快就会变得很麻烦。像 Memcache 这样的集中式缓存可以帮助创建解决方案,但它并不是微不足道的。
这是我保留本地缓存的方法:
class HitCountCache
{
class Counter
{
public unsigned int count {get;set}
public accountid {get;set}
};
private Dictionary<accountType, Counter> _counts = new Dictionary<...>();
private Object _lock= new Object();
// invoke this on every call
//
void IncrementAccountId (accountId)
{
Counter count;
lock(_lock)
{
if (_counts.TryGetValue (accountId, out count))
{
++count.count;
}
else
{
_counts.Add (accountId,
new Counter {accountId = accountId; count=0});
}
}
}
// Schedule this to be invoked every X minutes
//
void Save (SqlConnection conn)
{
Counter[] counts;
// Snap the counts, under lock
//
lock(_lock)
{
counts = _counts.ToArray();
_counts.Clear();
}
// Lock is released, can do DB work
//
foreach(Counter c in counts)
{
SqlCommand cmd = new SqlCommand(
@"Update table set count+=@count where accountId=@accountId",
conn);
cmd.Parameters.AddWithValue("@count", c.count);
cmd.Parameters.AddWithValue("@accountId", accountId);
cmd.ExecuteNoQuery();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个骨架,可以改进,如果需要的话还可以返回当前的总计数,至少是本地节点已知的总计数。