Redis及Spring-Data-Redis入門學習
繼上一篇Solr和Spring Data Solr學習,我們思考一個問題,使用Solr的目的是什麼?肯定是為了加快伺服器的相應速度。因為即使不適用Solr,通過請求資料庫我們一樣能完成搜尋功能,但是這樣會給伺服器造成很大的壓力。
而Solr僅僅是在搜尋功能中用到了,但是大量請求的資料不僅僅出現在搜尋中,比如使用者的登入資訊,雖然資料量很小,但是整個專案每重新整理一次頁面都要請求一次使用者登入的Token資訊,也會拖慢伺服器的響應速度。我們通常有兩中解決方式:1.資料快取;2.網頁靜態化。
其實我們在Shiro實現使用者-角色-許可權管理系統中已經用到了快取技術,今天我們瞭解一下Redis快取技術。
專案開源地址: Github
安裝Redis
Redis是一款開源的Key-Value資料庫。首先我們要去 官網 下載Redis,由於筆者使用的是MacOS系統,和Windows系統有所不同。
安裝過程不再敘述,這裡提供兩個教程:
<br/>
啟動Redis
redis-server
redis-server &
建議使用第二個命令,用第二個命令啟動了redis server後能繼續輸入命令,使用第一個命令則不行。
如果終端中顯示如下logo表示redis啟動成功:
<br/>
操縱Redis
上面僅僅是啟動了Redis Server,但Redis是一種Key-Value型資料庫,也包含了一些查詢資料庫的命令,操作redis命令的入口就是: redis/bin/redis-cli
./bin/redis-cli
redis-cli
- 檢視當前(db0)資料庫中所有的key值:
keys *
- 清空當前資料庫中所有的資料:
flushall
更多的Redis命令可以參看:redis中文文件
<br/>
Spring Data Redis
之前學習Solr的時候用到了Spring Data Solr,現在學習Redis,Spring提供了Spring Data Redis用來實現通過配置檔案的方式訪問redis服務。Spring Data Redis對Redis底層開發包(Jedis, JRedis, and RJC)進行了高度封裝,RedisTemplate
Jedis
Jedis是Redis官方推出的一款面向Java的客戶端,提供了很多借口供Java語言呼叫。
Spring Data Redis針對Jedis提供瞭如下功能:
- 1.連線池自動管理,提供了一個高度封住的
RedisTemplate
類。 - 2.針對jedis客戶端中大量api進行歸類封裝,將同一型別操作封裝為operation介面: ValueOperations: 簡單的K-V操作 SetOperations: set型別資料操作 ZSetOperations: zset型別資料操作 HashOperations: 針對Map型別的資料操作 ListOperations: 針對List型別的資料操作
準備
匯入依賴
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.7.2.RELEASE</version>
</dependency>
建立redis-config.properties
redis.host=127.0.0.1
redis.port=6379
redis.pass=
redis.database=0
redis.maxIdle=300
redis.maxWait=3000
redis.testOnBorrow=true
解釋
redis.host
是安裝redis server的客戶端IP地址,如果安裝在本機上就是127.0.0.1,如果安裝在伺服器上請修改為伺服器的IP地址。redis.port
是redis server的預設埠,你安裝了redis,就預設使用這個埠號。redis.pass
是訪問redis server的密碼,一般我們不設定。redis.database=0
代表使用的是redis預設提供的db0這個資料庫。redis-maxIdle
是redis server的最大空閒數。redis-maxWait
是連線redis時的最大等待毫秒數。redis-testOnBorrow
在提取一個redis例項時,是否提前進行驗證操作;如果為true,則得到的jedis例項均是可用的。
建立spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:other/*.properties"/>
<!-- redis 相關配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!-- 最大空閒數 -->
<property name="maxIdle" value="${redis.maxIdle}"/>
<!-- 連線時最大的等待時間(毫秒) -->
<property name="maxWaitMillis" value="${redis.maxWait}"/>
<!-- 在提取一個jedis例項時,是否提前進行驗證操作;如果為true,則得到的jedis例項均是可用的 -->
<property name="testOnBorrow" value="${redis.testOnBorrow}"/>
</bean>
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="hostName" value="${redis.host}"/>
<property name="port" value="${redis.port}"/>
<property name="password" value="${redis.pass}"/>
<property name="poolConfig" ref="poolConfig"/>
</bean>
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
</bean>
</bean>
例項
本例項原始碼:Github
首先載入配置檔案spring-redis.xml
,注入RedisTemplate
模板類:
@Autowired
private RedisTemplate redisTemplate;
值型別
RedisTemplate
提供的很多操作redis資料庫的方法都是boundxxOps
這種。
新增
@Test
public void setValue(){
redisTemplate.boundValueOps("name").set("tycoding");
}
如果配置都正常的情況下,執行此方法就能向db0資料庫中新增一條key為name
的記錄;那麼我們在redis命令列中檢視所有的key:
奇怪,我新增的key明明是name
,為什麼查出來的確實一堆亂碼值呢?我們再使用redis命令列單獨新增一條記錄:
set testK testV
此時我們又發現,使用redis原生命令新增的資料是不會亂碼的;那麼就肯定是Spring Data Redis的原因了。經查詢是因為redisTemplate模板類在操作redis序列化的原因,我們要手動配置序列化方式為:StringRedisSerializer
修改之前建立的spring-redis.xml
配置檔案:
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory"/>
<!-- 序列化策略 推薦使用StringRedisSerializer -->
<property name="keySerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="valueSerializer">
<bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
</property>
<property name="hashKeySerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
<property name="hashValueSerializer">
<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
</property>
</bean>
再次新增資料
查詢
@Test
public void getValue(){
Object name = redisTemplate.boundValueOps("name").get();
System.out.println(name);
}
刪除
@Test
public void deleteValue(){
redisTemplate.delete("name");
}
Set型別
新增
@Test
public void setValueBySet(){
redisTemplate.boundSetOps("nameset").add("tycoding");
}
查詢
@Test
public void getValueBySet(){
Set nameset = redisTemplate.boundSetOps("nameset").members();
System.out.println(nameset);
}
刪除Set中某一個值
@Test
public void deleteValueBySet(){
redisTemplate.boundSetOps("nameset").remove("塗陌");
}
刪除整個Set
@Test
public void deleteAllValueByset(){
redisTemplate.delete("nameset");
}
List型別
右壓棧
右壓棧,後新增的物件排在後邊
@Test
public void setRightValueByList(){
redisTemplate.boundListOps("namelist").rightPush("tycoding");
redisTemplate.boundListOps("namelist").rightPush("塗陌");
}
顯示右壓棧集合
@Test
public void getRightValueByListI(){
List namelist = redisTemplate.boundListOps("namelist").range(0, 10);
System.out.println(namelist);
}
左壓棧
左壓棧,後新增的物件排在前面
@Test
public void setLeftValueByList(){
redisTemplate.boundListOps("namelist2").leftPush("tycoding");
redisTemplate.boundListOps("namelist2").leftPush("塗陌");
}
顯示左壓棧的集合:
@Test
public void getLeftValueByList(){
List name2 = redisTemplate.boundListOps("namelist2").range(0, 10);
System.out.println(name2);
}
根據索引查詢集合中的元素
@Test
public void searchByIndex(){
Object namelist = redisTemplate.boundListOps("namelist").index(1);
System.out.println(namelist);
}
Hash型別
新增
@Test
public void setValueByHash(){
redisTemplate.boundHashOps("namehash").put("a","tycoding");
}
提取所有的KEY
@Test
public void getKeysByHash(){
Set namehash = redisTemplate.boundHashOps("namehash").keys();
System.out.println(namehash);
}
提取所有的VALUE
@Test
public void getValuesByHash(){
List namehash = redisTemplate.boundHashOps("namehash").values();
System.out.println(namehash);
}
根據KEY取值
@Test
public void getValueByHash(){
Object o = redisTemplate.boundHashOps("namehash").get("a");
System.out.println(o);
}
根據KEY移除值
@Test
public void deleteValueByHash(){
redisTemplate.boundHashOps("namehash").delete("a");
}
<br/>
測試
上面說了一大堆,沒有實際的測試,著實不清楚Redis究竟效果如何,是不是真的提高了訪問速度?
下面我們以查詢資料庫所有值的功能來看一下使用Redis快取和未使用快取直接查詢資料庫所用時間。
本例原始碼地址:Github
未使用Redis快取,直接請求資料庫
public List<Goods> findAll() {
return goodsMapper.findAll();
}
使用了Redis快取
首先通過boundHashOps
獲取Redis資料庫中是否存在KEY為all
的資料,有的話就返回;沒有的話就查詢資料庫並將查詢到的資料新增到Redis資料庫中,且KEY為all
public List<Goods> findAll() {
List<Goods> contentList = (List<Goods>) redisTemplate.boundHashOps("goods").get("all");
if (contentList == null) {
//說明快取中沒有資料
System.out.println("從資料庫中讀取資料放入redis...");
contentList = goodsMapper.findAll();
redisTemplate.boundHashOps("goods").put("all", contentList); //存入redis中
} else {
System.out.println("從快取中讀取資料...");
}
// return goodsMapper.findAll();
return contentList;
}
@Test
public void run1() {
Long startTime = System.currentTimeMillis(); //開始時間
goodsMapper.findAll();
Long endTime = System.currentTimeMillis(); //結束時間
System.out.println("查詢資料庫--共耗時:" + (endTime - startTime) + "毫秒"); //1007毫秒
}
@Test
public void run2() {
Long startTime = System.currentTimeMillis(); //開始時間
goodsService.findAll();
Long endTime = System.currentTimeMillis(); //結束時間
System.out.println("從redis中讀取所有資料,共耗時:" + (endTime - startTime) + "毫秒");
}
在測試類中呼叫Service層的這兩個方法,得到的結果如下:
查詢資料庫--共耗時:1047毫秒
從redis中讀取所有資料,共耗時:197毫秒
<br/>
交流
如果大家有興趣,歡迎大家加入我的Java交流技術群:671017003 ,一起交流學習Java技術。博主目前一直在自學JAVA中,技術有限,如果可以,會盡力給大家提供一些幫助,或是一些學習方法,當然群裡的大佬都會積極給新手答疑的。所以,別猶豫,快來加入我們吧!
<br/>
聯絡
If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.
- [Blog@TyCoding's blog](http://www.tycoding.cn)
- [GitHub@TyCoding](https://github.com/TyCoding)
- [ZhiHu@TyCoding](https://www.zhihu.com/people/tomo-83-82/activities)