緩存技術

聊聊緩存:

  • 緩存最終的目的是爲減輕服務端壓力,減小網絡傳輸請求 好比: 瀏覽器訪問自帶緩存。 App客戶端底層都有緩存技術的。
    注: (移動端登陸 用token 本地是有個文件的)

案例:java

若是一旦請求到服務器端以後,會在本地緩存一份,快速響應給用戶。mysql

常見的緩存方案:git

  • 網絡緩存
  • 代理緩存(Nginx能夠緩存)
  • CDN緩存
  • 服務器緩存
  • 數據庫緩存
  • 平臺緩存級緩存

總結: 緩存最終的目的是爲減輕服務端壓力,減小網絡傳輸請求github

單機緩存與分佈式緩存:

  • Session 是存放在服務器端(保存在單個JVM中),返回SessionId(響應頭),客戶端存放的SessionId,下次請求時候,直接使用對於的SessionId從服務器端查詢對應Sessionweb

  • 分佈式Session (Session共享的問題)----直接使用token解決redis


案例單機版: 實現單個JVM緩存框架, Session Key Value 能夠用Map集合實現 過時時間須要好好設計了 寫一個給予Map集合實現Jvm緩存框架:算法

首先定義map實現的緩存類:(concurrentHashmap保證安全)spring

緩存:sql

@Component  //這樣的話 就是單例的了!!!注入到容器裏面 
public class MapCache<K,V> {
    //存放緩存容器
   public ConcurrentHashMap<K, V> concurrentHashMap = new ConcurrentHashMap<K,V>();
    //純手寫單個JVM緩存框架 緩存概念偏向於臨時
    
   //對傳統的Map包裝
   public void put(K key,V value){
       concurrentHashMap.put(key, value);
   }
   //查詢
   public V get(K key){
       return concurrentHashMap.get(key);
   }
   
   public void remove(String k){
       //這個map是安全的 不須要加鎖!
       concurrentHashMap.remove(k);
   }
}

controller類:數據庫

@RestController
public class IndexController {
  
    @Autowired
    private MapCache<String, Object> mapCache;
    
    @RequestMapping("/get")
    public String get(String key){
        return (String)mapCache.get(key);
    }
    
    @RequestMapping("/put")
    public String put(String key, String value){
        mapCache.put(key, value);
        return "成功";
    }
    
    @RequestMapping("/remove")
    public String remove(String key){
        mapCache.remove(key);
        return "成功";
    }

}

啓動類

@SpringBootApplication(scanBasePackages={"com.toov5.*"})
public class app {
   public static void main(String[] args) {
    SpringApplication.run(app.class, args);
}
    
}

 

 

單機版緩存框架

  • 單點緩存框架 只能針對單個jvm中,緩存容器存放jvm中,每一個緩存互不影響 Ehcache gauva chache 內置緩存框架 jvm緩存框架

  • 分佈式緩存技術(共享緩存數據) Redis Meacache

example:

mybatis、hibernate底層都使用了Ehcache

本地緩存Ehcache 什麼是Ehcache  Ehcache是純java的開源緩存框架,具備快速、精幹等特色,是Hibernate中默認的CacheProvider。它主要面向通用緩存、Java EE和輕量級容器,具備內存和磁盤存儲、緩存加載器、緩存擴展、緩存異常處理程序。

Ehcache最初由Greg Luck於2003年開始開發。2009年,該項目被Terracotta購買。軟件仍然開源,但一些新的主要功能(例如,快速可重啓性之間的一致性的)只能在商業產品中使用。

Ehcache 被普遍用於在Hibernate、Spring、Cocoon等其餘開源系統。

Ehcache的主要特性 1.快速;

2.簡單;

3.多種緩存策略;

4.緩存數據有兩級:內存和磁盤,所以無需擔憂容量問題;

5.緩存數據會在虛擬機重啓的過程當中寫入磁盤;

6.能夠經過 RMI、可插入 API 等方式進行分佈式緩存;

7.具備緩存和緩存管理器的偵聽接口;(爲了作集羣)

8.支持多緩存管理器實例,以及一個實例的多個緩存區域;

9.提供 Hibernate 的緩存實現;

Ehcache使用介紹

