2016-11-24 3 views
0

Наш туристический сайт. По конкретным критериям поиска (Location, checkIn, CheckOut, Adults, Child), я показываю результат в пользовательском интерфейсе, одновременно сохраняя результат на сервере redis для кеширования, поскольку значения не так часто меняются.Обработка параллелизма

ie Ключ будет критерием поиска и значением будет List.Now, когда приходит следующий запрос, он сначала ищет redis, и если для запрошенного ключа значение есть, результат будет отображаться из Redis другой мудрый новый поиск и результаты будут сохранены в кеше.

Вопрос: Если в Redis нет результатов, и миллион пользователей применяют поиск одинаковых критериев поиска одновременно. Как можно справиться с этой ситуацией? Поскольку все поисковые запросы поступают одновременно, и кэш не имеет результата в то время. Мы должны помнить о производительности.

Справка будет принята с благодарностью.

ответ

0

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

Возможно, у вас есть один decorator, чтобы убедиться, что одновременно выполняется только один тип запроса. Вы можете представить запрос с объектом, который находится в потокобезопасной коллекции, и блокировать его во время выполнения запроса.

Поэтому, учитывая интерфейс, выражающий путь вы запрос источника данных:

interface IQueryExecuter<TQuery, TResult> 
{ 
    TResult Execute(TQuery query); 
} 

Вы можете использовать Потокобезопасную объект декоратора, который кэширует результаты запросов и, в случае, если результат запроса не кэшируется, только один нить выполняет запрос к источнику данных:

Неиспользованный код!

class QueryThrottler<TQuery, TResult> : IQueryExecuter<TQuery, TResult> 
{ 
    // do not lock on external objects 
    class QueryObject 
    { 
     public TQuery Query { get; set; } 
    } 

    readonly IQueryExecuter<TQuery, TResult> _inner; 
    readonly ConcurrentDictionary<TQuery, QueryObject> _queries; 

    public QueryThrottler(IQueryExecuter<TQuery, TResult> inner) 
    { 
     _queries = new ConcurrentDictionary<TQuery, QueryObject>(); 
     _inner = inner; 
    } 

    public TResult Execute(TQuery query) 
    { 
     // if it is on cache return the result 
     TResult result; 
     if (!IsCached(query, out result)) 
     { 
      // otherwise lock other threads 
      // on the same query 
      var queryObject = _queries.GetOrAdd(query, k => new QueryObject() { Query = k }); 
      lock (queryObject) 
      { 
       // double check it is not cached already 
       if (!IsCached(query, out result)) 
       { 
        result = _inner.Execute(queryObject.Query); 
        PopulateCache(query, result); 
       } 
      } 
     } 
     return result; 
    } 

    private void PopulateCache(TQuery query, TResult result) 
    { 
     // Save the result in Redis using TQuery as key 
    } 

    private bool IsCached(TQuery query, out TResult result) 
    { 
     // go to redis and check if the query is cached using TQuery as key 
     // if exists, set the result out parameter and return true 
     // otherwise, return false 
     result = default(TResult); 
     return false; 
    } 
} 

Этот код основан на TQuery, имеющих соответствующие реализаций GetHashCode и Equals.

Декорированный объект (inner в конструкторе) является объектом, который будет выполнять фактический запрос к источнику данных.

Если у вас есть много серверов, и хотите, чтобы убедиться, что только один поток из всех серверов сделать фактический запрос к источнику данных, а не lock, вы можете использовать распределенную блокировку как LockTake/LockRelease от StackExchange.Redis.

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