Стратегия кеширования предыдущих поисков (ASP.NET) - PullRequest
2 голосов
/ 20 октября 2010

У меня есть веб-приложение ASP.NET MVC 2 (.NET 4, C #), , где пользователь может искать местоположения .

Страница реализована с помощью окна автозаполнения, аналогичного многим веб-сайтам. (Google, 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 Очередь ?

Но я никогда не использовал это раньше, так что совет? Как мне реализовать параллелизм для get / set? Нужно ли мне использовать полностью заблокированный Singleton? Кто-нибудь использовал Очередь для этой цели? Какие еще варианты у нас есть? Мне почти понадобится собственная очередь, чтобы ограничить количество элементов в стеке.

Спасибо за вашу помощь.

1 Ответ

1 голос
/ 20 октября 2010

Если вы хотите кэшировать только 100, вы можете использовать словарь с соответствующим словарем с последним использованным временем.И вы можете использовать блокировку записи для чтения вместо полной блокировки, которая позволяет использовать несколько читателей.

С кодом ниже потенциально два потока могут ввести EnterWriteLock для одного и того же значения.Наказанием будут два вызова в дБ, которые могут не быть проблемой.Вы можете избежать этого, сделав еще один 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();
        }
    }
}
...