Килограммы кэша больше ни к чему

Обновлено: 07.07.2024

Проблемы с производительностью приложений и служб в различных версиях Windows XP, Windows Vista, Windows Server 2003, Windows Server 2008 и Windows Server 2008 R2. Кроме того, вы заметите следующие признаки:

В доступной памяти почти не будет памяти.

Системный кэш файлов занимает большую часть физического ОЗУ.

К жесткому диску постоянно и большой объем запросов на чтение в кэш.

Причина

Для управления памятью в операционных системах Microsoft Windows используется алгоритм, основанный на запросе. Если какой-либо процесс запрашивает большой объем памяти, увеличивается размер рабочего набора (количество страниц памяти в физическом ОЗУ). Если эти запросы непрерывно и не установлены, рабочий набор процесса будет обрабатывать весь физический ОЗУ. В этом случае рабочие наборы для всех остальных процессов высвеяются на жестком диске. Это снижает производительность приложений и служб, так как страницы памяти непрерывно пишутся на жестком диске и читают их с жесткого диска.

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

В 32-битных версиях операционной системы Microsoft Windows, более ранних чем Windows Vista, рабочие наборы системного кэша имеют ограничение памяти менее 1 гигабайта (ГБ). Ограничение виртуального диапазона адресов предотвращает исчерпание физического ОЗУ для рабочих наборов системного кэша файлов.

В 32-битных версиях операционной системы Windows Vista ресурсы ядра выделяются динамически. Рабочий набор системного кэша файлов увеличивается для использования виртуального диапазона адресов режима ядра за счет других ресурсов ядра. Ограничение для этого диапазона памяти меньше 2 ГБ. Если на компьютере более 2 ГБ физической ОЗУ, кэш не может известить весь физический ОЗУ. Тем не менее, кэш может привести к исчерпанию виртуального адресного пространства в ядрах. Это может привести к сбоям выделения для других компонентов ядра.

В 64-битных версиях операционной системы Windows размер виртуального диапазона адресов обычно превышает физический ОЗУ. В этом случае рабочий набор для системного кэша может увеличиться для увеличения нагрузки на большую часть физического ОЗУ.

Решение

Алгоритмы управления памятью в операционных системах Windows 7 и Windows Server 2008 R2 были обновлены для устранения многих проблем кэширования файлов, найденных в более ранних версиях Windows. Существует лишь несколько уникальных ситуаций, в которых необходимо реализовать эту службу на компьютерах с Windows 7 или Windows Server 2008 R2.

Как определить, влияет ли это на систему

Чтобы определить, влияет ли эта проблема на вашу систему, установите средство SysInternals RamMap. Вы можете получить это средство на следующем веб-сайте Windows Sysinternals:

http://technet.microsoft.com/en-us/sysinternals/ff700229
При запуске средства выберите параметр "Использовать подсчеты". Отображается несколько столбцов, которые отображают текущий шаблон использования памяти. Щелкните столбец "Активные", чтобы отсортировать данные по количеству использованных bytes, и заметьте верхнее использование непосредственно под итогом.

Если в качестве наиболее часто используемого файла используется метафил и используется большая часть доступной памяти, у вас наблюдается проблема с кэшом системных файлов, описанная в разделе "Симптомы". Это можно проверить с помощью монитора производительности, чтобы отслеживать счетчик Resident Bytes в памяти\system cache, и со временем кэш постоянно растет.

Рисунок 1. Пример вывода RamMap, в котором проблема наблюдается на компьютере.

заме желтая текст


Рисунок 2. Пример вывода RamMap, в котором проблема не наблюдается на компьютере.

заме желтая текст


Если в счетчике Resident Bytes между памятью и системой в мониторе производительности показана восходящая тенденция, проблема наблюдается на компьютере( на рисунке 3).

Рисунок 3. Пример выходного монитора производительности, в котором компьютер со временем испытывает проблему.

Требования к перезапуску

