關於Redis入門及透明(透明即使用AOP實現低耦合)快取層實現
emm...博主在前些天學習了一下Redis,以及在網上查詢了部分資料、將看到的大牛們寫的相關文章整合了一下。。(如有侵權多見諒...)分享一些經驗,可能寫的或者瞭解的不是很透徹,哈哈...大牛們請無視
那麼在講解之前我們先來簡單的瞭解一下Redis
一、什麼是Redis?
- Redis是一個快取 ( 快取的優勢在於它的速度遠遠高於硬碟執行速度) 伺服器 ——高效能系統必備技術之一
- Redis是一個開源的、使用ANSI C語言編寫的、支援網路互動的、遵循BSD協議、可基於記憶體也可持久化的Key-Value資料庫
二、Redis的優點:
- 因為是純記憶體操作,Redis的效能非常出色,Redis的速度每秒可執行大約 110000
次的設定(SET)操作,每秒大約可執行 81000 次的讀取/獲取(GET)操作。- Redis可以支援五種資料型別:string(字串),hash(雜湊),list(列表),set(集合)及zset(sorted set:有序集合) 由於開發人員常用的大多數資料型別,例如列表,集合,排序集和雜湊等等。這使得Redis很容易被用來解決各種問題,因為我們知道哪些問題可以更好使用地哪些資料型別來處理解決。
- 操作具有原子性所有Redis操作都是原子操作,這確保如果兩個客戶端併發訪問,Redis伺服器能接收更新的值,避免接收異常資料。
- 支援資料持久化,支援AOF和RDB兩種持久化方式
- Redis是一個多實用工具,可用於多種用例,如:快取,訊息佇列(Redis本地支援釋出/訂閱),應用程式中的任何短期資料,例如,web應用程式中的會話,網頁命中計數等
- 此外單個value的最大限制是1GB,不像 memcached只能儲存1MB的資料,因此Redis可以用來實現很多有用的功能,比方說用他的List來做FIFO雙向連結串列,實現一個輕量級的高性 能訊息佇列服務,用他的Set可以做高效能的tag系統等等。另外Redis也可以對存入的Key-Value設定expire時間,因此也可以被當作一 個功能加強版的memcached來用。
缺點:
- 資料庫容量受到實體記憶體的限制,不能用作海量資料的高效能讀寫,因此Redis適合的場景主要侷限在較小資料量的高效能操作和運算上。
- Redis比較難線上擴容,在叢集的容量上限時,線上擴容會變得很複雜。
三、Redis與其他鍵值儲存系統相比:
- Redis是鍵值資料庫系統的不同進化路線,它的值可以包含更復雜的資料型別,可在這些資料型別上定義原子操作。
- Redis是一個記憶體資料庫,但在磁碟資料庫上是持久的,因此它代表了一個不同的權衡,在這種情況下,在不能大於儲存器(記憶體)的資料集的限制下實現非
- 常高的寫和讀速度。
- 記憶體資料庫的另一個優點是,它與磁碟上的相同資料結構相比,複雜資料結構在記憶體中儲存表示更容易操作。 因此,Redis可以做很少的內部複雜性。
四、Redis執行流程
- 業務層所有指定的查詢方法,都要先去快取去查詢,如果有,就直接返回快取的資料(物件);如果沒有,就去資料庫查詢,並且把資料庫中查到的資料,放入快取中;
- 刪除、修改:判斷Redis快取中key是否存在,如果有,則刪除快取中的資料(物件);如果沒有,則直接執行。這裡修改語句的話博主認為不需要在快取中去修改,而選擇直接刪除的原因是因為,在使用者修改完之後可能並不需要立即使用,並且多了許多程式碼所產生的收益微乎其微(還可以偷懶..)。
- 增:不必要提前查出來放到快取中或者進行key的非空驗證,所以在下面的程式碼中博主沒有做增加的環繞增強
5、Redis的運用場景
- 比如說資料高併發的讀寫
- 需要進行大量資料的讀寫
- 對擴充套件性要求高的資料
一些實現細節大家可以看圖來理解
下面來看一下相關的實現程式碼。
實體類以及Dao層、Service層操作省略
Bean配置
Spring-Beans.xml內程式碼
<?xml version="1.0" encoding="UTF-8"?>
<aop:aspectj-autoproxy/> //宣告自動為spring容器中那些配置@aspectJ切面的bean建立代理,織入切面
<context:annotation-config/>// 是用於啟用那些已經在spring容器裡註冊過的bean(無論是通過xml的方式還是通過package sanning的方式)上面的註解。
<context:component-scan base-package="com.landun.lichenming"/>// 在xml配置了這個標籤後,spring可以自動去掃描base-pack下面或者子包下面的java檔案,如果掃描到有@Component @[email protected]等這些註解的類,則把這些類註冊為bean
<!-- 配置Jedis -->
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxActive" value="20"/>//連線池最大的連線數
<property name="maxIdle" value="10"/> //連線池中最多可空閒maxIdle個連線 ,這裡取值為10,表示即使沒有資料庫連線時依然可以保持10空閒的連線,而不被清除,隨時處於待命狀態。設 0 為沒有限制。minIdle 表示 連線池中最少空閒maxIdle個連線
<property name="maxWait" value="1000"/>// maxWait 連線池中連線用完時,新的請求等待時間,毫秒,取值-1,表示無限等待,直到超時為止,也可取值9000,表示9秒後超時。超過時間會出錯誤資訊一般把maxActive設定成可能的併發量就行了
<property name="testOnBorrow" value="true"/>// 從連線池獲取一個連線時,驗證有效性
</bean>
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg index="0" ref="jedisPoolConfig" />
<constructor-arg index="1" value="127.0.0.1" />
<constructor-arg index="2" value="6379" />
<!-- timeout -->
<constructor-arg index="3" value="2000" />
<!-- password -->
<constructor-arg index="4" value="62416038626259878353394562797" />
</bean>
<!--匯入spring-mybatis.xml-->
<import resource="spring-mybatis.xml"/>
</bean>
配置mybatis-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
<configuration>
<settings>
<setting name="lazyLoadingEnabled" value="false"/>//延遲載入的開關,即懶載入
為什麼使用懶載入:因為記憶體容量有限 ,為了減少併發量,減少系統資源的消耗,
</settings>
<typeAliases>
<package name="com.landun.lichenming.entity"/>//對映實體類
</typeAliases>
<mappers>
<mapper resource="com/landun/lichenming/dao/UserMapper.xml"/>//對映dao內的對映檔案
</mappers>
</configuration>
配置RedisCache(進行jedis的查詢,刪除等操作)
package com.landun.lichenming.cache;
import javax.annotation.Resource;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
@Component
public class RedisCache {
@Resource
private JedisPool jedisPool;
public Object getFromCache(String key) {
Jedis jedis = jedisPool.getResource();
byte[] result = jedis.get(key.getBytes());
// 如果查詢沒有為空
if (null == result) {
return null;
}
// 查詢到了,反序列化
return SerializeUtil.unSerialize(result);
}
// 將資料庫中查詢到的資料放入redis
public void save(String redisKey, Object obj) {
// 序列化
byte[] bytes = SerializeUtil.serialize(obj);
// 存入redis
Jedis conn = jedisPool.getResource();
String result = conn.set(redisKey.getBytes(), bytes);
if ("OK".equals(result)) {
System.out.println("[RedisCache]:資料成功儲存到redis cache...");
}
}
// 將資料庫查詢到的key刪除
public void del(String key) {
Jedis jedis = jedisPool.getResource();
if (jedis.exists(key.getBytes())) {
jedis.del(key.getBytes());
if (jedis.exists(key.getBytes())) {
System.out.println(" [RedisCache] :資料刪除失敗!!!請聯絡程式設計師");
} else {
System.out.println(" [RedisCache] :資料刪除成功");
}
} else {
System.out.println(" [RedisCache] :記憶體資料不存在");
}
}
}
配置SerializeUtil(對傳進來的資料進行序列化與反序列化操作)
package com.landun.lichenming.cache;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializeUtil {
//序列化
public static byte[] serialize(Object obj){
ObjectOutputStream obi=null;
ByteArrayOutputStream bai=null;
try {
bai=new ByteArrayOutputStream();
obi=new ObjectOutputStream(bai);
obi.writeObject(obj);
byte[] byt=bai.toByteArray();
return byt;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//反序列化
public static Object unSerialize(byte[] byt){
ObjectInputStream oii=null;
ByteArrayInputStream bis=null;
bis=new ByteArrayInputStream(byt);
try {
oii=new ObjectInputStream(bis);
Object obj=oii.readObject();
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
定義切面
package com.landun.lichenming.cache;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;/**
* 該類表示一個切面,表示: “呼叫find*方法根據id查某個物件時,先從快取查詢,如果沒有,再查資料庫”
*
*/
@Component
// 使用Aspect註解將RedisCacheAspect定義為切面
@Aspect
public class RedisCacheAspect {private static String PROJECT_NAME = "REDIS-";
private static String MODULE_NAME = "AUTH-";private static final Logger log = Logger.getLogger(RedisCacheAspect.class);
@Resource
private RedisCache redisCache;// 定義切入點
@Pointcut("execution(* com.landun.lichenming.service..*Service.find*(..))")
public void getCachePointcut() {
}@Around("getCachePointcut()")
public Object around(ProceedingJoinPoint joinPoint) {// 先獲取目標方法引數
String id = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
id = String.valueOf(args[0]);
}// 得到一個具有唯一性的快取key
String redisKey = getRedisKey(joinPoint, id);
log.debug("進入環繞增強前半部分,嘗試從redis查詢,生成的快取key:" + redisKey);
// 根據key獲取從redis中查詢到的物件
Object data = redisCache.getFromCache(redisKey);
// 如果查詢到了
if (null != data) {
log.debug("[" + redisKey + "]快取命中:" + data);
return data;
}// 沒有查到,那麼查詢資料庫
try {
data = joinPoint.proceed();// 執行目標方法(service中的find*方法)
} catch (Throwable e) {
e.printStackTrace();
}
log.debug("沒有從redis中取出資料,改為從資料庫中查詢的資料..." + data);// 後置:將資料庫中查詢的資料放到redis中
redisCache.save(redisKey, data);
log.debug("資料已儲存到redis" + data);// 將查詢到的資料返回
return data;}
// 定義切入點
@Pointcut("execution(* com.landun.lichenming.service..*Service.update*(..))")
public void updateCachePointcut() {
}@Pointcut("execution(* com.landun.lichenming.service..*Service.delete*(..))")
public void deleteCachePointcut() {
}@Around("updateCachePointcut() or deleteCachePointcut()")
public Object UpdateAnddelete(ProceedingJoinPoint joinPoint) {// 先獲取目標方法引數
String id = null;
Object[] args = joinPoint.getArgs();
if (args != null && args.length > 0) {
id = String.valueOf(args[0]);
}// 得到一個具有唯一性的快取key
String redisKey = getRedisKey(joinPoint, id);
// 根據key獲取從redis中查詢到的物件
log.debug("update進入環繞增強前半部分,嘗試從redis查詢,生成的快取key:" + redisKey);
Object data = redisCache.getFromCache(redisKey);
// 如果查詢到了
if (null != data) {
log.debug("update[" + redisKey + "]快取命中:" + data);
try {
data = joinPoint.proceed();// 執行目標方法(service中的find*方法)
} catch (Throwable e) {
e.printStackTrace();
}
// 執行刪除操作
redisCache.del(redisKey);
log.debug("資料已從redis刪除");
return 1;
} else {
return 0;
}
}
/**
* 拼接一個獨特的key
* @param joinPoint
* @param id
* @return
*/
private String getRedisKey(ProceedingJoinPoint joinPoint, String id) {
return PROJECT_NAME + MODULE_NAME + joinPoint.getTarget().getClass().getName() + "-" + id;
}
}
建立一個測試類進行測試
package com.landun.lichenming;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;import com.landun.lichenming.entity.User;
import com.landun.lichenming.service.UserService;public class TestAspectOfCache {
private UserService userService;
@Before
public void setUp() throws Exception {
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext();
ac.setValidating(false);
ac.setConfigLocation("spring-beans.xml");
ac.refresh();
System.out.println("Spring IoC container initialized.");
userService = (UserService)ac.getBean("userService");
assertNotNull(userService);
System.out.println("userService initialized.");
}//我們測試一下查詢
@Test
public void testFindBtyId() {
User u = userService.findUser(1);
assertNotNull(u);
System.out.println(u);
}}
測試後控制檯輸出
相關推薦
關於Redis入門及透明(透明即使用AOP實現低耦合)快取層實現
emm...博主在前些天學習了一下Redis,以及在網上查詢了部分資料、將看到的大牛們寫的相關文章整合了一下。。(如有侵權多見諒...)分享一些經驗,可能寫的或者瞭解的不是很透徹,哈哈...大牛們請無視 那麼在講解之前我們先來簡單的瞭解一下Redis 一、什麼是Redis
solr搜索之入門及原理(一)
solr solr入門 1 solr簡介solr官方文檔:http://wiki.apache.org/solr/DataImportHandler 下載地址:http://www.apache.org/dyn/closer.cgi/lucene/solr/2 solr入門我們使
redis入門學習記錄(一)
1、linux線上下載Redis ,官網地址:https://redis.io/download目前,最新的Redist版本為redis-5.0.0,使用wget下載 進入/usr/local/src目錄,使用如下命令: wget http://download.redis.io/releases/red
redis入門學習記錄(二) redis入門學習記錄(一)
繼第一節 redis入門學習記錄(一)之後,我們來學習redis的基本使用。 接下來我們看看/usr/local/redis/bin目錄下的幾個檔案作用是什麼? redis-benchmark:redis效能測試工具 redis-check-aof:檢查aof日誌的工具 redi
MySQL 入門及安裝(親測適用win10)
今天上午學習MySQL資料庫的安裝,也順便理清楚了資料庫的一些背景知識,特記錄如下: I. 背景知識 1. 資料 常見的文字,圖片,視訊,音訊都是資料。 2. 資料庫 儲存和管理海量的資料。 3. 資料庫的分類(database - DB) A. 關係型資料庫
Python celery和Redis入門安裝使用(排難帖)
shrink 獲取 問題 wine span cti bubuko cover hub 1、redis安裝 下載地址 https://github.com/MicrosoftArchive/redis/releases,選擇Redis-x64-3.2.100.msi5.8
Redis安裝及配置(基於5.0版本)
目前最新版為:5.0.3,官方推薦使用原始碼方式進行安裝,以下是安裝步驟: 1、下載 wget http://download.redis.io/releases/redis-5.0.3.tar.gz 2、解壓 tar -xzvf redis-5.0.3.tar.gz -C
Redis入門及安裝
一、什麼是Redis Redis是Remote Dictionary Server(遠端資料服務)的縮寫,由義大利人antirez(Salvatore Sanflippo) 開發的一款 記憶體高速快取資料庫,該軟體使用C語言編寫,它的資料模型為(key-value),它支援
【Redis入門】-叢集(手動搭建)
使用哨兵模式可以有效的增加資料庫容量,同時可以實現自動化,但是,即使使用哨兵模式,redis叢集的每個資料庫仍然儲存著叢集中的所有資料,這樣就會存在木桶效應:資料庫的總容量受限於儲存記憶體最小的redis節點! 而這裡講的叢集,是對資料庫進行水平擴容,每個節點會儲存不同區域
Redis 原理及應用(3)--記憶體淘汰機制、主從同步原理,HA策略(哨兵機制)分析
非精準的LRU 上面提到的LRU(Least Recently Used)策略,實際上Redis實現的LRU並不是可靠的LRU,也就是名義上我們使用LRU演算法淘汰鍵,但是實際上被淘汰的鍵並不一定是真正的最久沒用的,這裡涉及到一個權衡的問題,如果需要在全部鍵空間內搜尋最優解,則必然會增加系統的開銷,Re
C/C++基本語法,入門及提高(1)
-- 學習C++可分為4個層次: 第一層次,C++基礎:挑選一本入門書籍,如《C++ Primer》、《C++大學教程》、或Stroustrup撰寫的經典《C++程式設計語言》或他一年半前的新作《C++程式設計原理與實踐》,而一般C++課程也止於此,另外《C++ 標準程式
Redis入門及在商城案例中的使用
自學那麼多月以來學到的知識點挺多幾乎每天都在接受新東西,接受的多忘的也多,想回頭再去找也不知道從哪裡找了,所以決定執行好幾個月前就決定的事情-寫部落格,用來記錄自己每次所學習到的東西。 由於自己實習的時候,自己做的專案的資料庫就是用的MySql跟Redis。所
jvm入門及理解(二)——類載入器子系統
一、類載入子系統的作用 類載入子系統負責從檔案系統或者網路中載入Class檔案,class檔案在檔案開頭有特定的檔案標識; ClassLoader只負責class檔案的載入,至於它是否可以執行,則由Execution Engine決定 載入的類資訊存放於一塊成為方法區的記憶體空間。除了類資訊之外,方法區還會
jvm入門及理解(六)——垃圾回收與演算法
一、jvm垃圾回收要做的事情 哪些記憶體需要回收 什麼時候回收 怎麼回收 二、如何判斷物件已經死亡,或者說確定為垃圾 引用計數法: 給物件中新增一個引用計數器,每當有一個地方引用它時,計數器的值就加1;當引用失效時,計數器值就減1;任何時刻計數器為0的物件就是不可能再被使用的。這也就是需要回收的物件
2017面向對象程序設計(Java) 第1周學習指導及要求(2017.8.24-2017.8.27)
令行 str applet 面向 學習目標 對象 com 變量 課程學習 2017面向對象程序設計(Java) 第1周學習指導及要求(2017.8.24-2017.8.27) 學習目標 了解課程上課方式及老師教學要求,掌握課程學習必要的軟件工具; 簡單了解Java特點
axios配置及使用(發起請求時帶上token)
ima exp The push .post 設置 export host 接口 1.安裝 利用npm安裝 npm install axios --save 2.引入即可使用 import axios from ‘axios‘ 3.目錄 4.各個文件設置: (1
【SU外掛情報局】 Enscape for SketchUp 基礎入門完全解析(附Enscape2.3.3安裝包)
作者 | 活力網-Andrew 同學們大家好! 活力網SU外掛情報局正式開播啦!!! 我們第一期要講的外掛是——一款重量級的實時渲染外掛 那它是一款什麼樣的外掛吶? 他是一款實時渲染的SU外掛 與它類似的軟體有: Lumion Twinmotion Mars 相比 L T
軟體測試流程及規範(參考大華為的規範)
軟體測試流程及規範(參考大華為的規範) 參考某大佬(窩真不知道是哪位大佬)總結的測試流程並結合在華為做測試學到的規範,整理的我們公司的測試流程,分享是一種美德,so開始你的閱讀吧~ 軟體測試流程及規範 一、目標 制定完整且具體的測試路線和流程,為快速、高效和高質量的軟體測試提供基礎流
SpringBoot全域性異常捕獲及處理(包括自定義異常捕獲處理)
在做專案的時候需要對自定義異常做捕獲和處理,現在將程式碼記錄下來便於以後查閱。 1、全域性異常捕捉處理 @ControllerAdvice( annotations = {RestController.class} ) public class ExceptionHandlerAdv
特殊權限set_uid、set_gid及stick_bit(使用不多,了解)
分享圖片 作用 set_gid tick com oss mar blog 臨時 set_uid存放密碼的文件:/etc/shadow ![](http://i2.51cto.com/images/blog/201812/04/c0dc4eaedca