2

我有一个 ASP.NET MVC 2 Web 应用程序(.NET 4,C#),用户可以在其中搜索位置

该页面是使用自动完成框实现的,类似于许多网站。(谷歌、YouTube 等)

现在,对服务器的 AJAX 调用导致对数据库的存储过程调用。(虽然高效,但可能会导致慢速打字机进行大量往返)。

我想知道如何创建一个策略来缓存最后 100 次搜索的结果?

我不能使用 OutputCache,因为调用是通过客户端的 AJAX 进行的。我需要缓存存储过程的输出(查询文本的匹配位置列表)。

换句话说,很多人会搜索“纽约”或“旧金山”,而这些数据只能通过手动管理员更改来更改(例如,我们可以手动使缓存失效)。

那么,我怎样才能缓存最后 100 次搜索呢?我希望有一个类似 FIFO 的功能,如果缓存已经有 100 次搜索,最旧的搜索就会被丢弃,所有的东西都会被下移。

我希望代码是这样的:

public ICollection<MatchedLocation> FindLocations(string queryText)
{
    // Check last 100 searches.. How?
    string cacheKey = queryText;
    var matchedLocations = cachedPersistence.Get(cacheKey);

    if (matchedLocations == null)
    {
        // Call db
        matchedLocations = dbPersistence.GetLocations(queryText);

        // Add to cache
        cachedPersistence.Add(cacheKey, matchedLocations);
    }
    else
    {
        // Found in Cache! Awesome!
        return matchedLocations;
    }
}

我认为显而易见的选择是 .NET Queue

但是我以前从未使用过,所以有什么建议吗?我将如何为 get/set 实现并发,我需要使用完全锁定的单例吗?有没有人为此使用队列?我们还有哪些其他选择?我几乎需要一个自定义队列来限制堆栈中的项目数量。

谢谢你的帮助。

4

1 回答 1

1

如果您只想缓存 100,您可以使用带有最后使用时间的相应字典的字典。而且您可以使用读写器锁而不是允许多个读取器的完整锁。

使用下面的代码可能有两个线程可以输入EnterWriteLock相同的值。惩罚将是两次 db 调用,这可能不是问题。TryGetValue如有必要,您可以通过再次锁定(双重锁定)来避免这种情况。

class Cache
{
    static readonly Dictionary<string, ICollection<MatchedLocation>> _cache = new Dictionary<string, ICollection<MatchedLocation>>(100);
    static readonly Dictionary<string,DateTime> _cacheTimes = new Dictionary<string, DateTime>(100);
    static readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();

    public ICollection<MatchedLocation> FindLocations(string queryText)
    {
        _lock.EnterUpgradeableReadLock();
        try
        {
            ICollection<MatchedLocation> result;
            if (_cache.TryGetValue(queryText, out result))
            {
                return result;
            }
            else
            {
                _lock.EnterWriteLock();
                try
                {
                    // expire cache items
                    if( _cache.Count > 100)
                    {
                        // could be more efficient http://code.google.com/p/morelinq/ - MinBy
                        string key = _cacheTimes.OrderBy(item => item.Value).First().Key;
                        _cacheTimes.Remove(key);
                        _cache.Remove(key);
                    }
                    // add new item
                    result = dbPersistence.GetLocations(queryText);
                    _cache[queryText] = result;
                    _cacheTimes[queryText] = DateTime.UtcNow;                        
                }
                finally
                {
                    _lock.ExitWriteLock();
                }
                return result;
            }
        }
        finally
        {
            _lock.ExitUpgradeableReadLock();
        }
    }
}
于 2010-10-20T11:15:43.827 回答