Вам не нужно перезапускать компьютер при установке, установке или использовании этой службы.

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

Убедитесь в том, что выходные данные клиента, данные perfmon или poolmon подтверждают, что системный кэш файлов расходует большую часть физического ОЗУ, как описано выше.

Чтобы получить службу Windows Dynamic Cache, скачайте ее здесь.

Некоторые параметры реестра Динамического кэша параметров находятся в этой области:

Файловые серверы, возможно, вы захотите попробовать 1 ГБ.
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DynCache\Parameters]
"MaxSystemCacheMBytes"=dword:00000400
"MinSystemCacheMBytes"=dword:00000064
"SampleIntervalSecs"=dword:0000003c
"CacheUpdateThresholdMBytes"=dword:00000064

Exchange 2007 может потребоваться попробовать 500 МБ:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DynCache\Parameters]
"MaxSystemCacheMBytes"=dword:000001F4
"MinSystemCacheMBytes"=dword:00000064
"SampleIntervalSecs"=dword:0000003c
"CacheUpdateThresholdMBytes"=dword:00000064

SQL 2005 и более высоких версий в прошлом при SQL EE использовали 2 ГБ:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\DynCache\Parameters]
"MaxSystemCacheMBytes"=dword:000007D0
"MinSystemCacheMBytes"=dword:00000064
"SampleIntervalSecs"=dword:0000003c
"CacheUpdateThresholdMBytes"=dword:00000064

Дополнительная информация

Чтобы обойти эту проблему, используйте функции GetSystemFileCacheSize API и SetSystemFileCacheSize API, чтобы установить максимальное или минимальное значение размера для рабочих наборов системного кэша файлов. Эти функции — единственный поддерживаемый способ ограничить потребление физической памяти в кэше системных файлов.

Служба Microsoft Windows Dynamic Cache — это образец службы, демонстрирующая стратегию использования этих API для минимизации влияния этой проблемы.

Установка и использование службы Microsoft Dynamic Cache Service не приводят к исключению поддержки Microsoft Windows. Эта служба и ее исходный код являются примером использования поддерживаемых Майкрософт API для уменьшения роста кэша файловой системы.

Вы можете получить службу и исходный код на следующем веб-сайте Майкрософт:

Дополнительные ресурсы

Чтение глав 9 (управление памятью) и 10 (диспетчер кэша) внутренних приложений Windows, выпуска 5-го выпуска.

976618 Вы испытываете проблемы с производительностью приложений и служб, когда системный кэш файлов занимает большую часть физической оперативной памяти 918483, что позволяет уменьшить запас запаса памяти в 64-битной версии SQL Server 895932 Что нужно учесть перед тем, как включить режим кэша системы в Windows XP 232271: оптимизация Windows NT-сервера с помощью записи блога о кэше Диспетчера кэша в

Статус

Корпорация Майкрософт подтверждает наличие этой проблемы в своих продуктах, которые перечислены в разделе "Применяется к".

Кэш и всё, что с ним связано

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

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

Кэш-память

Стриминговые сервисы

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

Файлы пользователя

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

Похожим образом обстоят дела с фотографиями, особенно у людей, неспособных и дня прожить без селфи. В редких случаях всё внутреннее хранилище забивают фильмами или сериалами для просмотра по дороге на работу или учебу. Здесь нет ничего сложного — контролируйте личные файлы, удаляя ненужные или используя для их хранения облачные хранилища. То же самое актуально для приложений.

Удаление приложений

Корзина в файловых менеджерах

Медиафайлы мессенджеров

  1. Зайдите в корневое меню с файлами смартфона (любой файловый менеджер в помощь).
  2. Найдите папку WhatsApp и перейдите в директорию Media.
  3. Удалите все ненужные файлы. Этот приём распространяется и на другие мессенджеры, необходимо только поискать их папку в корневом хранилище.

Подробная инструкция по очистке памяти от файлов WhatsApp доступна по этой ссылке.

WhatsApp забивает память

