那些年背过的题:Hibernate缓存机制底层原理

151 阅读5分钟

Hibernate 缓存机制是为了提高数据库访问性能,通过减少对数据库的直接访问次数来实现的。Hibernate 提供了两级缓存机制:一级缓存(Session 级别)和二级缓存(SessionFactory 级别)。

一级缓存

  • 范围:一级缓存是 Hibernate 默认启用的,会话级别(Session)。每个 Session 都有一个独立的一级缓存。
  • 特点:在同一个 Session 中,重复读取同一条数据时,Hibernate 会直接从缓存中获取,而不是再次查询数据库。
  • 实现:通过一个 Map 结构来保存实体对象,使用实体的主键作为键。

二级缓存

  • 范围:二级缓存是跨越会话的缓存,需要显式配置,并且在 SessionFactory 级别共享。
  • 特点:适用于需要在多个 Session 之间共享的持久化对象,通过某些第三方缓存提供者(如 EhCache、OSCache、Infinispan 等)实现。
  • 实现:通过缓存提供者的具体实现来管理和维护缓存内容,包括对象生命周期、缓存策略等。

底层原理

  1. 缓存存储

    • 一级缓存存储在内存中的一个 HashMap 中,这个 HashMap 是 Session 对象的一部分。
    • 二级缓存的存储依赖于具体的缓存提供者,可以存储在内存中,也可以持久化到磁盘。
  2. 缓存操作

    • 当执行查询操作时,首先检查一级缓存,如果命中则直接返回结果;否则,再检查二级缓存。
    • 如果二级缓存也未命中,则执行实际的数据库查询,并将结果分别放入一级缓存和二级缓存。
  3. 缓存同步

    • 对于一级缓存来说,同步是自动的,因为它只存在于当前 Session 中。
    • 对于二级缓存,Hibernate 提供了多种缓存策略(如读写、非严格读写、只读、事务性),不同策略有不同的同步机制。
  4. 缓存失效

    • 一级缓存随着 Session 的关闭而失效。
    • 二级缓存具有可配置的失效策略,如基于时间的失效、LRU(最近最少使用)算法等。

示例流程

  1. 读取数据

    • 首先尝试从一级缓存读取,如果找到则返回。
    • 否则,尝试从二级缓存读取,如果找到则返回,并放到一级缓存中。
    • 如果都找不到,则执行数据库查询,将结果放到一级缓存和二级缓存中。
  2. 更新数据

    • 更新操作会使一级缓存中的相应实体失效(因为数据已修改,需要重新加载)。
    • 同时通知二级缓存,更新或清除相应的缓存条目。

总的来说,Hibernate 的缓存机制通过减少数据库访问次数,提高了应用程序的性能,但同时需要注意缓存一致性、失效策略等问题以确保数据的正确性和实时性。

Hibernate 缓存机制的源码实现涉及到多个模块和类。以下是对其核心部分的简要介绍,以帮助理解其工作原理。

一级缓存(Session Cache)

一级缓存是在 Session 级别实现的,默认情况下是开启的,且每个 Session 都有独立的缓存。

关键类

  • org.hibernate.engine.spi.SessionImplementor
  • org.hibernate.internal.SessionImpl

示例代码

public class SessionImpl implements Session {
    private EntityEntryContext entityEntryContext = new StatefulPersistenceContext(this);
    
    @Override
    public Object get(Class<?> clazz, Serializable id) {
        // 从一级缓存中读取对象
        Object cachedEntity = entityEntryContext.getEntry(id);
        if (cachedEntity != null) {
            return cachedEntity;
        }
        
        // 如果一级缓存中没有,则查询数据库
        Object entity = databaseQuery(clazz, id);
        entityEntryContext.addEntry(id, entity);
        return entity;
    }
}

二级缓存(SessionFactory Cache)

二级缓存是可选的,需要在配置文件中显式启用,并可以选择不同的缓存提供者,如 EHCache、Infinispan 等。

关键类

  • org.hibernate.cache.spi.CacheImplementor
  • org.hibernate.cache.internal.StandardCacheImplementor
  • org.hibernate.cfg.Settings
  • org.hibernate.cache.spi.RegionFactory

配置示例

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>
<property name="hibernate.cache.provider_configuration_file_resource_path">ehcache.xml</property>

实现逻辑

  1. 缓存实体:使用 CacheConcurrencyStrategy 来管理并发访问。
  2. 缓存区域:通过 Region 接口定义缓存区域。

示例代码

以下是一个简单的示例,展示如何使用 EHCache 实现二级缓存:

public class EhCacheRegionFactory implements RegionFactory {
    private CacheManager cacheManager;

    @Override
    public void start(Settings settings, Properties properties) throws CacheException {
        this.cacheManager = CacheManager.create(properties.getProperty("hibernate.cache.provider_configuration_file_resource_path"));
    }

    @Override
    public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)
            throws CacheException {
        net.sf.ehcache.Cache cache = cacheManager.getCache(regionName);
        if (cache == null) {
            cacheManager.addCache(regionName);
            cache = cacheManager.getCache(regionName);
        }
        return new EhCacheEntityRegion(cache, regionName, metadata);
    }
}

缓存操作流程

  1. 查询数据

    • Hibernate 首先会检查一级缓存,如果命中则直接返回。
    • 如果一级缓存未命中,再检查二级缓存。
    • 如果二级缓存也未命中,则查询数据库,并将结果分别放入一级缓存和二级缓存。
  2. 更新数据

    • 更新操作会使一级缓存中的相应实体无效。
    • 通知二级缓存更新或清除对应的缓存条目。

示例代码片段

以下是一个完整生命周期中对缓存的处理:

// 获取SessionFactory
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();

// 开始一个session
Session session = sessionFactory.openSession();
Transaction transaction = session.beginTransaction();

// 查询操作,首先检查一级缓存
MyEntity entity = session.get(MyEntity.class, 1);

// 保存操作,更新一级缓存
entity.setSomeField("newValue");
session.saveOrUpdate(entity);

// 提交事务,通知二级缓存同步
transaction.commit();
session.close();

// 在新的session中再次查询,检查二级缓存
Session newSession = sessionFactory.openSession();
MyEntity newEntity = newSession.get(MyEntity.class, 1);

newSession.close();

总结

Hibernate 的缓存机制通过一级缓存和二级缓存减少了直接访问数据库的次数,从而提高了应用程序的性能。一级缓存是会话级别的,默认启用;二级缓存需要显式配置,并允许跨会话共享缓存数据。理解这些缓存机制及其源码实现,有助于更好地优化和调试 Hibernate 应用程序。