Ehcache是用來管理緩存的一個工具,其緩存的數據能夠是存放在內存裏面的,也能夠是存放在硬盤上的。其核心是CacheManager,一切Ehcache的應用都是從CacheManager開始的。它是用來管理Cache(緩存)的,一個應用能夠有多個CacheManager,而一個CacheManager下又能夠有多個Cache。Cache內部保存的是一個個的Element,而一個Element中保存的是一個key和value的配對,至關於Map裏面的一個Entry。

  • Ehcache緩存過時策略 (操做系統裏面就有呀~)

  • 當緩存須要被清理時(好比空間佔用已經接近臨界值了),須要使用某種淘汰算法來決定清理掉哪些數據。經常使用的淘汰算法有下面幾種:

FIFO:First In First Out,先進先出。判斷被存儲的時間,離目前最遠的數據優先被淘汰。

LRU:Least Recently Used,最近最少使用。判斷最近被使用的時間,目前最遠的數據優先被淘汰。(默認)

LFU:Least Frequently Used,最不常用。在一段時間內,數據被使用次數最少的,優先被淘汰。

注意:

  • 緩存是容器存放在內存中,爲了保證持久化機制,將緩存中的值持久化到硬盤上(日誌緩存文件格式)。不然服務器重啓 就沒了哦!
  • 緩存框架都是支持對內存和硬盤支持
  • JVM內置緩存實現 (爲了減輕數據庫訪問壓力)
  • 內存吃不愁銷,限制而容量大小,會有相應的算法處理機制。

 


 

單機版緩存框架

  • 單點緩存框架 只能針對單個jvm中,緩存容器存放jvm中,每一個緩存互不影響 Ehcache gauva chache 內置緩存框架 jvm緩存框架

  • 分佈式緩存技術(共享緩存數據) Redis Meacache

example:

mybatis、hibernate底層都使用了Ehcache

本地緩存Ehcache 什麼是Ehcache  Ehcache是純java的開源緩存框架,具備快速、精幹等特色,是Hibernate中默認的CacheProvider。它主要面向通用緩存、Java EE和輕量級容器,具備內存和磁盤存儲、緩存加載器、緩存擴展、緩存異常處理程序。

Ehcache最初由Greg Luck於2003年開始開發。2009年,該項目被Terracotta購買。軟件仍然開源,但一些新的主要功能(例如,快速可重啓性之間的一致性的)只能在商業產品中使用。

Ehcache 被普遍用於在Hibernate、Spring、Cocoon等其餘開源系統。

Ehcache的主要特性 1.快速;

2.簡單;

3.多種緩存策略;

4.緩存數據有兩級:內存和磁盤,所以無需擔憂容量問題;

5.緩存數據會在虛擬機重啓的過程當中寫入磁盤;

6.能夠經過 RMI、可插入 API 等方式進行分佈式緩存;

7.具備緩存和緩存管理器的偵聽接口;(爲了作集羣)

8.支持多緩存管理器實例,以及一個實例的多個緩存區域;

9.提供 Hibernate 的緩存實現;

Ehcache使用介紹

Ehcache是用來管理緩存的一個工具,其緩存的數據能夠是存放在內存裏面的,也能夠是存放在硬盤上的。其核心是CacheManager,一切Ehcache的應用都是從CacheManager開始的。它是用來管理Cache(緩存)的,一個應用能夠有多個CacheManager,而一個CacheManager下又能夠有多個Cache。Cache內部保存的是一個個的Element,而一個Element中保存的是一個key和value的配對,至關於Map裏面的一個Entry。

  • Ehcache緩存過時策略 (操做系統裏面就有呀~)

  • 當緩存須要被清理時(好比空間佔用已經接近臨界值了),須要使用某種淘汰算法來決定清理掉哪些數據。經常使用的淘汰算法有下面幾種:

FIFO:First In First Out,先進先出。判斷被存儲的時間,離目前最遠的數據優先被淘汰。

LRU:Least Recently Used,最近最少使用。判斷最近被使用的時間,目前最遠的數據優先被淘汰。(默認)

LFU:Least Frequently Used,最不常用。在一段時間內,數據被使用次數最少的,優先被淘汰。

注意:

  • 緩存是容器存放在內存中,爲了保證持久化機制,將緩存中的值持久化到硬盤上(日誌緩存文件格式)。不然服務器重啓 就沒了哦!
  • 緩存框架都是支持對內存和硬盤支持
  • JVM內置緩存實現 (爲了減輕數據庫訪問壓力)
  • 內存吃不愁銷,限制而容量大小,會有相應的算法處理機制。

 