Скрытая папка Thumbnails

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

Заключение

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

(16 оценок, среднее: 4,13 из 5)

Фото: магазин Apple Store в Шанхае, Китай (Фото: пользователя Roger Luo с сайта flickr.com)

У всех компаний есть на счетах остаток средств, необходимый для функционирования бизнеса. Если их объем превышает сумму краткосрочных и долгосрочных кредитов, то такую ситуацию называют отрицательным чистым долгом. А сами «лишние» деньги — свободными, ведь их можно в любой момент инвестировать в развитие, отдать на дивиденды или потратить как-то еще.

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

Среди причин такого поведения — отсутствие приемлемых объектов для инвестиций или же создание финансовой подушки безопасности. Большой запас наличности или же других высоколиквидных активов позволяет спокойно вести бизнес и чувствовать себя уверенно в экстренной ситуации.

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

В глазах инвесторов такие компании выглядят защитным активом на фоне крайней нестабильности рынков — они считаются самыми устойчивыми и здоровыми. Самые яркие примеры таких компаний мы собрали в этом тексте.

Самые толстые подушки: Apple, Alphabet, Microsoft

Больше всего держать резервы в наличных любят технологические компании: по мнению эксперта Everscore Ли Горовица, это связано с тем, что те «стремятся держать порох сухим, чтобы пережить циклические спады и извлечь прибыль из отскоков рынка».

Своеобразным чемпионом по объему кеша на счетах можно назвать Apple: по данным The Economist, компании накопила $207 млрд наличными, что превышает годовые бюджеты отдельных стран. В то же время кратко- и долгосрочные обязательства Apple составляют $108 млрд.

Выделяются и другие компании технологического сектора. Владеющая Google компания Alphabet аккумулировала $120 млрд наличными при практически отсутствующих долгах. Кеш составляет 15% от рыночной капитализации компании, оценивающийся в $788 млрд. Кроме того, сильными резервами в наличных и краткосрочных облигациях могут похвастаться Microsoft ($134 млрд), Facebook ($55 млрд), Cisco ($33 млрд).

В автопроме мощный запаса кеша у Ford ($62,8 млрд). Среди нетехнологичных компаний нужно отметить холдинг Уоррена Баффета Berkshire Hathaway ($128 млрд).

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

А есть такие компании в России?

Идеальный пример готовой к нынешнему кризису российской компании — «Сургутнефтегаз». На ее счетах около $52 млрд в валюте при полном отсутствии долгов. Например, это больше, чем рыночная капитализация «Лукойла» ($39,8 млрд). При этом капитализация самого «Сургутнефтегаза» оценивается всего в $18,3 млрд.

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

Фото:Полиметалл

Кроме того, большие резервы накоплены у энергетической компании «Интер РАО»: по итогам четвертого квартала 2019 года чистый долг компании составил -₽196,5 млрд. Также стоит выделить «Распадскую» с отрицательным чистым долгом $571 млн по итогам 2019 года.

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

Главное отличие наших компаний и иностранных — в количестве таких компаний. Так как большая часть российских компаний развивается за счет кредитования и привлечения инвестиций и пока не могут показывать большое количество cash flow», — отметил директор офиса продаж компании «БКС Брокер» Вячеслав Абрамов.

Больше новостей об инвестициях вы найдете в нашем телеграм-канале «Сам ты инвестор!»

Дивиденды — это часть прибыли или свободного денежного потока (FCF), которую компания выплачивает акционерам. Сумма выплат зависит от дивидендной политики. Там же прописана их периодичность — раз в год, каждое полугодие или квартал. Есть компании, которые не платят дивиденды, а направляют прибыль на развитие бизнеса или просто не имеют возможности из-за слабых результатов. Акции дивидендных компаний чаще всего интересны инвесторам, которые хотят добиться финансовой независимости или обеспечить себе достойный уровень жизни на пенсии. При помощи дивидендов они создают себе источник пассивного дохода. Подробнее

