一:二級緩存簡介
為什么需要緩存:
查詢大量數據時更快,拉高程序的性能
什么樣的數據需要緩存:
很少被修改或根本不改的數據 數據字典
業務場景比如:耗時較高的統計分析sql、電話賬單查詢sql等
關系型數據庫:
數據與數據之間存在關系(聯系)的數據庫 mysql/Oracle、sqlserver
非關系型數據庫(數據與數據之間是不存在關系的,key-value):
- 基於文件存儲的數據庫:ehcache
- 基於內存存儲的數據庫:redis、memcache
- 基於文檔存儲的數據庫:mongodb
ehcache是什么:
Ehcache 是現在最流行的純Java開源緩存框架,配置簡單、結構清晰、功能強大
我們來用map簡單測試一下緩存原理:
package com.ht.six.test; import java.util.HashMap; import java.util.Map; /** * 利用map集合簡易實現緩存原理 * @author Administrator * */ public class EhcacheDemo1 { static Map<String, Object> cache = new HashMap<String, Object>(); static Object getValue(String key) { Object value = cache.get(key); System.out.println("從緩存中獲取數據。。。。"); if(value == null) { System.out.println("從軟件相對應的配置文件中獲取數據。。。"); cache.put(key, new String[] {"zs"}); return cache.get(key); } return value; } /** * 1.從緩存中獲取數據。。。。 * 2.從軟件相對應的配置文件中獲取數據。。。 * 3."zs" * 4.從緩存中獲取數據。。。。 * 5."zs" * @param args */ public static void main(String[] args) { System.out.println(getValue("sname")); System.out.println(getValue("sname")); } }
運行原理圖解:
我們來看一下運行結果:
ehcache簡介
ehcache特點:
-
夠快
Ehcache的發行有一段時長了,經過幾年的努力和不計其數的性能測試,Ehcache終被設計於large, high concurrency systems.
-
夠簡單
開發者提供的接口非常簡單明了,從Ehcache的搭建到運用運行僅僅需要的是你寶貴的幾分鍾。其實很多開發者都不知道自己用在用Ehcache,Ehcache被廣泛的運用於其他的開源項目
-
夠小
關於這點的特性,官方給了一個很可愛的名字small foot print ,一般Ehcache的發布版本不會到2M,V 2.2.3 才 668KB。
-
夠輕量
核心程序僅僅依賴slf4j這一個包,沒有之一!
-
好擴展
Ehcache提供了對大數據的內存和硬盤的存儲,最近版本允許多實例、保存對象高靈活性、提供LRU、LFU、FIFO淘汰算法,基礎屬性支持熱配置、支持的插件多
-
監聽器
緩存管理器監聽器 (CacheManagerListener)和 緩存監聽器(CacheEvenListener),做一些統計或數據一致性廣播挺好用的
-
分布式緩存
從Ehcache 1.2開始,支持高性能的分布式緩存,兼具靈活性和擴展性
核心接口:
- CacheManager:緩存管理器
- Cache:緩存對象,緩存管理器內可以放置若干cache,存放數據的實質,所有cache都實現了Ehcache接口
- Element:單條緩存數據的組成單位
ehcache的使用
首先我們在POM.XML中導入相關依賴
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <junit.version>4.12</junit.version> <servlet.version>4.0.0</servlet.version> <hibernate.version>5.2.12.Final</hibernate.version> <mysql.driver.version>5.1.46</mysql.driver.version> <ehcache.version>2.10.0</ehcache.version> <slf4j-api.version>1.7.7</slf4j-api.version> <log4j-api.version>2.9.1</log4j-api.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${servlet.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate.version}</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.driver.version}</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>${ehcache.version}</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-ehcache</artifactId> <version>${hibernate.version}</version> </dependency> <!-- slf4j核心包 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j-api.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> <version>${slf4j-api.version}</version> <scope>runtime</scope> </dependency> <!--用於與slf4j保持橋接 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>${log4j-api.version}</version> </dependency> <!--核心log4j2jar包 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>${log4j-api.version}</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>${log4j-api.version}</version> </dependency> </dependencies>
用二級緩存來演示存儲數據
導入相關配置文件
ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!--磁盤存儲:將緩存中暫時不使用的對象,轉移到硬盤,類似於Windows系統的虛擬內存--> <!--path:指定在硬盤上存儲對象的路徑--> <!--java.io.tmpdir 是默認的臨時文件路徑。 可以通過如下方式打印出具體的文件路徑 System.out.println(System.getProperty("java.io.tmpdir"));--> <diskStore path="D://xxx"/> <!--defaultCache:默認的管理策略--> <!--eternal:設定緩存的elements是否永遠不過期。如果為true,則緩存的數據始終有效,如果為false那么還要根據timeToIdleSeconds,timeToLiveSeconds判斷--> <!--maxElementsInMemory:在內存中緩存的element的最大數目--> <!--overflowToDisk:如果內存中數據超過內存限制,是否要緩存到磁盤上--> <!--diskPersistent:是否在磁盤上持久化。指重啟jvm后,數據是否有效。默認為false--> <!--timeToIdleSeconds:對象空閑時間(單位:秒),指對象在多長時間沒有被訪問就會失效。只對eternal為false的有效。默認值0,表示一直可以訪問--> <!--timeToLiveSeconds:對象存活時間(單位:秒),指對象從創建到失效所需要的時間。只對eternal為false的有效。默認值0,表示一直可以訪問--> <!--memoryStoreEvictionPolicy:緩存的3 種清空策略--> <!--FIFO:first in first out (先進先出)--> <!--LFU:Less Frequently Used (最少使用).意思是一直以來最少被使用的。緩存的元素有一個hit 屬性,hit 值最小的將會被清出緩存--> <!--LRU:Least Recently Used(最近最少使用). (ehcache 默認值).緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存--> <defaultCache eternal="false" maxElementsInMemory="1000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LRU"/> </ehcache>
EhcacheUtil類
package com.ht.six.util; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import java.io.InputStream; public class EhcacheUtil { private static CacheManager cacheManager; static { try { InputStream is = EhcacheUtil.class.getResourceAsStream("/ehcache.xml"); cacheManager = CacheManager.create(is); } catch (Exception e) { throw new RuntimeException(e); } } private EhcacheUtil() { } public static void put(String cacheName, Object key, Object value) { Cache cache = cacheManager.getCache(cacheName); if (null == cache) { //以默認配置添加一個名叫cacheName的Cache cacheManager.addCache(cacheName); cache = cacheManager.getCache(cacheName); } cache.put(new Element(key, value)); } public static Object get(String cacheName, Object key) { Cache cache = cacheManager.getCache(cacheName); Element element = cache.get(key); return null == element ? null : element.getValue(); } public static void remove(String cacheName, Object key) { Cache cache = cacheManager.getCache(cacheName); cache.remove(key); } }
測試類:
package com.ht.six.test; import com.ht.six.util.EhcacheUtil; /** * 演示利用緩存存儲數據 * @author Administrator * */ public class EhcacheDemo2 { public static void main(String[] args) { System.out.println(System.getProperty("java.io.tmpdir")); EhcacheUtil.put("com.ht.four.entity.Book", 11, "zhangsan"); System.out.println(EhcacheUtil.get("com.ht.four.entity.Book", 11)); } }
若是自己沒有去配置槽中的內容,管理器一般就是自動規划為默認配置,不會存儲到硬盤里面去
什么是槽?由下面的圖告訴你cache的內部結構
把槽中數據修改一下測試一下
<!--name: Cache的名稱,必須是唯一的(ehcache會把這個cache放到HashMap里)--> <cache name="com.ht.one.entity.User" eternal="false" maxElementsInMemory="100" overflowToDisk="true" diskPersistent="true" timeToIdleSeconds="0" timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU"/>
測試結果中,我們在D盤中發現存儲進去的文件:
hibernate中使用二級緩存(ehcache)
-
依賴包已經在上面全部導入 所以這里就不重復了
hibernate一級緩存使用
/** * 默認情況下,sql語句形成了三次,這里為了提高性能,必須使用二級緩存SessionFactory緩存 * <!-- 開啟二級緩存 --> * <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 開啟查詢緩存 --> <property name="hibernate.cache.use_query_cache">true</property> <!-- EhCache驅動 --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property> 映射文件中添加標簽 <cache usage="read-write" region="com.javaxl.one.entity.User"/> 這里的region指的是Ehcache.xml中cacheName * @param args */ public static void main(String[] args) { UserDao userDao = new UserDao(); User u = new User(); u.setId(7); User user = userDao.get(u); System.out.println(user); User user2 = userDao.get(u); System.out.println(user2); User user3 = userDao.get(u); System.out.println(user3); }
因為一個session中只生產一個sql語句,所以這里用了多個session,要是小數據還可行,大量數據就大大降低性能,那么就要用到二級緩存了。
-
hibernate.cfg.xml中添加二級緩存相關配置
<!-- 開啟二級緩存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 開啟查詢緩存 --> <property name="hibernate.cache.use_query_cache">true</property> <!-- EhCache驅動 --> <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.internal.EhCacheRegionFactory</property>
-
對應的映射文件中添加緩存配置 User.hbm.xml
<!-- 添加緩存標號 指定槽 --> <cache usage="read-write" region="com.ht.one.entity.User"/>
-
調用 測試方法
/** * 同一個session,sql語句只生成一次,這里用到了一級緩存 * * session級別的緩存就是一級緩存 * sessionfactory級別的緩存就是二級緩存 * 默認的一級緩存開啟的 */ public static void test1() { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); User user = session.get(User.class, 7); System.out.println(user); User user2 = session.get(User.class, 7); System.out.println(user2); User user3 = session.get(User.class, 7); System.out.println(user3); transaction.commit(); session.close(); }
EhcacheDemo4
package com.ht.six.test; import java.util.List; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.query.Query; import com.ht.two.util.SessionFactoryUtils; /** * hibernate二級緩存不會同時緩存多條數據 * @author Administrator * */ public class EhcacheDemo4 { public static void main(String[] args) { Session session = SessionFactoryUtils.openSession(); Transaction transaction = session.beginTransaction(); Query query = session.createQuery("from User"); query.setCacheable(true); List list = query.list(); System.out.println(list); List list2 = query.list(); System.out.println(list2); List list3 = query.list(); System.out.println(list3); transaction.commit(); session.close(); } }
發現他也執行多次
原因:對於查詢單條數據的時候,緩存機制是默認用一級緩存的,若是想要用二級緩存來提高性能,還得靠自己手動開啟二級緩存
query.setCacheable(true);