Кэширование памяти, блокировка и условия гонки

StackOverflow https://stackoverflow.com/questions/1608902

  •  05-07-2019
  •  | 
  •  

Вопрос

Мы пытаемся обновить объекты memcached при записи в базу данных, чтобы избежать необходимости считывать их из базы данных после вставок / обновлений.

Для нашего объекта записи на форуме у нас есть поле viewCount, содержащее количество просмотров записи.

Мы опасаемся, что вводим условие гонки, обновляя объект memcached, поскольку одно и то же сообщение может быть просмотрено в одно и то же время на другом сервере фермы.

Есть какие-нибудь идеи, как справиться с такого рода проблемами - казалось бы, необходима какая-то блокировка, но как сделать это надежно на всех серверах фермы?

Это было полезно?

Решение

Если вы имеете дело с данными, это не обязательно потребность чтобы обновляться в режиме реального времени, и для меня количество просмотров является одним из них, вы могли бы добавить поле expires к объектам, которые хранятся в memcache.

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

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

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

Редактировать:

Memcache не дает гарантии, что при получении и настройке с разных серверов ваши данные не будут сбиты с толку.

Из документов memcache:

  • Последовательность команд не является атомарной.Если вы выдаете "get" для элемента, работаете с данными, а затем хотите "установить" их обратно в memcached, вы не гарантированно будете единственным процессом, работающим с этим значением.Параллельно вы могли бы в конечном итоге перезаписать значение, установленное чем-то другим.

Условия гонки и устаревшие данные

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

Допустим, вы кэшируете последние пять комментариев для отображения на боковой панели вашего приложения.Вы решаете, что данные необходимо обновлять только один раз в минуту.Однако вы забываете помнить, что отображение боковой панели отображается 50 раз в секунду!Таким образом, как только проходит 60 секунд и срок действия кэша истекает, внезапно более 10 процессов запускают один и тот же SQL-запрос для повторного заполнения этого кэша.Каждый раз, когда срок действия кэша истекает, возникает внезапный всплеск SQL-трафика.

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

Следует помнить о возможных проблемах при заполнении или повторном заполнении нашего кэша.Помните, что процесс проверки memcached, извлечения SQL и сохранения в memcached вообще не является атомарным!

Другие советы

Я думаю - может ли решение заключаться в том, чтобы хранить viewcount отдельно от объекта Post, а затем делать для него INCR. Конечно, это потребует чтения 2 отдельных значений из memcached при отображении информации.

Мы столкнулись с этим в нашей системе.Мы могли бы получить так

  • Если значение не задано, оно устанавливается с помощью флага ('g') и [8] второго TTL и возвращает false, чтобы вызывающая функция сгенерировала его.
  • Если значение не помечено (!== 'g'), то отмените сериализацию и верните его.
  • Если значение помечено (==='g'), подождите 1 секунду и повторите попытку, пока оно не будет помечено.В конечном итоге он будет установлен другим процессом или истечет по TTL.

Когда мы это внедрили, нагрузка на нашу базу данных снизилась в 100 раз.

function get($key) {
  $value=$m->get($key);
  if ($value===false) $m->set($key, 'g', $ttl=8);
  else while ($value==='g') {
    sleep(1);
    $value=$m->get($key);
  }
  return $value;
}

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top