Анализ ccache, высокопроизводительного компонента кэша на языке go

Go

1. Предпосылки

При написании кода использование принципа локальности для кэширования данных является распространенным методом оптимизации производительности.

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

Стратегии уменьшения конфликтов блокировок:

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

В частности, как добиться взгляда ниже.

2. lru cache

Прежде чем анализировать исходный код, сначала кратко разберитесь, что делает кэш lru.

lru — это аббревиатура термина «наименее недавно использованный».Как следует из названия, после заполнения кэша lru cache должен удалить контент, к которому не обращались в течение длительного времени, прежде чем кэшировать новый контент.

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

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

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

Если мы выполним операцию get (key2), вы найдете значение 2 и элемент 2 через ключ 2. Вы можете найти Node2 через Element2, затем переместить Node2 в команду List, поэтому после выполнения GET (KEY2) изображение изменится

Затем, если есть еще одна операция набора (key5, value5), и наш кеш может буферизовать до четырех данных, будет то, как с этим справиться. Сначала в хэш-таблицу вставляется значение5 и ключ5, а в начало списка очереди вставляется Node5, а затем удаляется команда хвоста списка элементов, здесь node4, удаляются, удаляются данные, соответствующие node4 в хеш-таблице. При выполнении плана станет

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

3. Стратегия оптимизации ccache

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

3.1 Разделение хеш-таблицы

Это очень распространенная стратегия.

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

Как показано на рисунке, хэш-таблица разбивается на три хеш-таблицы по ключу, и блокировок также становится три. Таким образом, при одновременном доступе к хэш-таблице1 и хэш-таблице2 не будет конфликта.

3.2 Повышение привилегий происходит только после многократного накопительного доступа

К значению добавляется новый счетчик доступа, и счетчик равен +1 для каждой операции получения. Когда счетчик достигает порога, он перемещается в начало списка, а счетчик сбрасывается до 0.

Если порог равен 3, то операция записи в список сократится в 3 раза, а вероятность конфликтов блокировок уменьшится в 3 раза.

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

3.3 Откройте один поток, чтобы обновить список

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

В этом есть два преимущества

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

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

3.4 Пакетное исключение

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

3.5 Общий процесс

После реализации вышеуказанной стратегии общий процесс выглядит примерно так:

3.5.1 получить операцию

3.5.2 установка операции