Меня зовут Виктор Пряжников, я работаю в SRV-команде Badoo. Наша команда занимается разработкой и поддержкой внутреннего API для наших клиентов со стороны сервера, и кэширование данных — это то, с чем мы сталкиваемся каждый день.

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


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

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

Деление данных между кэширующими серверами

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

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

Есть разные алгоритмы для реализации этого. Самый простой — вычисление номера сервера как остатка от целочисленного деления численного представления ключа (например, CRC32) на количество кэширующих серверов:


Такой алгоритм называется хешированием по модулю (англ. modulo hashing). CRC32 здесь использован в качестве примера. Вместо него можно взять любую другую хеширующую функцию, из результатов которой можно получить число, большее или равное количеству серверов, с более-менее равномерно распределённым результатом.

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

Я написал небольшой скрипт, который продемонстрирует эту проблему.

В нём генерируется 1 млн уникальных ключей, распределённых по пяти серверам с помощью хеширования по модулю и CRC32. Я эмулирую выход из строя одного из серверов и перераспределение данных по четырём оставшимся.

В результате этого «сбоя» примерно 80% ключей изменят своё местоположение, то есть окажутся недоступными для последующего чтения:

Total keys count: 1000000
Shards count range: 4, 5

ShardsBefore ShardsAfter LostKeysPercent LostKeys
5 4 80.03% 800345

Самое неприятное тут то, что 80% — это далеко не предел. С увеличением количества серверов процент потери кэша будет расти и дальше. Единственное исключение — это кратные изменения (с двух до четырёх, с девяти до трёх и т. п.), при которых потери будут меньше обычного, но в любом случае не менее половины от имеющегося кэша:



Я выложил на GitHub скрипт, с помощью которого я собрал данные, а также ipynb-файл, рисующий данную таблицу, и файлы с данными.

Для решения этой проблемы есть другой алгоритм разбивки — согласованное хеширование (англ. consistent hashing). Основная идея этого механизма очень простая: здесь добавляется дополнительное отображение ключей на слоты, количество которых заметно превышает количество серверов (их могут быть тысячи и даже больше). Сами слоты, в свою очередь, каким-то образом распределяются по серверам.

При изменении количества серверов количество слотов не меняется, но меняется распределение слотов между этими серверами:

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


На картинке начального разбиения все слоты одного сервера расположены подряд, но в реальности это не обязательное условие — они могут быть расположены как угодно.

Основное преимущество этого способа перед предыдущим заключается в том, что здесь каждому серверу соответствует не одно значение, а целый диапазон, и при изменении количества серверов между ними перераспределяется гораздо меньшая часть ключей ( k / N , где k — общее количество ключей, а N — количество серверов).

Если вернуться к сценарию, который я использовал для демонстрации недостатка хеширования по модулю, то при той же ситуации с падением одного из пяти серверов (с одинаковым весом) и перераспределением ключей с него между оставшимися мы потерям не 80% кэша, а только 20%. Если считать, что изначально все данные находятся в кэше и все они будут запрошены, то эта разница означает, что при согласованном хешировании мы получим в четыре раза меньше запросов к базе данных.

Код, реализующий этот алгоритм, будет сложнее, чем код предыдущего, поэтому я не буду его приводить в статье. При желании его легко можно найти — на GitHub есть rendezvous hashing), но они гораздо менее распространены.

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

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

В нашем случае почти все кэшируемые данные относятся к одному пользователю, поэтому мы используем User ID в качестве параметра шардирования данных в кэше. Благодаря этому у нас получается распределить данные более-менее равномерно. Кроме того, мы получаем бонус — возможность использования multi_get для загрузки сразу нескольких разных ключей с информацией о юзере (что мы используем в предзагрузке часто используемых данных для текущего пользователя). Если бы положение каждого ключа определялось динамически, то невозможно было бы использовать multi_get при таком сценарии, так как не было бы гарантии, что все запрашиваемые ключи относятся к одному серверу.