Spring Boot 整合EhCache: 

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.toov5.architect</groupId>
  <artifactId>architect</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RELEASE</version>
    </parent>
    <dependencies>
        <!-- SpringBoot 對lombok 支持 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- SpringBoot web 核心組件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </dependency>
        <!-- SpringBoot 外部tomcat支持 -->
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <!-- springboot-log4j -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j</artifactId>
            <version>1.3.8.RELEASE</version>
        </dependency>
        <!-- springboot-aop 技術 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
        </dependency>
        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>
        <!--開啓 cache 緩存 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <!-- ehcache緩存 -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.9.1</version><!--$NO-MVN-MAN-VER$ -->
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!-- mysql 依賴 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
    </dependencies>
  
</project>

實體類:

@Data
public class Users {
  private String name;
  private Integer age;
}

controller:

@RestController
public class IndexController {
      
    @Autowired
    private UserService userService;
    
    @RequestMapping("/getUser")
    public List<Users> getUser(Long id){
        return userService.getUser(id);
    }

}

Service

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;
    
   public List<Users> getUser(Long id){
       return userMapper.getUser(id);
   }
}

Mapper:

//引入的jar包後就有了這個註解了 很是好用 (配置緩存的基本信息)
@CacheConfig(cacheNames={"userCache"})  //緩存的名字  整個類的
public interface UserMapper {
    @Select("SELECT ID ,NAME,AGE FROM users where id=#{id}")
    @Cacheable //讓這個方法實現緩存 查詢完畢後 存入到緩存中  不是每一個方法都須要緩存呀!save()就不用了吧
    List<Users> getUser(@Param("id") Long id);
}

EhCache配置:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">
 
    <diskStore path="java.io.tmpdir/ehcache-rmi-4000" />
 
 
    <!-- 默認緩存 -->
    <defaultCache maxElementsInMemory="1000" eternal="true"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
        diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
        diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
   
    <!-- demo緩存 --><!-- name="userCache" 對應咱們在 @CacheConfig(cacheNames={"userCache"}) !!!!! -->
    <!--Ehcache底層也是用Map集合實現的 -->
    <cache name="userCache" maxElementsInMemory="1000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
        diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
        diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">  <!-- LRU緩存策略 -->
        <cacheEventListenerFactory
            class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
        <!-- 用於在初始化緩存,以及自動設置 -->
        <bootstrapCacheLoaderFactory
            class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" />
    </cache>
</ehcache>

yml:

###端口號配置
server:
  port: 8080
###數據庫配置 
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    test-while-idle: true
    test-on-borrow: true
    validation-query: SELECT 1 FROM DUAL
    time-between-eviction-runs-millis: 300000
    min-evictable-idle-time-millis: 1800000
# 緩存配置讀取
  cache:
    type: ehcache
    ehcache:
      config: classpath:app1_ehcache.xml 

啓動:

@EnableCaching //開啓緩存
@MapperScan(basePackages={"com.toov5.mapper"})
@SpringBootApplication(scanBasePackages={"com.toov5.*"})
public class app {
   public static void main(String[] args) {
    SpringApplication.run(app.class, args);
}
    
}

項目結構:

 

訪問:

 

備註介紹:【EhCache參數相關】

1. diskStore :指定數據(.data and .index)存儲位置,可指定磁盤中的文件夾位置期 The diskStore element is optional. It must be configured if you have overflowToDisk or diskPersistent enabled for any cache. If it is not configured, a warning will be issues and java.io.tmpdir will be used. 2. defaultCache 默認的管理策略 Ehcache 使用Map集合實現的 element 其實就是 key 和value 1、如下屬性是必須的:   1、name: Cache的名稱,必須是惟一的(ehcache會把這個cache放到HashMap裏)  2、maxElementsInMemory:在內存中緩存的element的最大數目。   3、maxElementsOnDisk:在磁盤上緩存的element的最大數目,默認值爲0,表示不限制。   4、eternal:設定緩存的elements是否永遠不過時。若是爲true,則緩存的數據始終有效,若是爲false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷。   5、overflowToDisk若是內存中數據超過內存限制,是否要緩存到磁盤上。 2、如下屬性是可選的:   1、timeToIdleSeconds對象空閒時間,指對象在多長時間沒有被訪問就會失效。只對eternalfalse的有效。默認值0,表示一直能夠訪問。   2、timeToLiveSeconds對象存活時間,指對象從建立到失效所須要的時間。只對eternalfalse的有效。默認值0,表示一直能夠訪問。   3、diskPersistent是否在磁盤上持久化。指重啓jvm後,數據是否有效。默認爲false  4、diskExpiryThreadIntervalSeconds對象檢測線程運行時間間隔。標識對象狀態的線程多長時間運行一次。   5、diskSpoolBufferSizeMB: DiskStore使用的磁盤大小,默認值30MB。每一個cache使用各自的DiskStore  6、memoryStoreEvictionPolicy若是內存中數據超過內存限制,向磁盤緩存時的策略。默認值LRU,可選FIFO、LFU


