← Назад к вопросам
Как долго действует кэш первого уровня
2.0 Middle🔥 161 комментариев
#Кэширование и NoSQL
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Как долго действует кэш первого уровня (L1 Cache)
Кэш первого уровня (L1 Cache) в контексте ORM (например, Hibernate) и JVM - это очень быстрая, но очень короткоживущая память. Рассмотрим оба контекста.
1. L1 Cache в Hibernate (Session/Persistence Context)
Время жизни: Весь lifecycle Session
@Autowired
private SessionFactory sessionFactory;
public void demonstrateL1Cache() {
Session session = sessionFactory.openSession();
try {
// Кэш L1 существует с момента открытия Session
User user1 = session.get(User.class, 1L);
System.out.println("Первый запрос - SELECT из БД");
// Этот запрос не пойдет в БД - вернет из L1
User user2 = session.get(User.class, 1L);
System.out.println("Второй запрос - из L1 Cache (нет SQL)");
// Проверка: это один объект?
System.out.println(user1 == user2); // true - один объект!
user1.setName("Updated");
session.flush(); // Обновление пойдет в БД
} finally {
session.close(); // L1 Cache УНИЧТОЖАЕТСЯ здесь
}
// L1 Cache более не существует
}
Время жизни L1 Cache в Spring:
@Transactional
public void transactionLevel() {
User user1 = userRepository.findById(1L); // SELECT
User user2 = userRepository.findById(1L); // ИЗ КЭША
} // transaction.commit() -> Session закрывается -> L1 Cache очищается
User user3 = userRepository.findById(1L); // Новая транзакция, новая Session
// SELECT снова
2. L1 Cache в JVM (CPU уровне): Микросекунды
Это совсем другое понимание L1 Cache - это аппаратный кэш.
Типичные латенси:
├── L1 Cache (CPU) 4 цикла (~ 1-4 наносекунды)
├── L2 Cache (CPU) 10 цикла (~ 3-10 наносекунд)
├── L3 Cache (CPU) 40 цикла (~ 10-40 наносекунд)
├── RAM 200 цикла (~ 60-100 наносекунд)
├── SSD 150,000 цикла (~ 50 микросекунд)
├── HDD 10,000,000 цикла (~ 5 миллисекунд)
└── Network 100,000,000+ цикла (~ 100+ миллисекунд)
Этот кэш НЕ контролируется программистом.
3. Сравнение разных типов кэшей
// ❌ Неправильно смешивать понятия
// L1 Cache может означать:
// 1. Hibernate L1 Cache (Session) — ВРЕМЯ ЖИЗНИ: одна транзакция
// 2. JVM L1 Cache (CPU) — ВРЕМЯ ЖИЗНИ: наносекунды (не контролируется)
public class CacheComparison {
// Вариант 1: Hibernate L1 (Session level)
@Transactional
public void hibernateL1() {
User u1 = userRepository.findById(1L); // SELECT
User u2 = userRepository.findById(1L); // Cache hit (одна Session)
} // Session close -> Cache clear
// Вариант 2: Spring Cache (L2 level)
@Cacheable("users")
public User getUserWithCache(Long id) {
return userRepository.findById(id).orElse(null); // SELECT только первый раз
// Потом возвращает из Redis/Caffeine
}
// Вариант 3: CPU Cache (микросекунды - не контролируем)
public int sumArray(int[] arr) {
int sum = 0;
for (int num : arr) {
sum += num; // num может быть в L1 CPU Cache
}
return sum;
}
}
4. Практический пример: L1 Cache в Hibernate
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void demonstrateL1() {
// Вся транзакция = одна Session = один L1 Cache
User user1 = userRepository.findById(1L);
user1.setActive(true);
// Один и тот же объект
User user2 = userRepository.findById(1L);
System.out.println(user1 == user2); // true
System.out.println(user2.isActive()); // true (видит изменение от user1)
// user2 изменится, как и user1 - это один объект
user2.setEmail("new@example.com");
} // flush() -> commit() -> Session close -> L1 clear
}
// Следующая транзакция = новая Session = новый L1 Cache
@Transactional
public void nextTransaction() {
User user3 = userRepository.findById(1L); // SELECT - L1 Cache новой Session
// user3 == user1 ? FALSE - разные объекты в разных Sessions
}
5. Проблемы с L1 Cache
Проблема 1: LazyInitializationException
@Transactional
public void initializeOutside() {
User user = userRepository.findById(1L);
// user в L1 Cache этой Session
}
// Session close -> L1 очищен
// Попытка доступа к lazy-loaded полю
System.out.println(user.getProfile().getName()); // ❌ LazyInitializationException!
// Profile было lazy-loaded, но L1 Session уже закрыта
Решение:
@Transactional
public void initializeInside() {
User user = userRepository.findById(1L);
// Инициализируем ленивые поля ДО закрытия Session
user.getProfile().getName(); // ✅ Работает
}
Проблема 2: Memory Leak
@Transactional
public void memoryLeak() {
List<User> users = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
User user = userRepository.findById((long)i);
users.add(user); // ❌ Все объекты в L1 Cache + список
}
// L1 Cache содержит 1 миллион объектов в памяти!
}
Решение:
@Transactional
public void noClear() {
for (int i = 0; i < 1000000; i++) {
User user = userRepository.findById((long)i);
processUser(user);
// Очищаем L1 Cache каждые 100 объектов
if (i % 100 == 0) {
entityManager.clear(); // L1 Cache clear
}
}
}
6. Управление L1 Cache
Явное очищение L1 Cache
@Autowired
private EntityManager entityManager;
@Transactional
public void clearL1Cache() {
User user1 = userRepository.findById(1L);
user1.setName("Alice");
// Очищаем L1 Cache
entityManager.clear(); // Все объекты выгружены из кэша
// Следующий findById пойдет в БД
User user2 = userRepository.findById(1L);
user1 == user2 // false - разные объекты
user2.getName() // "John" - старое значение из БД
}
Detach объект из L1 Cache
@Transactional
public void detachObject() {
User user = userRepository.findById(1L);
user.setName("Alice");
entityManager.detach(user); // Удаляем только этот объект из L1
user.setName("Bob"); // Изменение НЕ будет сохранено
} // flush() не вызовет UPDATE для user
7. Время жизни L1 Cache по транзакциям
Трансакция 1:
├─ Session open
├─ L1 Cache create
├─ Запросы и операции (кэш активен)
└─ Session close -> L1 Cache destroy
Трансакция 2: (новая Session!)
├─ Session open
├─ L1 Cache create (новый!)
├─ Запросы и операции
└─ Session close -> L1 Cache destroy
8. Сравнение L1 и L2 Cache
| Параметр | L1 Cache (Session) | L2 Cache (Second-level) |
|---|---|---|
| Время жизни | 1 транзакция | Весь ApplicationContext |
| Область видимости | 1 Session | Несколько Sessions |
| Размер | ~10-100 MB | ~100-500 MB (configurable) |
| Тип | Map (в памяти) | Redis/Memcached/EhCache |
| Инвалидация | Автоматическая | Ручная или TTL |
| Контроль | Автоматический | Нужна конфигурация |
9. Настройка L1 Cache
# application.yml
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 20
fetch_size: 50
order_inserts: true
order_updates: true
Итоговый ответ
В контексте ORM (Hibernate): L1 Cache действует в течение всего жизненного цикла Session, которая обычно соответствует одной транзакции в Spring приложении. После закрытия транзакции L1 Cache полностью очищается.
В контексте JVM (CPU): L1 Cache действует в течение нескольких наносекунд и полностью контролируется процессором, не программистом.
Практически: L1 Cache вашего приложения = одна @Transactional метода.