Redis及Spring-Data-Redis入門學習

繼上一篇Solr和Spring Data Solr學習,咱們思考一個問題,使用Solr的目的是什麼?確定是爲了加快服務器的相應速度。由於即便不適用Solr,經過請求數據庫咱們同樣能完成搜索功能,可是這樣會給服務器形成很大的壓力。html

而Solr僅僅是在搜索功能中用到了,可是大量請求的數據不只僅出如今搜索中,好比用戶的登陸信息,雖然數據量很小,可是整個項目每刷新一次頁面都要請求一次用戶登陸的Token信息,也會拖慢服務器的響應速度。咱們一般有兩中解決方式:1.數據緩存;2.網頁靜態化。java

其實咱們在Shiro實現用戶-角色-權限管理系統中已經用到了緩存技術,今天咱們瞭解一下Redis緩存技術。git

項目開源地址: Githubgithub

安裝Redis

Redis是一款開源的Key-Value數據庫。首先咱們要去 官網 下載Redis,因爲筆者使用的是MacOS系統,和Windows系統有所不一樣。redis

安裝過程再也不敘述,這裏提供兩個教程:spring

<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

  1. 查看當前(db0)數據庫中全部的key值: keys *
  2. 清空當前數據庫中全部的數據: 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提供了redis各類操做、異常處理及序列化。

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

解釋

  1. redis.host是安裝redis server的客戶端IP地址,若是安裝在本機上就是127.0.0.1,若是安裝在服務器上請修改成服務器的IP地址。
  2. redis.port是redis server的默認端口,你安裝了redis,就默認使用這個端口號。
  3. redis.pass是訪問redis server的密碼,通常咱們不設置。
  4. redis.database=0表明使用的是redis默認提供的db0這個數據庫。
  5. redis-maxIdle是redis server的最大空閒數。
  6. redis-maxWait是鏈接redis時的最大等待毫秒數。
  7. 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;
}

TestTime.java

@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.

相關文章
相關標籤/搜索