Параллельные запросы на обновление данных

Посмотрите на такой простой кусочек кода:


Что произойдёт при отсутствии запрашиваемых данных в кэше? Судя по коду, должен запуститься механизм, который достанет эти данные. Если код выполняется только в один поток, то всё будет хорошо: данные будут загружены, помещены в кэш и при следующем запросе взяты уже оттуда. А вот при работе в несколько параллельных потоков всё будет иначе: загрузка данных будет происходить не один раз, а несколько.

Выглядеть это будет примерно так:


На момент начала обработки запроса в процессе №2 данных в кэше ещё нет, но они уже читаются из базы данных в процессе №1. В этом примере проблема не такая существенная, ведь запроса всего два, но их может быть гораздо больше.

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

Предположим, у вас есть какой-то функционал, использующий кэш с нагрузкой 200 запросов в секунду. Если на на загрузку данных нужно 50 мс, то за это время вы получите 50 / (1000 / 200) = 10 запросов.

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

Эта проблема называется cache stampede (русского аналога этого термина я не нашёл, дословно это можно перевести как «паническое бегство кэша», и картинка в начале статьи показывает пример этого действия в дикой природе), hit miss storm («шторм непопаданий в кэш») или dog-pile effect («эффект собачьей стаи»). Есть несколько способов её решения:

Блокировка перед началом выполнения операции пересчёта/ загрузки данных

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

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

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

Вынос обновлений в фон

Основная идея этого способа — разделение по разным процессам чтения данных из кэша и записи в него. В онлайн-процессах происходит только чтение данных из кэша, но не их загрузка, которая идёт только в отдельном фоновом процессе. Данный вариант делает невозможными параллельные обновления данных.

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

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

Вероятностные методы обновления

Суть этих методов заключается в том, что данные в кэше обновляются не только при отсутствии, но и с какой-то вероятностью при их наличии. Это позволит обновлять их до того, как закэшированные данные «протухнут» и потребуются сразу всем процессам.

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


В данном примере $ttl — это время жизни значения в кэше, $delta — время, которое потребовалось для генерации кэшируемого значения, $expiry — время, до которого значение в кэше будет валидным, $beta — параметр настройки алгоритма, изменяя который, можно влиять на вероятность пересчёта (чем он больше, тем более вероятен пересчёт при каждом запросе). Подробное описание этого алгоритма можно прочитать в white paper «Optimal Probabilistic Cache Stampede Prevention», ссылку на который вы найдёте в конце этого раздела.

Нужно понимать, что при использовании подобных вероятностных механизмов вы не исключаете параллельные обновления, а только снижаете их вероятность. Чтобы исключить их, можно «скрестить» несколько способов сразу (например, добавив блокировку перед обновлением).

«Холодный» старт и «прогревание» кэша

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

В этом случае сразу после выкатки данные начнут загружаться (первое проявление проблемы), после чего попадут в кэш — и какое-то время всё будет хорошо, а после истечения срока жизни кэша все данные снова начнут загружаться и создавать повышенную нагрузку на базу данных.

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

  • плавным включением нового функционала. Для этого необходим механизм, который позволит это сделать. Простейший вариант реализации — выкатывать новый функционал включённым на небольшую часть пользователей и постепенно её увеличивать. При таком сценарии не должно быть сразу большого вала обновлений, так как сначала функционал будет доступен только части пользователей, а по мере её увеличения кэш уже будет «прогрет».
  • разным временем жизни разных элементов набора данных. Данный механизм можно использовать, только если система в состоянии выдержать пик, который наступит при выкатке всего функционала. Его особенность заключается в том, что при записи данных в кэш у каждого элемента будет своё время жизни, и благодаря этому вал обновлений сгладится гораздо быстрее за счёт распределения последующих обновления во времени. Простейший способ реализовать такой механизм — умножить время жизни кэша на какой-то случайный множитель:

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

Пример

