天天看點

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

1.利用Redis緩存實作商品分類查詢

1.1 編輯ItemCatController

(1)

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

(2)

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

(3)

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題
百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

2.AOP案例說明

以上功能寫法代碼耦合性太高了,如果将來出現一個比redis更好的插件,那修改的時候就要把含有redis的所有地方都修改了;這樣就不科學了,是以引入AOP,降低程式的耦合性

2.1 業務描述

說明:由于将代碼直接寫Service業務層中,會導緻代碼的耦合性高,不便于擴充,為了實作代碼的松耦合可以利用AOP

2.2AOP知識回顧

2.2.1 什麼是AOP

2.2.2 通知方法

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

2.2.3 切入點表達式

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

2.2.4 AOP入門

package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component  //1.我是一個javaBean
@Aspect     //2.我是一個切面
public class CacheAOP {
    //1.定義切入點表達式
    @Pointcut("bean(itemCatServiceImpl)") //隻攔截xxx類中的方法
    public void pointCut(){

    }
    /**
     * 2.定義通知方法
     * 需求:
     *  1.想擷取目标方法名稱
     *  2.擷取目标方法對象
     *  3.擷取使用者傳遞的參數
    */
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("我是前置通知");
        //1.擷取類名稱
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        //2.擷取對象
        Object target = joinPoint.getTarget();
        //3.擷取參數
        Object[] objs = joinPoint.getArgs();
        System.out.println("類名名稱:"+className);
        System.out.println("方法名稱:"+methodName);
        System.out.println("對象名稱:"+target);
        System.out.println("方法參數:"+objs);
    }
}
           

2.3 AOP實作緩存業務

2.3.1 自定義注解@CacheFind

說明:該注解由于使用的業務較多,是以将改注解寫入Common中.

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題
package com.jt.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)         //注解在方法中使用
@Retention(RetentionPolicy.RUNTIME) //運作期有效
public @interface CacheFind {

    String key();              //1.設定key 使用者自己設定
    int seconds() default  0;  //2.可以指定逾時時間,也可以不指定.
}
           

2.3.2 使用自定義注解

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

2.3.3 切換代碼執行

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

2.3.4 利用AOP實作緩存業務

package com.jt.aop;

import com.jt.anno.CacheFind;
import com.jt.pojo.ItemCat;
import com.jt.unit.ObjectMapperUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;

import java.util.Arrays;
import java.util.List;

@Component  //1.我是一個javaBean
@Aspect     //2.我是一個切面
public class CacheAOP {

    //引入redis緩存配置
    @Autowired
    private Jedis jedis;


    /**
     * AOP緩存實作的業務政策
     * 1.切入點表達式應該攔截  @CacheFind注解
     * 2.通知方法: 環繞通知
     * 注意事項:  如果使用環繞通知,則必須在第一個參數的位置添加 ProceedingJoinPoint
     *
     * 動态擷取注解參數的步驟:
     *  [email protected](cacheFind)   切入點表達式要求攔截一個類型為cacheFind注解.
     *  2.并且利用連接配接點為參數中的cacheFind指派.
     * */

    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind){
        try {
            Object result = null;
            //1.如何動态擷取注解中的資料
            String prekey = cacheFind.key();
            //2.動态擷取方法中的參數  将數組轉化為字元串
            String args = Arrays.toString(joinPoint.getArgs());
            String key = prekey + "::" + args;
            //3.檢驗redis中是否有資料
            if(jedis.exists(key)){
                //有緩存  從redis緩存中擷取json 之後還原對象傳回
                String json = jedis.get(key);
                //target代表這目标方法的傳回值類型......
                //動态擷取目标方法的傳回值類型??   向上造型 不用強轉   向下造型
                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
               Class returnClass = methodSignature.getReturnType();
                //将json資料轉化為對象
                result = ObjectMapperUtil.toObject(json, returnClass);
                System.out.println("AOP實作緩存查詢!!!!");

            }else{
                //第一次查詢資料庫.
                result = joinPoint.proceed();    //執行目标方法.
                System.out.println("AOP執行資料庫操作");
                //2.将資料儲存到redis中
                String json = ObjectMapperUtil.toJSON(result);
                if(cacheFind.seconds()>0) //表示需要設定逾時時間
                    jedis.setex(key, cacheFind.seconds(), json);
                else
                    //不需要逾時
                    jedis.set(key, json);
            }
            return result;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            throw new RuntimeException(throwable);  //将檢查異常,轉化為運作時異常
        }
    }



  /*  //1.定義切入點表達式
    @Pointcut("bean(itemCatServiceImpl)") //隻攔截xxx類中的方法
    public void pointCut(){

    }*/

    /**
     * 2.定義通知方法
     * 需求:
     *  1.想擷取目标方法名稱
     *  2.擷取目标方法對象
     *  3.擷取使用者傳遞的參數
    */

   /* @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        System.out.println("我是前置通知");
        //1.擷取類名稱
        String className = joinPoint.getSignature().getDeclaringTypeName();
        String methodName = joinPoint.getSignature().getName();
        //2.擷取對象
        Object target = joinPoint.getTarget();
        //3.擷取參數
        Object[] objs = joinPoint.getArgs();
        System.out.println("類名名稱:"+className);
        System.out.println("方法名稱:"+methodName);
        System.out.println("對象名稱:"+target);
        System.out.println("方法參數:"+objs);
    }*/
}
           

3. redis常見面試題

3.1 緩存穿透

特點: 使用者高并發環境下,通路資料庫中根本不存在的資料.

影響:由于使用者高并發通路,資料庫每次查詢都是白忙活一場,則資料庫可能存在當機的風險.

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

3.2 緩存擊穿

說明: 由于使用者高并發的通路. 通路的資料剛開始有緩存,但是由于特殊原有 導緻緩存失效.(資料’‘單個’’)

(資料‘單個’表示少量資料的意思)

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

解決方案:

使用多個redis,即如圖串聯兩個redis,當使用者通路第一個redisA的時候沒有資料,就去通路第二個redis B,如果有資料就直接由第二個redis傳回資料,(前提是要設定兩個redis緩存清除有一定的時間差,即保證兩個redis緩存不在同一時間清除,也就是緩存的逾時時間要設定不一樣),這樣資料通路的時候,至少有一個redis中有資料;如果兩個redis都沒有資料,進而才通路資料庫

3.3緩存雪崩

說明: 由于高并發的環境下.大量的使用者通路伺服器. redis中有大量的資料在同一時間逾時(删除).

解決方案:(設定兩個redis)不要同一時間删除資料.

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

3.4 Redis持久化問題

3.4.1 問題說明

說明:Redis中的資料都儲存在記憶體中.如果服務關閉或者當機則記憶體資源直接丢失.導緻緩存失效.

這就引入了redis持久化的問題!!!

3.4.2 持久化原理說明

說明:Redis中有自己的持久化政策.Redis啟動時根據配置檔案中指定的持久化方式進行持久化操作.

Redis中持久化的方式有兩種:

(1)RDB模式 (2)AOF模式

Redis中預設的持久化的方式為RDB模式.

3.4.3 RDB模式

特點說明:

1.RDB模式采用定期持久化的方式. 風險:可能丢失資料.

2.RDB模式記錄的是目前Redis的記憶體記錄快照. 隻記錄目前狀态. 持久化效率最高的

3.RDB模式是預設的持久化方式.

持久化指令:

指令1: save 同步操作. 要求記錄馬上持久化. 可能對現有的操作造成阻塞

名來2: bgsave 異步操作. 開啟單獨的線程實作持久化任務.

持久化周期:

save 900 :1 在900秒内,如果執行一次更新操作,則持久化一次.

save 300 : 10 在300秒内,如果執行10次更新操作,則持久化一次.

save 60 : 10000 在60秒内,如果執行10000次更新操作,則持久化一次.

save 1 1 :???不可以 容易阻塞 性能太低.不建議使用.

使用者操作越頻繁,則持久化周期越短.

3.4.4 AOF模式

特點:

1.AOF模式預設是關閉狀态 如果需要則手動開啟.

2.AOF能夠記錄程式的執行過程 可以實作資料的實時持久化. AOF檔案占用的空間較大.回複資料的速度較慢.

3.AOF模式開啟之後.RDB模式将不生效.

AOF配置:

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

持久化周期配置:

appendfsync always 實時持久化.

appendfsync everysec 每秒持久化一次 略低于rdb模式

appendfsync no 自己不主動持久化(被動:由作業系統解決)

3.4.5 redis中如何選擇持久化方式

思路: 如果允許資料少量的丢失,則首選RDB.(快),如果不允許資料丢失則使用AOF模式.

3.4.6 情景題

小張在雙11前夜誤操作将Redis伺服器執行了flushAll指令. 問項目經理應該如何解決??

A: 痛批一頓 ,讓其送出離職申請.

B: 批評教育, 讓其深刻檢討,并且請主管 捏腳.

C:項目經理快速解決.并且通知全部門注意.

解決方案:

修改aof檔案中的指令.删除flushAll之後重新開機redis即可.

3.5 Redis記憶體優化政策

3.5.1 修改Redis記憶體

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

修改記憶體大小:

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題

3.5.2 場景說明

Redis運作的空間是記憶體.記憶體的資源比較緊缺.是以應該維護redis記憶體資料,将改讓redis保留熱點資料.

redis記憶體優化算法:①LRU算法②LFU算法③RANDOM算法

3.5.3 LRU算法

LRU是Least Recently Used的縮寫,即最近最少使用,是一種常用的頁面置換算法,選擇最近最久未使用的頁面予以淘汰。該算法賦予每個頁面一個通路字段,用來記錄一個頁面自上次被通路以來所經曆的時間 t,當須淘汰一個頁面時,選擇現有頁面中其 t 值最大的,即最近最少使用的頁面予以淘汰。

次元: 自上一次使用的時間T

最為理想的記憶體置換算法.

3.5.4 LFU算法

LFU(least frequently used (LFU) page-replacement algorithm)。即最不經常使用頁置換算法,要求在頁置換時置換引用計數最小的頁,因為經常使用的頁應該有一個較大的引用次數。但是有些頁在開始時使用次數很多,但以後就不再使用,這類頁将會長時間留在記憶體中,是以可以将引用計數寄存器定時右移一位,形成指數衰減的平均使用次數。

least frequently used (LFU) page-replacement algorithm

即最不經常使用頁置換算法,要求在頁置換時置換引用計數最小的頁,因為經常使用的頁應該有一個較大的引用次數。但是有些頁在開始時使用次數很多,但以後就不再使用,這類頁将會長時間留在記憶體中,是以可以将引用計數寄存器定時右移一位,形成指數衰減的平均使用次數。

次元: 引用次數

3.5.5 RANDOM算法

随機算法

3.5.6 記憶體政策優化

1.volatile-lru 在設定了逾時時間的資料, 采用lru算法進行删除.

2.allkeys-lru 所有資料采用lru算法

3.volatile-lfu 在設定了逾時時間的資料, 采用LFU算法進行删除.

4.allkeys-lfu 所有資料采用LFU算法

5.volatile-random 設定逾時時間資料采用随機算法

6.allkeys-random 所有資料采用随機算法

7.volatile-ttl 設定了逾時時間的資料 根據ttl規則删除. 将剩餘時間少的提前删除

8.noeviction 記憶體滿了 不做任何操作.報錯傳回.

百萬發-京淘項目day13筆記補充1.利用Redis緩存實作商品分類查詢2.AOP案例說明3. redis常見面試題