二级缓存性能

Second level Cache Perfomance

最近我在研究一些改进我的代码的可能性,我对它做了一些修改。我安装了休眠配置以使用二级缓存,但这种改进的结果还不够好。我期待我的数据库查询时间很快。当我在实施后进行一些研究时,时间比我在代码中进行此修改之前的时间要长。即使我启用缓存查询,我的结果也不会改变。启用查询缓存后我看到的是更糟糕的时间结果。我可以看到的另一件事是,如果我的表与另一个表有某种关系,搜索时间会增加很多。

HibernateUtil

    package com.journaldev.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
public class HibernateUtil {

private static SessionFactory sessionFactory;

private static SessionFactory buildSessionFactory() {
    try {
        // Create the SessionFactory from hibernate.cfg.xml
        Configuration configuration = new Configuration();
        configuration.configure("hibernate.cfg.xml");
        System.out.println("Hibernate Configuration loaded");

        ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
        System.out.println("Hibernate serviceRegistry created");

        SessionFactory sessionFactoryLocal = configuration.buildSessionFactory(serviceRegistry);

        return sessionFactoryLocal;
    } catch (Throwable ex) {
        System.err.println("Initial SessionFactory creation failed." + ex);
        ex.printStackTrace();
        throw new ExceptionInInitializerError(ex);
    }
}

public static SessionFactory getSessionFactory() {
    if (sessionFactory == null) {
        sessionFactory = buildSessionFactory();
    }
    return sessionFactory;
}}

员工

package com.journaldev.hibernate.model;



    import javax.persistence.Column;
     import javax.persistence.Entity;
     import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private long id;

@Column(name = "emp_name")
private String name;

@Column(name = "emp_salary")
private double salary;



public Employee(String name) {
    this.name = name;
}

public Employee() {
}}

地址

    package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "ADDRESS")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Address {

    @Id
    @GeneratedValue
    private long id;

    @Column(name = "address_line1")
    private String addressLine1;

    @Column(name = "zipcode")
    private String zipcode;

    @Column(name = "city")
    private String city;


    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getAddressLine1() {
        return addressLine1;
    }

    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }

    public String getZipcode() {
        return zipcode;
    }

    public void setZipcode(String zipcode) {
        this.zipcode = zipcode;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }


}

员工关系地址

    package com.journaldev.hibernate.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;

@Entity
@Table(name = "EMPLOYEE")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region="employee")
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "emp_id")
    private long id;

    @Column(name = "emp_name")
    private String name;

    @Column(name = "emp_salary")
    private double salary;

    @OneToOne(mappedBy = "employee")
    @Cascade(value = org.hibernate.annotations.CascadeType.ALL)
    private Address address;

    public Employee(String name) {
        this.name = name;
    }

    public Employee() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

}

Class 测试

    package com.journaldev.hibernate.main;

import org.hibernate.Session;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
import java.util.Date;

public class HibernateEHCacheMain {

    public static void main(String[] args) {
        //insert10000();
        // System.out.println("Temp Dir:" + System.getProperty("java.io.tmpdir"));

        long init1 = new Date().getTime();
        list10000WithCache();
        long end1 = new Date().getTime();
        long result1 = end1 - init1;
        long init2 = new Date().getTime();
        list10000WithCache();
        long end2 = new Date().getTime();
        long result2 = end2 - init2;
        long init3 = new Date().getTime();
        list10000WithCache();
        //list10000WithoutCache();
        long end3 = new Date().getTime();
        long result3 = end3 - init3;
        System.err.println("Result 1 : " + result1 + "\nResult 2 :" + result2 + "\nResult 3 :" + result3);
        System.exit(1);
    }

    private static void insert10000() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        for (int i = 0; i < 10000; i++) {
            session.save(new Employee(i + ""));
            if (i % 20 == 0) {
                session.flush();
                session.clear();
            }
        }
        session.getTransaction().commit();
        session.close();
    }

    private static void list10000WithoutCache() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.createQuery("from Employee").list();
        session.getTransaction().commit();
        session.close();
    }

    private static void list10000WithCache() {
        Session session = HibernateUtil.getSessionFactory().openSession();
        session.beginTransaction();
        session.createQuery("from Employee").setCacheable(true).list();
        session.getTransaction().commit();
        session.close();
    }

}

Hibernate.cfg

    <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration SYSTEM "classpath://org/hibernate/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">pass</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/mydb</property>
        <property name="hibernate.connection.username">user</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.default_batch_fetch_size">20</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.show_sql">true</property>

        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

        <!-- For singleton factory -->
        <!-- <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</property>
        -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- enable second level cache and query cache -->
        <property name="hibernate.cache.use_second_level_cache">true</property>
        <property name="hibernate.cache.use_query_cache">true</property>
        <property name="net.sf.ehcache.configurationResourceName">/myehcache.xml</property>

        <mapping class="com.journaldev.hibernate.model.Employee" />
        <mapping class="com.journaldev.hibernate.model.Address" />
    </session-factory>
</hibernate-configuration>

myehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
    monitoring="autodetect" dynamicConfig="true">

    <diskStore path="java.io.tmpdir/ehcache" />

    <defaultCache maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" diskSpoolBufferSizeMB="30"
        maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU" statistics="true">
        <persistence strategy="localTempSwap" />
    </defaultCache>

    <cache name="employee" maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="5" timeToLiveSeconds="10">
        <persistence strategy="localTempSwap" />
    </cache>

    <cache name="org.hibernate.cache.internal.StandardQueryCache"
        maxEntriesLocalHeap="5" eternal="false" timeToLiveSeconds="120">
        <persistence strategy="localTempSwap" />
    </cache>

    <cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
        maxEntriesLocalHeap="5000" eternal="true">
        <persistence strategy="localTempSwap" />
    </cache>
</ehcache>

查询缓存如何工作?

查询缓存用于缓存查询结果。当查询缓存打开时,查询结果将根据组合查询和参数进行存储。每次触发查询时,缓存管理器都会检查参数和查询的组合。如果在缓存中找到结果,则返回它们,否则启动数据库事务。

为什么不应该使用查询缓存?

如您所见,如果查询具有多个参数,则缓存它不是一个好主意,因为单个参数可以采用多个值。对于这些组合中的每一个,结果都存储在存储器中。这会导致 大量使用内存。

在启用查询二级时有很多陷阱 caching.Please 通过这个 link

我在您的 configuration.All 中没有发现任何问题,您需要在 sessionfactory 上启用统计信息才能确定二级缓存是否正常工作。

Statistics statistics = sessionFactory.getStatistics(); statistics.setStatisticsEnabled(true);

您可以检查以下值

statistics.getEntityFetchCount()

statistics.getSecondLevelCacheHitCount()

statistics.getSecondLevelCachePutCount()

statistics.getSecondLevelCacheMissCount()

使用这个post 来精确解释二级缓存的统计信息。