Я написал небольшой скрипт, который эмулирует ситуацию «непрогретого» кэша.
В нём я воспроизвожу ситуацию, при которой пользователь при запросе загружает данные о себе (если их нет в кэше). Конечно, пример синтетический, но даже на нём можно увидеть разницу в поведении системы.

Вот как выглядит график количества hit miss-ов в ситуации с фиксированным (fixed_cache_misses_count) и различным (random_cache_misses_count) сроками жизни кэша:


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

«Горячие» ключи

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


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


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

Мы в Badoo решаем эту проблему тем, что пишем данные во все кэширующие серверы сразу. Благодаря этому при чтении мы можем использовать общий механизм выбора сервера — в коде можно использовать обычный механизм шардирования по User ID, и при чтении не нужно ничего знать про специфику этого «горячего» ключа. В нашем случае это работает, поскольку у нас сравнительно немного серверов (примерно десять на площадку).

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

Если вы используете определение сервера по ключу кэша, то можно добавить к нему ограниченное количество псевдослучайных значений (сделав из total_users_count что-то вроде t otal_users_count_1 , total_users_count_2 и т. д.). Подобный подход используется, например, в Etsy.

Если вы используете явные указания параметра шардирования, то просто передавайте туда разные псевдослучайные значения.

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

Сбои в работе

Система не может быть надёжной на 100%, поэтому нужно предусмотреть, как она будет вести себя при сбоях. Сбои могут быть как в работе самого кэша, так и в работе базы данных.

При сбоях в работе базы данных и отсутствии кэша мы можем попасть в ситуацию cache stampede, про которую я тоже уже рассказывал раньше. Выйти из неё можно уже описанными способами, а можно записать в кэш заведомо некорректное значение с небольшим сроком жизни. В этом случае система сможет определить, что источник недоступен, и на какое-то время перестанет пытаться запрашивать данные.

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

реклама


А теперь о Raptoreum. Важно! Не поддавайтесь на провокации, относительно этой криптовалюты, которая добывается на мощностях кэш-памяти центральных процессоров! Чем больше кэш-память второго и любого последующего уровня (при его наличии), тем быстрее происходят расчёты, результатом которых является условный доход. Так как сейчас на рынке именно у процессоров AMD самый «жирный» кэш, то наличие их на полках магазинов и соответственно рекомендованной стоимости этих процессоров под угрозой со стороны новой криптовалюты. Под удар могут попасть старейшие модели AMD Ryzen и все Threadripper.

MSI RTX 3070 сливают дешевле любой другой, это за копейки Дешевая 3070 Gigabyte Gaming - успей пока не началось

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


Все эти операции производятся на очень высоких скоростях, от десятков гигабайт в секунду у процессоров 20-ти летней давности, до сотен гигабайт в секунду у современных процессоров. Все это время процессор обменивается данными с оперативной памятью, которая работает в разы медленнее самого процессора. Каждое считывание из нее и обратная запись информации в неё отнимают уйму времени.

реклама

var firedYa28 = false; window.addEventListener('load', () => < if(navigator.userAgent.indexOf("Chrome-Lighthouse") < window.yaContextCb.push(()=>< Ya.Context.AdvManager.render(< renderTo: 'yandex_rtb_R-A-630193-28', blockId: 'R-A-630193-28' >) >) >, 3000); > > >);

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

Кэш-память процессора – это небольшая по объему, но супербыстрая оперативная память. Она встроена в процессор и является своеобразным буфером, при обмене CPU данными с медленной оперативной памятью, а в современных реалиях еще и с NVME SSD и видеокартой.


В большинстве процессоров используется многоуровневая система кэша:

реклама

• Кэш-память первого уровня или L1 – самая маленькая, но и самая быстрая область кэш-памяти. Её объем не превышает пару десятков килобайт. Работает L1 без каких-либо задержек. В нем содержатся данные, которые чаще всего используются процессором.