EhCache同步DB:
場景描述:

update 或者delete 容易形成緩存和DB不一樣步問題 

解決方案:

    • 主動通知或者定時Job
    • update語句後,加上通知,進行主動通知
    • 先修改爲功 而後清理緩存。【都是在同一個事務中的】
    • 定時Job健康檢查,檢查DB和緩存是否一致。而後決定執行清理緩存

 

@RestController
public class IndexController {
    @Autowired
    private CacheManager cacheManager;
    @Autowired
    private UserService userService;
    //注意引入的jar包是 org.springframework.cache.CacheManager;
    @RequestMapping("/remoKey")
    public void remoKey() {
        cacheManager.getCache("userCache").clear();  //傳入名字 進行清除
    }
    
    @RequestMapping("/getUser")
    public List<Users> getUser(Long id){
        return userService.getUser(id);
    }

}

1.訪問:

2.修改數據

3.手動清理緩存,接口調用

4.訪問:

 


 

分佈式緩存

EhCache+Redis實現分佈式緩存

Ehcache集羣模式介紹

因爲 EhCache 是進程中的緩存系統,一旦將應用部署在集羣環境中,每個節點維護各自的緩存數據,當某個節點對緩存數據進行更新,這些更新的數據沒法在其它節點中共享,這不只會下降節點運行的效率,並且會致使數據不一樣步的狀況發生。例如某個網站採用 A、B 兩個節點做爲集羣部署,當 A 節點的緩存更新後,而 B 節點緩存還沒有更新就可能出現用戶在瀏覽頁面的時候,一會是更新後的數據,一會是還沒有更新的數據,儘管咱們也能夠經過 Session Sticky 技術來將用戶鎖定在某個節點上,但對於一些交互性比較強或者是非 Web 方式的系統來講,Session Sticky 顯然不太適合。

EhCache經常使用集羣模式

EhCache從1.7版本開始,支持五種集羣方案,分別是:

  • Terracotta
  • RMI
RMi集羣模式 你如何知道集羣環境中的其餘緩存? 分佈式傳送的消息是什麼形式? 什麼狀況須要進行復制?增長(Puts),更新(Updates)或是失效(Expiries)? 採用什麼方式進行復制?同步仍是異步方式? 1、正確的元素類型:只有可序列化的元素能夠進行復制。一些操做,好比移除,只須要元素的鍵值而不用整個元素;在這樣的操做中即便元素不是可序列化的但鍵值是可序列化的也能夠被複制。 2、成員發現(Peer Discovery):Ehcache進行集羣的時候有一個cache組的概念。每一個cache都是其餘cache的一個peer,沒有主cache的存在。成員發現(Peer Discovery)正是用來解決 「你如何知道集羣環境中的其餘緩存?這個問題的。Ehcache提供了兩種機制用來進行成員發現,即:自動成員發現和手動成員發現。要使用一個內置的成員發現機制要在ehcache的配置文件中指定cacheManagerPeerProviderFactory元素的class屬性爲 net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory
  • JMS
  • JGroups
  • EhCache Server

Ehcache的使用場景

  • 使用純java的ehcache做爲本地緩存
  • Reids 做爲遠程分佈式緩存
  • 解決redis緩存壓力過大,提升緩存速度,以及緩存性能。

Redis和Ehcache緩存的區別

  • 若是是單個應用或者對緩存訪問要求很高的應用,用EhCache。
  • 若是是大型系統,存在緩存共享、分佈式部署、緩存內容很大的,建議用redis。
  • EHCache適合單體項目緩存,不適合於分佈式。(不支持共享),由於他們存放在JVM中,互不影響。
  • 分佈式緩存架構中,使用Redis+EHCache實現分佈式緩存。分佈式 一級、二級緩存。 好比: 若是Redis集羣 由於 某些緣由 所有宕機 。 客戶端會一直查詢數據庫!
  • Redis支持分佈式共享查詢

注: EhCache支持分佈式緩存 可是不推薦

