看完這篇,Redis你就入門了

1、Redis簡介java

一、Redis(Remote Dictionary Server 遠程字段服務)是一個開源的使用ANSI C語言編寫、支持網絡、科技與內存亦可持久化的日誌型、key-value數據庫,並提供多種語言的API。web

二、Redis是一個key-value存儲系統,它支持存儲的value類型相對更多,包括string、list、set、zset(sorted set --有序集合)和hash。這些數據結構都支持push/pop、add/remove及取交集並集和差集及更豐富的操做,並且這些操做都是原子性的。在此基礎上,Redis支持各類不一樣方式的排序。爲了保證效率,數據都是緩存在內存中,Redis會週期性的把更新的數據寫入磁盤或者把修改操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。redis

三、Redis提供了java、C/C++、PHP、JavaScript、Perl、Object-C、Python、Ruby、Erlang等客戶端,使用很方便。spring

四、Reids支持主從同步。數據能夠從主服務器向任意數量的從服務器上同步,從服務器能夠是關聯其餘服務器的主服務器。這使得Redis可執行單層數複製。存盤能夠有意無心的對數據進行寫操做。因爲徹底實現了發佈/訂閱機制,使得從數據庫在任何地方同步樹時,可訂閱一個頻道並接收主服務器完整的消息發佈記錄。同步對讀取操做的可擴展性和數據冗餘頗有幫助。數據庫

五、 在咱們平常的Java Web開發中,無不都是使用數據庫來進行數據的存儲,因爲通常的系統任務中一般不會存在高併發的狀況,因此這樣看起來並無什麼問題,但是一旦涉及大數據量的需求,好比一些商品搶購的情景,或者是主頁訪問量瞬間較大的時候,單一使用數據庫來保存數據的系統會由於面向磁盤,磁盤讀/寫速度比較慢的問題而存在嚴重的性能弊端,一瞬間成千上萬的請求到來,須要系統在極短的時間內完成成千上萬次的讀/寫操做,這個時候每每不是數據庫可以承受的,極其容易形成數據庫系統癱瘓,最終致使服務宕機的嚴重生產問題。緩存

2、NoSQL技術安全

爲了克服上述問題,java web項目一般會引入NoSQL技術,這是一種基於內存的數據庫,而且提供必定的持久化功能。springboot

Redis和MongoDB是當前使用最普遍的NoSQL, 而就Redis技術而言,它的性能十分優越,能夠支持每秒十幾萬的讀寫操做,其性能遠超數據庫,而且還支持集羣、。分佈式、主從同步等配置,原則上能夠無限擴展,讓更多的數據存儲在內存中,更讓人欣慰的是它還支持必定的事務能力,這保證了高併發的場景下數據的安全和一致性。服務器

3、Redis在Java Web中的應用網絡

Redis 在 Java Web 主要有兩個應用場景:

  • 存儲緩存用的數據
  • 須要高速讀寫的場合

一、存儲緩存用的數據 

在平常對數據庫的訪問中,讀操做的次數遠超寫操做,比例大概在 1:93:7,因此須要讀的可能性是比寫的可能大得多的。當咱們使用SQL語句去數據庫進行讀寫操做時,數據庫就會去磁盤把對應的數據索引取回來,這是一個相對較慢的過程。 
若是放在Redis中,也就是放在內存中,讓服務器直接讀取內存中的數據,那麼速度就會快不少,而且會極大減小數據庫的壓力,可是使用內存進行數據存儲開銷也是比較大的,限於成本的緣由,通常咱們只是使用Redis存儲一些經常使用的和主要的數據,好比用戶登陸信息等。

通常而言在使用 Redis 進行存儲的時候,咱們須要從如下幾個方面來考慮:

(1)業務數據經常使用嗎?使用率如何?

若是使用率較低,就不必寫入緩存。

(2)該業務是讀操做多,仍是寫操做多?

若是寫操做多,頻繁須要寫入數據庫,也不必使用緩存。

(3)業務數據大小如何?

若是要存儲幾百兆字節的文件,會給緩存帶來很大的壓力,這樣也不必。

在考慮了這些問題以後,若是以爲有必要使用緩存,那麼就使用它!使用 Redis 做爲緩存的讀取邏輯以下圖所示:

從上圖咱們能夠知道如下兩點:

(1)當第一次讀取數據的時候,讀取Redis的數據就會失敗,此時就會觸發程序讀取數據庫,把數據讀取出來,而且寫入Redis中

(2)當第二次以及之後須要讀取數據時,就會直接讀取Redis,讀取數據後就結束了流程,這樣速度大大提升了。

從上面的分析能夠知道,讀操做的可能性是遠大於寫操做的,因此使用 Redis 來處理平常中須要常常讀取的數據,速度提高是顯而易見的,同時也下降了對數據庫的依賴,使得數據庫的壓力大大減小。

分析了讀操做的邏輯,下面咱們來看看寫操做流程:

從流程能夠看出,更新或者寫入的操做,須要多個 Redis 的操做,若是業務數據寫次數遠大於讀次數那麼就沒有必要使用 Redis。

二、高速讀寫場合

在現在的互聯網中,愈來愈多的存在高併發的狀況,好比天貓雙十一、搶紅包、搶演唱會門票等,這些場合都是在某一個瞬間或者是某一個短暫的時刻有成千上萬的請求到達服務器,若是單純的使用數據庫來進行處理,就算不崩,也會很慢的,輕則形成用戶體驗極差用戶量流水,重則數據庫癱瘓,服務宕機,而這樣的場合都是不容許的!

因此咱們須要使用 Redis 來應對這樣的高併發需求的場合,咱們先來看看一次請求操做的流程:

咱們來進一步闡述這個過程:

(1)當一個請求到達服務器時,只是把業務數據在Redis上進行讀寫,而沒有對數據庫進行任何的操做,這樣就能大大提升讀寫的速度,從而知足高速相應的需求。

(2)可是這些緩存的數據仍然須要持久化,也就是存入數據庫之中,因此在一個請求操做完Redis的讀寫以後,會去判斷該高速讀寫的業務是否結束,這個判斷一般會在秒殺商品爲0,紅包金額爲0時成立,若是不成立,則不會操做數據庫;若是成立,則觸發事件將Redis的緩存的數據以批量的形式一次性寫入數據庫,從而完成持久化的工做。

4、在java中使用Redis

一、添加Jedis依賴

想要在 Java 中使用 Redis 緩存,須要添加相關的Jar包依賴,打開Maven倉庫的網站:https://mvnrepository.com/ ,搜索Jedis:

把它導入工程中去就能夠啦,下面咱們來對Redis的寫入性能作一下測試:

@Test
public void redisTester() {
    Jedis jedis = new Jedis("localhost", 6379, 100000);
    int i = 0;
    try {
        long start = System.currentTimeMillis();// 開始毫秒數
        while (true) {
            long end = System.currentTimeMillis();
            if (end - start >= 1000) {// 當大於等於1000毫秒(至關於1秒)時,結束操做
                break;
            }
            i++;
            jedis.set("test" + i, i + "");
        }
    } finally {// 關閉鏈接
        jedis.close();
    }
    // 打印1秒內對Redis的操做次數
    System.out.println("redis每秒操做:" + i + "次");
}
-----------測試結果-----------
redis每秒操做:10734次

二、使用Redis鏈接池

跟數據庫鏈接池相同,Java Redis也一樣提供了類 redis.clients.jedis.JedisPool來管理咱們的Reids鏈接池對象,而且咱們可使用 redis.clients.jedis.JedisPoolConfig來對鏈接池進行配置,代碼以下:
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大空閒數
poolConfig.setMaxIdle(50);
// 最大鏈接數
poolConfig.setMaxTotal(100);
// 最大等待毫秒數
poolConfig.setMaxWaitMillis(20000);
// 使用配置建立鏈接池
JedisPool pool = new JedisPool(poolConfig, "localhost");
// 從鏈接池中獲取單個鏈接
Jedis jedis = pool.getResource();
// 若是須要密碼
//jedis.auth("password");

Redis只能支持六種數據結構 (string/hash/list/set/zset/hyperloglog)的操做 ,但在Java中咱們一般以類對象爲主,因此在Redis存儲的數據結構月java對象之間進行轉換,如本身編寫一些工具類 好比一個角色對象的轉換,仍是比較容易的,可是涉及到許多對象的時候,這其中不管工做量仍是工做難度都是很大的,因此整體來講, 就操做對象而言,使用Redis仍是挺難的,好在spring對這些進行了封裝和支持。

5、在spring中使用Redis

上面說到了 Redis 沒法操做對象的問題,沒法在那些基礎類型和 Java 對象之間方便的轉換,可是在 Spring 中,這些問題均可以經過使用RedisTemplate獲得解決!

想要達到這樣的效果,除了 Jedis 包之外還須要在 Spring 引入 spring-data-redis 包。

一、使用spring配置JedisPoolConfig對象

大部分的狀況下,咱們仍是會用到鏈接池的,因而先用 Spring 配置一個 JedisPoolConfig 對象:

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <!--最大空閒數-->
    <property name="maxIdle" value="50"/>
    <!--最大鏈接數-->
    <property name="maxTotal" value="100"/>
    <!--最大等待時間-->
    <property name="maxWaitMillis" value="20000"/>
</bean>

二、爲鏈接池配置工廠模型

好了,咱們如今配置好了鏈接池的相關屬性,那麼具體使用哪一種工廠實現呢?在Spring Data Redis中有四種可供咱們選擇的工廠模型,它們分別是:

  • JredisConnectionFactory
  • JedisConnectionFactory
  • LettuceConnectionFactory
  • SrpConnectionFactory

咱們這裏就簡單配置成JedisConnectionFactory:

<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <!--Redis服務地址-->
    <property name="hostName" value="localhost"/>
    <!--端口號-->
    <property name="port" value="6379"/>
    <!--若是有密碼則須要配置密碼-->
    <!--<property name="password" value="password"/>-->
    <!--鏈接池配置-->
    <property name="poolConfig" ref="poolConfig"/>
</bean>

三、配置RedisTemplate

普通的鏈接根本沒有辦法直接將對象直接存入 Redis 內存中,咱們須要替代的方案:將對象序列化(能夠簡單的理解爲繼承Serializable接口)。咱們能夠把對象序列化以後存入Redis緩存中,而後在取出的時候又經過轉換器,將序列化以後的對象反序列化回對象,這樣就完成了咱們的要求:

  RedisTemplate能夠幫助咱們完成這份工做,它會找到對應的序列化器去轉換Redis的鍵值:

<bean id="redisTemplate"
      class="org.springframework.data.redis.core.RedisTemplate"
      p:connection-factory-ref="connectionFactory"/>

四、測試

首先編寫好支持咱們測試的POJO類:

/**
 * @author: 素小暖
 * @create: 2020-2-12
 */
public class Student implements Serializable{

    private String name;
    private int age;

    /**
     * 給該類一個服務類用於測試
     */
    public void service() {
        System.out.println("學生名字爲:" + name);
        System.out.println("學生年齡爲:" + age);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

而後編寫測試類:

@Test
public void test() {
    ApplicationContext context =
            new ClassPathXmlApplicationContext("applicationContext.xml");
    RedisTemplate redisTemplate = context.getBean(RedisTemplate.class);
    Student student = new Student();
    student.setName("我沒有三顆心臟");
    student.setAge(21);
    redisTemplate.opsForValue().set("student_1", student);
    Student student1 = (Student) redisTemplate.opsForValue().get("student_1");
    student1.service();
}

運行結果:

6、springboot中使用Redis

一、在springboot中添加Redis依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

二、添加配置文件application.peoperties

# REDIS (RedisProperties)
# Redis數據庫索引(默認爲0)
spring.redis.database=0
# Redis服務器地址
spring.redis.host=localhost
# Redis服務器鏈接端口
spring.redis.port=6379
# Redis服務器鏈接密碼(默認爲空)
spring.redis.password=
# 鏈接池最大鏈接數(使用負值表示沒有限制)
spring.redis.pool.max-active=8
# 鏈接池最大阻塞等待時間(使用負值表示沒有限制)
spring.redis.pool.max-wait=-1
# 鏈接池中的最大空閒鏈接
spring.redis.pool.max-idle=8
# 鏈接池中的最小空閒鏈接
spring.redis.pool.min-idle=0
# 鏈接超時時間(毫秒)
spring.redis.timeout=0

三、測試訪問

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest()
public class ApplicationTests {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void test() throws Exception {

        // 保存字符串
        stringRedisTemplate.opsForValue().set("aaa", "111");
        Assert.assertEquals("111", stringRedisTemplate.opsForValue().get("aaa"));

    }
}

經過上面這段極爲簡單的測試案例演示瞭如何經過自動配置的StringRedisTemplate對象進行Redis的讀寫操做,該對象從命名中就可注意到支持的是String類型。本來是RedisTemplate<K, V>接口,StringRedisTemplate就至關於RedisTemplate<String, String>的實現。

四、存儲對象

這一步跟上面使用Spring同樣,只須要將POJO類實現Serializable接口就能夠了,我這裏就貼一下測試代碼:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest()
public class ApplicationTests {

    @Autowired
    private RedisTemplate redisTemplate;

    @Test
    public void test() throws Exception {

        User user = new User();
        user.setName("我沒有三顆心臟");
        user.setAge(21);

        redisTemplate.opsForValue().set("user_1", user);
        User user1 = (User) redisTemplate.opsForValue().get("user_1");

        System.out.println(user1.getName());
    }
}

運行結果與spring一致

 

 

相關文章
相關標籤/搜索