看完这篇,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一致

 

 

相关文章
相关标签/搜索