EhCache實際使用場景總結:

  1. 咱們在項目中使用集中式緩存(Redis或者式Memcached等),一般都是檢查緩存中是否存在指望值的數據,若是存在直接返回,若是不存在就查詢數據庫讓後在將數據庫緩存,這個時候若是緩存系統由於某寫緣由宕機,形成服務沒法訪問,那麼大的量請求直接穿透到數據庫,最數據庫壓力很是大。這時候咱們讓ehcache做爲二級緩存,當redis服務器宕機後,能夠查詢ehcache緩存。 這樣可以有效的扛住服務器請求壓力。

分佈式緩存圖 :


Spring Boot2.0+Redis+Ehcache實現二級緩存

前言:

  • 先走本地,本地沒有再走網絡 儘可能少走Redis 效率會高一些
  • Ehchache 不須要走網絡,直接從內存中獲取.因爲Ehchache容器限制,會持久化在硬盤上。

實現:

  • spring boot中集成了spring cache,並有多種緩存方式的實現,如:Redis、Caffeine、JCache、EhCache等等。但若是隻用一種緩存,要麼會有較大的網絡消耗(如Redis),要麼就是內存佔用太大(如Caffeine這種應用內存緩存)。在不少場景下,能夠結合起來實現1、二級緩存的方式,可以很大程度提升應用的處理效率。

  • spring cache:主要包含spring cache定義的接口方法說明和註解中的屬性說明

補充: 緩存、兩級緩存

簡單的理解,緩存就是將數據從讀取較慢的介質上讀取出來放到讀取較快的介質上,如磁盤-->內存。平時咱們會將數據存儲到磁盤上,如:數據庫。若是每次都從數據庫裏去讀取,會由於磁盤自己的IO影響讀取速度,因此就有了像redis這種的內存緩存。能夠將數據讀取出來放到內存裏,這樣當須要獲取數據時,就可以直接從內存中拿到數據返回,可以很大程度的提升速度。可是通常redis是單獨部署成集羣,因此會有網絡IO上的消耗,雖然與redis集羣的連接已經有鏈接池這種工具,可是數據傳輸上也仍是會有必定消耗。因此就有了應用內緩存,如:caffeine。當應用內緩存有符合條件的數據時,就能夠直接使用,而不用經過網絡到redis中去獲取,這樣就造成了兩級緩存。應用內緩存叫作一級緩存,遠程緩存(如redis)叫作二級緩存 

spring boot + spring cache 實現兩級緩存(redis + caffeine)代碼實現:

 

實體類:

@Data
public class Users implements Serializable{
  private String name;
  private Integer age;
}

controller

@RestController
public class IndexController {
    @Autowired
    private UserService userService;
    
    @RequestMapping("/userId")
    public Users getUserId(Long id){
        return userService.getUser(id);  
    }

}

Service

@Component
public class EhCacheUtils {

    // @Autowired
    // private CacheManager cacheManager;
    @Autowired
    private EhCacheCacheManager ehCacheCacheManager;

    // 添加本地緩存 (相同的key 會直接覆蓋)
    public void put(String cacheName, String key, Object value) {
        Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName);
        Element element = new Element(key, value);
        cache.put(element);
    }

    // 獲取本地緩存
    public Object get(String cacheName, String key) {
        Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName);
        Element element = cache.get(key);
        return element == null ? null : element.getObjectValue();
    }

    public void remove(String cacheName, String key) {
        Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName);
        cache.remove(key);
    }

}

 

@Component
public class RedisService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    //這樣該方法支持多種數據類型 
    public void set(String key , Object object, Long time){
        //開啓事務權限
        stringRedisTemplate.setEnableTransactionSupport(true);
        try {
            //開啓事務
            stringRedisTemplate.multi();
            
            String argString =(String)object;  //強轉下
            stringRedisTemplate.opsForValue().set(key, argString);
            
            //成功就提交
            stringRedisTemplate.exec();
        } catch (Exception e) {
            //失敗了就回滾
            stringRedisTemplate.discard();
            
        }
        if (object instanceof String ) {  //判斷下是String類型不
            String argString =(String)object;  //強轉下
            //存放String類型的
            stringRedisTemplate.opsForValue().set(key, argString);
        }
        //若是存放Set類型
        if (object instanceof Set) {
            Set<String> valueSet =(Set<String>)object;
            for(String string:valueSet){
                stringRedisTemplate.opsForSet().add(key, string);  //此處點擊下源碼看下 第二個參數能夠放好多
            }
        }
        //設置有效期
        if (time != null) {
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS);
        }
        
    }
    //作個封裝
    public void setString(String key, Object object){
        String argString =(String)object;  //強轉下
        //存放String類型的
        stringRedisTemplate.opsForValue().set(key, argString);
    }
    public void setSet(String key, Object object){
        Set<String> valueSet =(Set<String>)object;
        for(String string:valueSet){
            stringRedisTemplate.opsForSet().add(key, string);  //此處點擊下源碼看下 第二個參數能夠放好多
        }
    }
    
    public String getString(String key){
     return    stringRedisTemplate.opsForValue().get(key);
    }
    
    
}

 

