使用Redis實現切成編程操作

此工程爲Spring boot項目

Redis切成編程

目標:在方法上添加一個註解,實現查詢的時候緩存

方法:自定義註解+AOP切面編程

步驟

話不多說,直接上代碼演示

第一步

定義註解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface RedisCache {

    //存儲前綴
    String name() default "Spl---";

    //要存儲的key,默認是查詢條件的第一個參數
    String key() default "";

    //默認5分鐘
    int expireTime() default 5;

    //默認值是以分鐘爲單位
    TimeUnit unit() default TimeUnit.MINUTES;
}
第二步

註解使用的地方
在這裏插入圖片描述

第三步

AOP切面思想

@Component
@Aspect
@Slf4j
/**
 * AOP切面編程
 */
public class RedisCacheAop {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 切入點
     */
    @Pointcut("@annotation(com.trc.study.annotation.RedisCache)")
    public void pointCup(){}

    /**
     * 增強方法-環繞
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "pointCup()")
    public Object cache(ProceedingJoinPoint joinPoint) throws Throwable{
        //從切入點,獲取方法簽名
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        //從切點,獲取目標對象的字節碼,的方法。參數:(方法名,方法的所有參數)
        RedisCache redisCache = method.getAnnotation(RedisCache.class);
        String key = redisCache.key();
        Object[] args = joinPoint.getArgs();
        String prefix = redisCache.name();
        String methodName = method.getName();
        //獲取key值
        String cacheKey = getCacheKey(key, args, prefix, methodName);

        Object proceed = null;
        //根據key從緩存獲取值
        String value = stringRedisTemplate.opsForValue().get(cacheKey);
        //判斷value是否爲空
        if (StringUtils.isNotBlank(value)){
            log.info("從redis裏面獲取數據!");
            return getValueActualTypeData(method,value);
        }else {
            log.info("從mysql裏面獲取數據!");
            //查詢數據庫,執行原來的方法
            proceed = joinPoint.proceed();
            //封裝結果
            String results = JSONObject.toJSONString(proceed);
            int expireTime = redisCache.expireTime();
            TimeUnit timeUnit = redisCache.unit();
            //存入緩存
            stringRedisTemplate.opsForValue().set(cacheKey,results,expireTime,timeUnit);
            return proceed;
        }
    }

    /**
     *  獲取key值
     * @param key
     * @param args
     * @param prefix
     * @param methodName
     * @return
     */
    private String getCacheKey(String key,Object[] args,String prefix,String methodName){
        String cacheKey = "";
        if (StringUtils.isNotBlank(prefix)) {
            cacheKey = "/" + prefix + "/";
        }
        if (StringUtils.isNotBlank(key)) {
            return cacheKey += key;
        }
        if (null != args && 0 < args.length) {
            return cacheKey += args[0];
        }
        return methodName;
    }

    /**
     * 獲取數據
     * @param method
     * @param value
     * @return
     * @throws ClassNotFoundException
     */
    private Object getValueActualTypeData(Method method, String value) throws ClassNotFoundException {
        Class returnActualType = getReturnActualType(method);
        if (null != returnActualType) {
            return JSONObject.parseArray(value, returnActualType);
        }
        return null;
    }
    /**
     * 獲取返回值類型
     * @param method
     * @return
     * @throws ClassNotFoundException
     */
    private Class getReturnActualType(Method method) throws ClassNotFoundException {
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType) {
            Type[] actualTypes = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            for (Type actualType : actualTypes) {
                return Class.forName(actualType.getTypeName());
            }
        }
        return null;
    }
    
}

其他:工程結構
mvc模式
在這裏插入圖片描述
配置文件

server.port=9009
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
spring.datasource.username=root
spring.datasource.password=root
mybatis.type-aliases-package=com.trc.study.common
mapper.mappers=com.trc.study.dao.RedisCacheMapper

logging.level.info
#redis配置
#Redis服務器地址
spring.redis.host=127.0.0.1
#Redis服務器連接端口
spring.redis.port=6379
#Redis數據庫索引(默認爲0)
spring.redis.database=0
#連接池最大連接數(使用負值表示沒有限制)
spring.redis.jedis.pool.max-active=50
#連接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.jedis.pool.max-wait=3000
#連接池中的最大空閒連接
spring.redis.jedis.pool.max-idle=20
#連接池中的最小空閒連接
spring.redis.jedis.pool.min-idle=2
#連接超時時間(毫秒)
spring.redis.timeout=5000


redis基本數據類型

數據類型
	String: 字符串
		可以用來存儲字符串、整數、浮點數
		緩存
		數據共享分佈式
		分佈式鎖
		全局ID
		計數器
		限流
	Hash: 散列
		包含鍵值對的無序散列表。value只能是字符串,不能嵌套其他類型
		存儲對象類型的數據
	List: 列表
		存儲有序的字符串(從左到右),元素可以重複。可以充當隊列和棧的角色
		用戶消息時間線timeline
	Set: 集合
		String類型的無序集合,最大存儲數量2^32-1(40億左右)
		隨機獲取元素
	ZSet: 有序集合
		sorted set,有序的set,每個元素有個score。
		score相同時,按照key的ASCII碼排序。
	BitMaps
		是在字符串類型上面定義的位操作。一個字節由8個二進制位組成。
	Hyperloglogs
		提供了一種不太準確的基數統計方法
	Streams
		5.0推出的數據類型。支持多播的可持久化的消息隊列,用於實現發佈訂閱功能

Redis 爲什麼這麼快

1)純內存結構
	KV結構的內存數據庫,時間複雜度O(1)
2)單線程
	1、沒有創建線程、銷燬線程帶來的消耗
	2、避免了上線文切換導致的CPU消耗
	3、避免了線程之間帶來的競爭問題,例如加鎖釋放鎖死鎖等等
3)多路複用
	“多路”指的是多個網絡連接,“複用”指的是複用同一個線程
	利用select、poll、epoll可以同時監察多個流的 I/O 事件的能力,在空閒的時候,會把當前線程阻塞掉,當有一個或多個流有I/O事件時,就從阻塞態中喚醒,於是程序就會輪詢一遍所有的流(epoll是隻輪詢那些真正發出了事件的流),依次順序的處理就緒的流,這種做法就避免了大量的無用操作

主從、哨兵

集羣
	可用性、數據安全、性能
主從
	從節點不能寫入數據(只讀),只能從 master 節點同步數據
	主從複製原理
		連接階段
		 數據同步階段
		命令傳播階段
	不足
		RDB文件過大的情況下,同步非常耗時
		如果主服務器掛了,對外提供的服務就不可用
哨兵
	通過運行監控服務器來保證服務的可用性
		會對Sentinel做集羣的部署。Sentinel既監控所有的Redis服務,Sentinel之間也相互監控
	通信
		SDOWN與ODOWN轉換過程
		Sentinel與slaves"自動發現"機制
		Leader選舉
分佈式方案
	Redis數據的分片
		在客戶端實現相關的邏輯,例如用取模或者一致性哈希對key進行分片,查詢和修改都先判斷key的路由
		把做分片處理的邏輯抽取出來,運行一個獨立的代理服務,客戶端連接到這個代理服務,代理服務做請求的轉發
		基於服務端實現
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章