繁体   English   中英

何时以及如何使用 hibernate 二级缓存?

[英]When and how to use hibernate second level cache?

我无法理解 hibernate 何时命中二级缓存以及何时使缓存无效。

这是我目前的理解:

  • 二级缓存存储会话之间的实体,scope是SessionFactory
  • 您必须告诉要缓存哪些实体,默认情况下不会缓存任何实体
  • 查询缓存将查询结果存储在缓存中。

我不明白的是

  • hibernate 什么时候命中这个缓存?
  • 假设我已经设置了二级缓存但没有设置查询缓存。 我想缓存我的客户,其中有 50000 个。 我可以通过哪些方式从缓存中检索客户?
  • 我假设我可以通过缓存中的 id 获取它们。 这很容易,但也不值得缓存。 但是,如果我想对所有客户进行一些计算怎么办? 假设我想显示客户列表,那么我将如何访问他们?
  • 如果查询缓存被禁用,我将如何获得所有客户?
  • 如果有人更新了其中一个客户会怎样?
    • 该客户会在缓存中失效还是所有客户都会失效?

还是我认为缓存完全错误? 在那种情况下,二级缓存的更合适用途是什么? hibernate 文档根本不清楚缓存在现实中是如何工作的。 只有关于如何设置它的说明。

更新:所以我开始明白二级缓存(没有查询缓存)对于通过 id 加载数据很有用。 例如,我有用户 object,我想检查 web 应用程序中每个请求的权限。 这是通过在二级缓存中缓存用户来减少数据库访问的好案例吗? 就像我将用户 ID 存储在 session 中一样,或者当我需要检查权限的任何地方时,我会通过它的 ID 加载用户并检查权限。

首先,让我们谈谈进程级缓存(或在 Hibernate 中称之为二级缓存)。 为了使它工作,你应该

  1. 配置缓存提供程序
  2. 告诉 hibernate 要缓存哪些实体(如果使用这种映射,则在 hbm.xml 文件中)。

您告诉缓存提供者它应该存储多少对象以及何时/为什么应该使它们无效。 因此,假设您有 Book 和 Author 实体,每次从 DB 中获取它们时,只会从实际 DB 中选择那些不在缓存中的实体。 这显着提高了性能。 在以下情况下很有用:

  • 您仅通过 Hibernate 写入数据库(因为它需要一种方法来知道何时更改或使缓存中的实体无效)
  • 你经常阅读物品
  • 您只有一个节点,并且没有复制。 否则,您将需要复制缓存本身(使用 JGroups 之类的分布式缓存),这会增加更多复杂性,并且它的扩展性不如无共享应用程序。

那么缓存什么时候起作用呢?

  • 当您session.get()session.load()时,之前选择并驻留在缓存中的 object。 缓存是一种存储,其中 ID 是键,属性是值。 因此,只有当有可能通过 ID 进行搜索时,您才能消除对数据库的访问。
  • 当您的关联是延迟加载时(或使用选择而不是连接进行急切加载)

但它在以下情况下不起作用:

  • 如果你没有 select 的 ID。 再次 - 二级缓存将实体 ID 的 map 存储到其他属性(它实际上并不存储对象,而是数据本身),因此如果您的查找如下所示: from Authors where name =:name ,那么您不需要t命中缓存。
  • 当您使用 HQL 时(即使您使用where id =? )。
  • 如果在您的映射中设置fetch="join" ,这意味着加载关联连接将在任何地方使用,而不是单独的 select 语句。 进程级缓存仅在使用fetch="select"时才对子对象起作用。
  • 即使您有fetch="select"但是在 HQL 中您使用连接到 select 关联 - 这些连接将立即发出,它们将覆盖您在 hbm.xml 或注释中指定的任何内容。

现在,关于查询缓存。 您应该注意,它不是一个单独的缓存,它是对进程级缓存的补充。 假设您有一个 Country 实体。 它是 static,所以你知道当你说from Country时,每次都会有相同的结果集。 这是查询缓存的完美候选者,它本身会存储一个ID列表,当您下次使用 select 所有国家/地区时,它会将此列表返回到进程级缓存,而后者又会返回每个 ID 的对象因为这些对象已经存储在二级缓存中。 每次与实体相关的任何内容发生更改时,查询缓存都会失效。 因此,假设您from Authors配置为放入查询缓存中。 由于作者经常更改,因此它不会有效。 因此,您应该只对更多或更少的 static 数据使用查询缓存。

  • 二级缓存是键值存储。 它仅在您通过 id 获取实体时才有效
  • 当通过 hibernate 更新/删除实体时,每个实体的二级缓存无效/更新。 如果数据库以不同的方式更新,它不会失效。
  • 对于查询(例如客户列表),使用查询缓存。