• Кэш-память второго уровня (L2) чуть медленнее кэш-памяти L1, но и объем ее в современных процессорах измеряется уже в мегабайтах. Служит она для временного хранения важных данных, вероятность запроса которых ниже, чем у данных, находящихся в L1 кэше.

• Кэш-память третьего уровня (L3) – еще более объемная и еще более медленная. Но она все равно быстрее любой оперативной памяти, даже новой DDR5. Со скоростями в несколько сотен гигабайт в секунду пока еще приходится считаться. Ее размер в современных процессорах в мейнстрим сегменте достигает нескольких десятков мегабайт, а в серверных AMD Epyc счет пошел уже на сотни мегабайт. В отличие от L1 и L2, кэш третьего уровня является общим для всех ядер процессора.

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

реклама


А теперь небольшой экскурс в развитие и эволюцию кэш-памяти. Если за отправную точку взять Pentium 1, то кэша L2 у него не было. L1 был объёмом 32 Кб. L2 как правило распаивался на материнской плате. Когда появился Pentium II, то L2 сразу стал равным 512 Кб, но он располагался рядом с ядром процессора на отдельной микросхеме и имел низкую скорость, но все равно это лучше, чем ничего.

Pentium-III с Socket 370 имел уже половину объема от Pentium II – 256 Кб, но зато этот кэш был быстрее, так как он был интегрирован в кристалл процессора. Pentium 4 вначале удвоил этот объём до 512 Кб, так называемый Nothwood, а последовавшее за ним ядро Prescott довел L2 до 1 Мб. Это уже объём дней сегодняшних.


Во времена Pentium 4 случилось еще одно важное событие: у Pentium 4 Extreme Edition впервые появился L3 кэш. До этого момента в десктопах такого явления не было. Объем L3 равнялся «жирным» 2 мегабайтам, что положительно влияло на производительность и цену процессора.


После смены с 478 на 775 контактов без ножек, первые “новые” Pentium с ядрами Prescott-2M и Cedar Mill увеличили L2 до 2-х мегабайт, а L1 так и оставался в пределах 32 Кб.

Микроархитектура Core2 и Core2 Quad значительно увеличили объемы кэшей. Так, объем L2 кэша уже варьировался от 4 до 12 Мб, но эти цифры нужно поделить на два, из особенности организации кэша, так как группы ядер по факту взаимодействовали только с половиной этого объема, но суммарный объем был именно таким.

Микроархитектура Sandy Bridge поделила процессоры на различные линейки в зависимости от объема L2 кэша. В этот момент времени, именно объем кэш-памяти начинает решать главенствующую роль в позиционировании и производительности процессоров.


Так, i5-2500К отличался от i7-2600K только объемом кэша. И надо отметить, речь уже идет о L3 кэше, который начинает появляться уже повсеместно. В первом случае L3 = 6 Мб, во втором 8 Мб. L2 у обеих моделей равнялся 256 Кб на одно ядро, а L1 был равен по-прежнему 32 Кб.

Далее происходит рост L3 у последующих поколений. У i9-9900K он уже равен 16 Мб. Параллельно развивается HEDT линейка процессоров Intel, где L3 уже достигает 24.5 Мб. Но дальнейшее развитие всей процессорной архитектуры меняют процессоры AMD с микроархитектурой ZEN и производные от них. Стартовало первое поколение AMD Ryzen c L2 = 512 Кб и L3 = 8 Мб, а на сегодняшний день топовый Ryzen Gen3 уже обладает 64 Мб L3 кэша. Недавний анонс новых серверных процессоров AMD Epyс вообще увеличил объем L3 до астрономических 768 Мб.


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

Поэтому при выборе того или иного процессора, обращайте внимание на объем L3 кэша процессора. Возможно, в будущем, объемы порядка пары гигабайт L3 кэша станут нормой, но а пока следим за стоимостью AMD Ryzen, в зависимости от популярности Raptoreum. Надеюсь, этот блог оказался для вас полезным.

Читайте также: