мой идеальный кэш с помощью guava
на протяжении последних нескольких недель я пытался найти мой идеал реализации кэша, используя гуава это картографа. См. мои предыдущие два вопроса здесь и здесь следить за моим мыслительным процессом.
принимая то, что я узнал, моя следующая попытка будет отбросить мягкие значения в пользу maximumSize и expireAfterAccess:
ConcurrentMap<String, MyObject> cache = new MapMaker()
.maximumSize(MAXIMUM_SIZE)
.expireAfterAccess(MINUTES_TO_EXPIRY, TimeUnit.MINUTES)
.makeComputingMap(loadFunction);
здесь
Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
@Override
public MyObject apply(String uidKey) {
return getFromDataBase(uidKey);
}
};
тем не менее, одна оставшаяся проблема, которую я все еще борюсь с тем, что эта реализация будет выселять объекты, даже если они сильно достижимы, как только их время истекло. Это может привести к нескольким объектам с одним и тем же UID, плавающим в среде, чего я не хочу (я считаю, что то, что я пытаюсь достичь, известно как канонизация).
насколько я могу сказать, единственный ответ-иметь дополнительную карту, которая функционирует как интернер, который я могу проверить, если объект данных все еще находится в память:
ConcurrentMap<String, MyObject> interner = new MapMaker()
.weakValues()
.makeMap();
и функция нагрузки будет пересмотрена:
Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
@Override
public MyObject apply(String uidKey) {
MyObject dataObject = interner.get(uidKey);
if (dataObject == null) {
dataObject = getFromDataBase(uidKey);
interner.put(uidKey, dataObject);
}
return dataObject;
}
};
однако использование двух карт вместо одной для кэша кажется неэффективным. Есть ли более утонченный способ подойти к этому? В общем, правильно ли я это делаю, или я должен переосмыслить свою стратегию кэширования?
2 ответов
эффективность двух карт полностью зависит от того, насколько дорог getFromDatabase() и насколько велики ваши объекты. Это не кажется из всех разумных границ, чтобы сделать что-то подобное.
Что касается реализации, похоже, что вы, вероятно, можете наложить свои карты немного другим способом, чтобы получить нужное поведение, и все еще иметь хорошие свойства параллелизма.
- создайте свою первую карту со слабыми значениями и поместите вычислительную функцию getFromDatabase () на этой карте.
- вторая карта является истекающей, также вычислительной, но эта функция просто получает от первой карты.
У всех свой доступ через вторую карту.
другими словами, истекающая карта действует, чтобы закрепить наиболее недавно использованное подмножество ваших объектов в памяти, в то время как карта слабой ссылки является реальным кэшем.
- dg
Я не понимаю полную картину здесь, но две вещи.
учитывая это утверждение: "эта реализация будет выселять объекты, даже если они сильно достижимы, как только их время истекло. Это может привести к нескольким объектам с одним и тем же UID, плавающим в среде, чего я не хочу."...похоже, вам просто нужно использовать weakKeys() и не использовать ни Временное, ни размерное выселение.
или если вы хотите принести "интернер" в этом, я бы использовал настоящий
Interners.newWeakInterner
.