Hibernate 缓存一级二级查询缓存

FreeGuideOnline 最新 2026-07-12

Hibernate 缓存体系概述

Hibernate 提供了一套多层缓存机制,用于减少数据库访问、提升应用性能。缓存体系分为三个核心层次:

  • 一级缓存Session 级别,默认开启,事务范围内有效。
  • 二级缓存SessionFactory 级别,跨 Session 共享,需显式配置。
  • 查询缓存:针对 HQL/Criteria 查询结果集的缓存,依赖二级缓存,需手动开启。

理解并正确使用这些缓存,能在不牺牲数据一致性的前提下大幅优化读操作。


一级缓存(Session 级缓存)

工作原理

一级缓存与 Session 生命周期绑定。当 Session 通过主键加载实体时,会先检查缓存中是否存在该实例:

  • 缓存命中:直接返回,不发起 SQL 查询。
  • 缓存未命中:查询数据库,将结果放入一级缓存并返回。

同一个 Session 内,多次 session.get(id)session.load(id) 只会执行一次数据库查询

生命周期与清除

Session 关闭时,一级缓存随之销毁。也可以主动清理或刷新:

  • session.clear():清空一级缓存,所有对象变为游离状态。
  • session.evict(object):从缓存中移除特定对象。
  • session.flush():将缓存中的变更同步到数据库,但不清空缓存。

自动脏检查与快照

Hibernate 会在脏检查时对比一级缓存中的对象与其初始快照,自动生成 UPDATE 语句。频繁的 flush 可能触发大量 SQL


二级缓存(SessionFactory 级缓存)

什么是二级缓存

二级缓存是应用级别的缓存,多个 Session 可以共享。开启后,当一个 Session 加载实体,在另一个 Session 中获取相同主键的数据时,可直接命中缓存。

配置步骤

  1. 引入缓存提供商
    常用 Ehcache、Hazelcast、Redis(通过自定义 RegionFactory)。以 Ehcache 为例,添加依赖并配置 hibernate.cfg.xml

    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.region.factory_class">
        org.hibernate.cache.ehcache.EhCacheRegionFactory
    </property>
    
  2. 配置缓存策略
    实体的缓存通过注解或 XML 声明:

    @Entity
    @Cacheable
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    public class Product {
        // ...
    }
    

    可用策略:

    • READ_ONLY:用于不修改的数据,性能最高。
    • NONSTRICT_READ_WRITE:偶尔更新时使用,不保证严格一致性。
    • READ_WRITE:读写锁机制,保证隔离性。
    • TRANSACTIONAL:完全事务性缓存,需 JTA 环境。
  3. 配置集合缓存
    关联集合(如 @OneToMany)也可以缓存:

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
    private Set<Product> products;
    

注意事项

  • 二级缓存只按 主键(ID) 存储实体对象。
  • 查询结果不会自动放入二级缓存,需要配合查询缓存。
  • 更新频繁的数据不适合放入二级缓存,可能造成频繁失效和缓存同步开销。

查询缓存

为什么需要查询缓存

二级缓存仅缓存单个实体,但真实业务中大量使用 HQL 或 Criteria 查询(如按条件查询集合)。查询缓存专门缓存 查询语句 + 参数 的结果集,结果集中仅存储实体的 ID(或主键值),实际实体仍从二级缓存或数据库读取。

启用查询缓存

  1. 全局开启

    <property name="hibernate.cache.use_query_cache">true</property>
    
  2. 在查询中设置可缓存

    Query query = session.createQuery("from Product p where p.price > :price");
    query.setParameter("price", new BigDecimal("100"));
    query.setCacheable(true);
    List products = query.list();
    

    或者使用 JPA 的 Hint

    Query query = entityManager.createQuery("select p from Product p where p.price > :price");
    query.setHint("org.hibernate.cacheable", true);
    

查询缓存的工作流程

  1. 首次执行查询时,执行 SQL 获取结果集,将 查询键(HQL + 参数)和实体ID列表 放入查询缓存区域。
  2. 再次执行相同查询(完全一致的 HQL 和参数)时,从查询缓存中获取 ID 列表。
  3. 根据每个 ID 去二级缓存或一级缓存获取实体对象,若缺失则批量加载。

注意事项

  • 查询缓存依赖 二级缓存,若未配置二级缓存,实体仍会重新查询数据库。
  • 任何一张 关联表的修改(增、删、改)都会导致相关查询缓存区域失效,开销可能较大。
  • 不同查询参数生成不同缓存键,要防止缓存膨胀。

缓存的组合使用与常见陷阱

典型缓存策略

  • 极少变化的数据:开启 READ_ONLY 二级缓存 + 查询缓存。
  • 频繁读、极少写的数据READ_WRITE 二级缓存 + 适量查询缓存。
  • 经常更新的数据:只使用一级缓存,避免二级缓存带来的失效风暴。

脏数据与并发

  • 使用 READ_WRITE 策略时,Hibernate 通过读写锁保证数据一致性,但会略降性能。
  • 分布式环境下,需选用支持分布式缓存的实现(如 Hazelcast/Infinispan),否则不同节点缓存不一致。

性能监控

启用 Hibernate 统计信息可监控缓存命中率:

<property name="hibernate.generate_statistics">true</property>

通过 sessionFactory.getStatistics().getSecondLevelCacheHitCount() 分析缓存效率。


总结

  • 一级缓存是每个 Session 工作的基础,无需配置但要理解其行为和脏检查。
  • 二级缓存适合跨会话共享且不常变化的实体,需显式声明缓存策略和提供者。
  • 查询缓存用于缓存查询结果的 ID 集合,必须与二级缓存配合使用,谨慎选择缓存范围。
  • 错误的缓存配置可能引入性能问题和脏读,建议在监控下逐步开启。