2010-10-20 2 views
2

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

Страница выполнена с автоматической коробкой, аналогичной многим веб-сайтам. (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 Queue?

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

Благодарим за помощь.

ответ

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(); 
     } 
    } 
} 
+0

Интересный подход. Я попробую это, запустите некоторые тесты и вернусь к вам. Спасибо – RPM1984

+0

Возможно, вы захотите добавить время истечения срока действия, чтобы периодически обновлять данные БД. Другим подходом является использование объекта Cache в ASP.Net и сохранение счетчика и использование триггеров истечения. Я думаю, все зависит от того, сколько вы хотите контролировать по кешу и сколько данных вам нужно кэшировать, чтобы заставить решение летать. Интересно прочитать на http://social.msdn.microsoft.com/Forums/en-US/velocity/thread/2ea7a5bc-987b-4395-ab4f-f03890a3c0a0, который также говорит о скорости. –

+0

Также проверьте этот небольшой проект - http://code.msdn.microsoft.com/Release/ProjectReleases.aspx?ProjectName=SimpleCache&ReleaseId=1171 –

Смежные вопросы