实际上,拥有一个键值对分布式缓存很有用——这就是 memcached,它为 facebook、twitter 等提供支持。 但是,如果您没有按 id 进行查找,那么它不会很有用。

迟到了,但想系统地回答许多开发人员提出的这些问题。

在这里一一回答你的问题是我的答案。

Q. hibernate 什么时候命中这个缓存?

A.一级缓存Session object相关联。 二级缓存Session 工厂 object相关联。 如果第一级没有找到object,则检查第二级。

问:假设我设置了二级缓存,但没有设置查询缓存。 我想缓存我的客户,他们有 50000 个。 我可以通过哪些方式从缓存中检索客户?

答:您在更新中得到了答案。 此外,查询缓存仅存储 object 的 ID 列表,而这些对象 w.r.t 的 ID 存储在相同的二级缓存中。 因此,如果您启用查询缓存,您将使用相同的资源。 整齐吧?

问:我假设我可以通过 id 从缓存中获取它们。 这很容易,但也不值得缓存。 但是,如果我想对所有客户进行一些计算怎么办。 假设我想显示客户列表,那么我将如何访问他们?

A. 以上回答。

问:如果禁用查询缓存,我将如何获得所有客户?

A. 以上回答。

问:如果有人更新了其中一位客户会发生什么? 该客户会在缓存中失效还是所有客户都会失效?

A. Hibernate 不知道,但您可以使用其他第三方 IMDG / 分布式缓存来实现hibernate 二级缓存并使它们失效。 例如TayzGrid就是这样一种产品,我猜还有更多。

Hibernate 二级缓存理解和实现有点棘手。 根据您的问题,我们可以说以下内容:

Hibernate 什么时候命中这个缓存?

正如您所建议的,仅在 L1 缓存之后才查询 Hibernate L2 缓存(如果启用;默认情况下未打开)。 这是一个键值缓存,其数据跨多个会话保留。

假设我设置了二级缓存,但没有设置查询缓存。 我想缓存我的客户,他们有 50000 个。 我可以通过哪些方式从缓存中检索客户?

查询缓存最适合此用例,因为客户数据是 static 并从关系数据库中检索。

如果有人更新了其中一位客户会发生什么? 该客户会在缓存中失效还是所有客户都会失效?

这取决于您使用的特定 Hibernate 缓存策略。 Hibernate 实际上有四种不同的缓存策略:

READ_ONLY :对象在缓存中一次不会更改。

NONSTRICT_READ_WRITE :对象在相应的数据库条目更新后(最终)发生变化; 这保证了最终的一致性。

READ_WRITE :对象在相应的数据库条目更新后(立即)发生变化; 这通过使用“软”锁保证了强一致性。

TRANSACTIONAL :使用分布式 XA 事务更改对象,确保数据完整性; 这可以保证完全成功或回滚所有更改。 但是,在所有这四种情况下,更新单个数据库条目不会使缓存中的整个客户列表无效。 Hibernate 比这聪明一点:)

To learn more about how L2 caching works in Hibernate, you can check out the article “What is the Hibernate L2 cache,” or the in-depth article Caching in Hibernate with Redis

我的工作二级缓存代码。

hibernate.cfg.xml

<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/practice</property>
        <property name="connection.user">root</property>
        <property name="connection.password">Welcome123#</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">update</property>

        <property name="cache.use_second_level_cache">true</property>
        <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
        <property name="cache.ehcache.missing_cache_strategy">create</property>

        <mapping class="com.oracle.dto.BankAccount"/>
        <mapping class="com.oracle.dto.Citizen"/>



    </session-factory>
</hibernate-configuration>

package com.oracle.dto;

import lombok.AccessLevel;
import lombok.Data;
import lombok.Setter;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import javax.persistence.*;
import java.util.Date;

@Entity
@Data
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class BankAccount {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO,generator = "bid")
    @SequenceGenerator(name = "bid",sequenceName = "b_id",initialValue = 2000,allocationSize = 1)
    @Setter(AccessLevel.NONE)
    @Column(name="bank_id",updatable = false,nullable = false)
    private Long bId;
    private Integer branchCode;
    private Double accountBalance;
    @Temporal(TemporalType.DATE)
    @Column(name="account_opening_date",updatable = false,nullable = false)
    private Date accountOpeningDate;
}

主 Class

package com.oracle.main;

import com.oracle.dto.BankAccount;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class CacheDriver {
    public static void main(String[] args) {
        SessionFactory sf=new Configuration().configure().buildSessionFactory();
        Session session1 = sf.openSession();
        BankAccount account1 = session1.get(BankAccount.class,2000l);
        System.out.println(account1);
        session1.close();
        Session session2 = sf.openSession();
        BankAccount account2 = session2.get(BankAccount.class,2000l);
        System.out.println(account2);
        session1.close();
        sf.close();
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM