Как регистрировать изменения в базе данных каждые 5 минут в приложении с высокими транзакциями с C # и SQL? - PullRequest
6 голосов
/ 06 мая 2011

Представьте себе такой сценарий: у вас есть веб-служба WCF, которая работает миллион раз в день.Каждый удар содержит идентификатор «Идентификатор учетной записи».Служба WCF размещена в распределенном кластере ASP.NET, и у вас нет доступа к удаленному рабочему столу к серверу.

Ваша цель - сохранить «количество обращений в час» для каждого идентификатора учетной записи в 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

Вопрос в следующем: как это можно сделать без подключения к базе данных сервера SQL при каждом обращении?

Вот одно решение, о котором я подумал: Storeвременные результаты в объекте System.Web.Cache, прослушивании его срока действия и в Cache Expiration записывают все накопленные данные в базу данных по истечении срока действия Cache.

Есть мысли о лучшем подходе?

Ответы [ 6 ]

3 голосов
/ 06 мая 2011

Задержанное обновление - это ключ, и вы на правильном пути с вашим подходом локального кэша.Если у вас нет необходимости отображать количество последних обновлений при каждом посещении, решение простое: обновите локальный кэш account_id-> count и периодически просматривайте этот кеш, замените счет на 0 и добавьтеколичество к итогу в базе данных.Вы можете потерять некоторое количество посещений, если ваш процесс ASP.Net потерян, и ваш счетчик посещений не точен (Узел 1 в ферме ASP возвращает свой счетчик лотов, Узел 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();
      }
   } 
}

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

0 голосов
/ 07 мая 2011

Если ваша цель - производительность на сайте, то, как сказал другой автор, просто используйте огонь и забудьте.Это может быть WebService, в который вы отправляете данные, или вы можете создать службу, работающую в фоновом режиме и прослушивающую очередь MSMQ.Я могу дать вам больше примеров этого, если интересно.Если вам необходимо синхронизировать веб-сайт или инструмент администратора с базой данных, вы можете сохранить значения в высокопроизводительном кэше, например memcache, одновременно с обновлением базы данных.

Если вы хотите запустить пакет из100 запросов к БД в одном запросе затем создают отдельный сервис, опять же с MSMQ, который опрашивает очередь и ожидает> 100 сообщений в очереди.Как только он обнаруживает, что есть 100 сообщений, он открывает транзакцию с MSTDC и считывает все сообщения в память и объединяет их для выполнения в одном запросе.MSMQ длительный, что означает, что если сервер отключается или служба отключается при отправке сообщения, она все равно будет доставлена, когда служба подключится к сети.Сообщения будут удалены из очереди только после завершения запроса.Если в результате запроса произошел сбой или что-то случилось со службой, сообщения все еще будут в очереди на обработку, вы ничего не потеряете.MSTDC просто помогает вам сохранить все в одной транзакции, поэтому, если одна часть процесса завершается неудачно, все откатывается.

Если вы не можете создать службу Windows для этого, просто сделайте WebService, который вы вызываете.Вы по-прежнему отправляете сообщение MSMQ каждый раз, когда загружается страница, и говорите, что один раз каждые 10 раз страница загружается, вы запускаете веб-службу для обработки всех сообщений в очереди.Единственная проблема, с которой вы можете столкнуться, - это установить службу MSMQ, как бы вы ни размещали ее, и установить что-то подобное для вас, если вы об этом попросите.

0 голосов
/ 06 мая 2011

Любая причина, почему они не используют app fabric или тому подобное?

Можете ли вы войти в реализацию сервиса? Если это так, то путь к достижению этого состоит в том, чтобы реализация службы запускала вызов регистрации в стиле «запусти и забывай» для любой другой службы, которую вы настроили для регистрации этого щенка. Не должен задерживать выполнение, должен выдерживать сбои приложения и тому подобное и не потребует углубления в угол SQL.

Честно говоря, я бы не взял эту работу, если бы не смог разобраться во всем, большинство других подходов обречены на провал.

0 голосов
/ 06 мая 2011

Предложенный объект в памяти является самым быстрым, но рискует потерять данные в случае сбоя приложения или сервера. Чтобы уменьшить потерю данных, вы можете лениво записывать кэшированные данные на диск. Затем периодически считывайте данные из файла кэша и записывайте агрегированную информацию на ваш сервер SQL.

0 голосов
/ 06 мая 2011

Вы спросили: «Как вы можете сделать это без подключения к базе данных сервера SQL при каждом обращении?»

Использовать пул соединений. При пуле соединений несколько подключений к SQL-серверу открываются ОДИН РАЗ, а затем они повторно используются для последующих вызовов. Таким образом, при каждом обращении к базе данных вам не нужно подключаться к SQL-серверу, поскольку вы уже подключены и можете повторно использовать существующее подключение для доступа к базе данных.

Обратите внимание, что пул соединений по умолчанию используется с поставщиком SQL ado.net, поэтому вы можете использовать его, даже не зная об этом.

0 голосов
/ 06 мая 2011

Один из вариантов - выгрузить соответствующую информацию в журналы вашего сервера (API журналирования уже оптимизированы для работы с большими объемами транзакций) и собрать их с помощью отдельного процесса.

...