@Component
public class UserService {
    @Autowired
    private EhCacheUtils ehCacheUtils;
    @Autowired
    private RedisService redisService;
    @Autowired
    private UserMapper userMapper;
    //定義個全局的cache名字
    private String cachename ="userCache";
    
    public Users getUser(Long id){
        //先查詢一級緩存  key以當前的類名+方法名+id+參數值
        String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName()
                + "-id:" + id;
        //查詢一級緩存數據有對應值的存在 若是有 返回
        Users user = (Users)ehCacheUtils.get(cachename, key);
        if (user != null) {
            System.out.println("key"+key+",直接從一級緩存獲取數據"+user.toString());
            return user;
        }
        //一級緩存沒有對應的值存在,接着查詢二級緩存    
        // redis存對象的方式  json格式 而後反序列號
        String userJson = redisService.getString(key);
        //若是rdis緩存中有這個對應的值,修改一級緩存    最下面的會有的 相同會覆蓋的    
        if (!StringUtil.isNullOrEmpty(userJson)) {  //有 轉成json
            JSONObject jsonObject = new JSONObject();//用的fastjson
            Users resultUser = jsonObject.parseObject(userJson,Users.class);
            ehCacheUtils.put(cachename, key, resultUser);
            return resultUser;
        }
        //都沒有 查詢DB 
        Users user1 = userMapper.getUser(id);
        if (user1 == null) {
            return null;
        }
        //存放到二級緩存 redis中
        redisService.setString(key, new JSONObject().toJSONString(user1));
        //存放到一級緩存 Ehchache
        ehCacheUtils.put(cachename, key, user1);
        return user1;
    }
}

啓動類

@EnableCaching //開啓緩存
@MapperScan(basePackages={"com.toov5.mapper"})
@SpringBootApplication(scanBasePackages={"com.toov5.*"})
public class app {
   public static void main(String[] args) {
    SpringApplication.run(app.class, args);
}
    
}

app1.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">
 
    <diskStore path="java.io.tmpdir/ehcache-rmi-4000" />
 
 
    <!-- 默認緩存 -->
    <defaultCache maxElementsInMemory="1000" eternal="true"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
        diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
        diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">
    </defaultCache>
   
    <!-- demo緩存 --><!-- name="userCache" 對應咱們在 @CacheConfig(cacheNames={"userCache"}) !!!!! -->
    <!--Ehcache底層也是用Map集合實現的 -->
    <cache name="userCache" maxElementsInMemory="1000" eternal="false"
        timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
        diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
        diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
        memoryStoreEvictionPolicy="LRU">  <!-- LRU緩存策略 -->
        <cacheEventListenerFactory
            class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
        <!-- 用於在初始化緩存,以及自動設置 -->
        <bootstrapCacheLoaderFactory
            class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" />
    </cache>
</ehcache>

 

yml:

###端口號配置
server:
  port: 8080
###數據庫配置 
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    test-while-idle: true
    test-on-borrow: true
    validation-query: SELECT 1 FROM DUAL
    time-between-eviction-runs-millis: 300000
    min-evictable-idle-time-millis: 1800000
# 緩存配置讀取
  cache:
    type: ehcache
    ehcache:
      config: classpath:app1_ehcache.xml
 
  redis:
    database: 0  
    host:  192.168.91.3
    port:  6379
    password:  123
    jedis:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0
    timeout: 10000

運行結果 

後面的訪問,在控制檯打印: 

代碼必定要注意:

  • 一級緩存過時時間, 比二級要短一些。
  • 這裏的兩級緩存時間問題,執行設置二級緩存時候須要時間的,因此這兩個時間設置問題必定要注意了。以下圖: 

分佈式緩存的思考:

  • 應用緩存: 緩存命中率、緩存回收策略 Java緩存類型、磁盤緩存
  • Http緩存: Http客戶端緩存、 NginxHttp緩存設置、Nginx代理緩存
  • 多級緩存: 緩存數據、 分佈式緩存與負載均衡、熱點數據與更新緩存、緩存崩潰與快速修復
相關文章
相關標籤/搜索