Redis入门

Redis 简介

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。

Redis 与其他 key - value 缓存产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis 优势

  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。

Redis 配置

Redis 的配置文件位于 Redis 安装目录下,文件名为 redis.conf(Windows 名为 redis.windows.conf)。

你可以通过 CONFIG 命令查看或设置配置项

Redis 数据类型

Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。

Redis 发布订阅

Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

Redis 客户端可以订阅任意数量的频道。

下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

Redis 事务

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。

在 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 的性能能达到十万级别,我不敢相信我的台式机电脑只有十分之一不到的性能,虽然说这里不是流水线的操作,会造成一定的影响,但我还是不信邪,我查到了官方的性能测试方法:

首先在Redis根目录下召唤Cmd:具体方法是按住【Shift】点击右键

然后输入命令:【redis-benchmark -n 100000 -q】:来同时执行10万个请求测试性能

好吧,我同时在我的笔记本上测试了一下,结果更加惨淡...low啊low...

第二步:使用 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 对这些进行了封装和支持。

第三步:在 Spring 中使用 Redis

上面说到了 Redis 无法操作对象的问题,无法在那些基础类型和 Java 对象之间方便的转换,但是在 Spring 中,这些问题都可以通过使用RedisTemplate得到解决!

想要达到这样的效果,除了 Jedis 包以外还需要在 Spring 引入 spring-data-redis 包:https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis

这里把2.0.7最新版本标红的意思是:别老想着使用最新的Jar包,特别是涉及到框架的一些东西,笔者用实际的操作体验告诉你们,引入该版本的包是会导致Jar包冲突的(也就是莫名其妙的错误),我乖乖换回了1.7.2的版本,代码就通了...我们来看看怎么做吧:

(1)第一步:使用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>

(2)第二步:为连接池配置工厂模型

好了,我们现在配置好了连接池的相关属性,那么具体使用哪种工厂实现呢?在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>

(3)第三步:配置RedisTemplate

普通的连接根本没有办法直接将对象直接存入 Redis 内存中,我们需要替代的方案:将对象序列化(可以简单的理解为继承Serializable接口)。我们可以把对象序列化之后存入Redis缓存中,然后在取出的时候又通过转换器,将序列化之后的对象反序列化回对象,这样就完成了我们的要求:

RedisTemplate可以帮助我们完成这份工作,它会找到对应的序列化器去转换Redis的键值:

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

笔者从《JavaEE互联网轻量级框架整合开发》中了解到,这一步需要配置单独的序列化器去支撑这一步的工作,但是自己在测试当中,发现只要我们的POJO类实现了Serializable接口,就不会出现问题...所以我直接省略掉了配置序列化器这一步...

(4)第四步:编写测试

首先编写好支持我们测试的POJO类:

/**
 * @author: @我没有三颗心脏
 * @create: 2018-05-30-下午 22:31
 */
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();
}

运行可以成功看到结果:

第四步:在 SpringBoot 中使用 Redis

(1)在SpringBoot中添加Redis依赖:

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

(2)添加配置文件:

在SpringBoot中使用.properties或者.yml都可以,这里给出.properties的例子,因为自己的.yml文件看上去感觉乱糟糟的:

# 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

(3)测试访问:

@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>的实现。

运行测试,如果一切成功则不会报错,如果我们没有拿到或者拿到的数不是我们想要的 “111” ,那么则会报错,这是使用Assert的好处(下面是我改成112之后运行报错的结果):

(4)存储对象:

这一步跟上面使用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());
    }
}

仍然没有任何问题:

Redis与Memcached的区别与比较
1 、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储。memcache支持简单的数据类型,String。

2 、Redis支持数据的备份,即master-slave模式的数据备份。

3 、Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用,而Memecache把数据全部存在内存之中

4、 redis的速度比memcached快很多

5、Memcached是多线程,非阻塞IO复用的网络模型;Redis使用单线程的IO复用模型。

 

使用redis有哪些好处?


(1) 速度快,因为数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1)

(2)支持丰富数据类型,支持string,list,set,sorted set,hash

(3) 支持事务,操作都是原子性,所谓的原子性就是对数据的更改要么全部执行,要么全部不执行

(4) 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除
 

Redis常见数据结构使用场景


1. String
常用命令: set,get,decr,incr,mget 等。

String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 
常规key-value缓存应用; 
常规计数:微博数,粉丝数等。

2.Hash
常用命令: hget,hset,hgetall 等。

Hash是一个string类型的field和value的映射表,hash特别适合用于存储对象。 比如我们可以Hash数据结构来存储用户信息,商品信息等等。

举个例子: 最近做的个人博客网站项目的首页就使用了redis的hash数据结构进行缓存,因为一个网站的首页访问量是最大的,所以通常网站的首页可以通过redis缓存来提高性能和并发量。我用jedis客户端来连接和操作我搭建的redis集群或者单机redis,利用jedis可以很容易的对redis进行相关操作,总的来说从搭一个简单的集群到实现redis作为缓存的整个步骤不难。感兴趣的可以看我昨天写的这篇文章:

《一文轻松搞懂redis集群原理及搭建与使用》: https://juejin.im/post/5ad54d76f265da23970759d3

3.List

常用命令: lpush,rpush,lpop,rpop,lrange等

list就是链表,Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,最新消息排行等功能都可以用Redis的list结构来实现。我自己的项目是最新文章、最新评论。

Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

4.Set
常用命令: 
sadd,spop,smembers,sunion 等

set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的。 
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。

在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同喜好、二度好友等功能。

5.Sorted Set
常用命令: zadd,zrange,zrem,zcard等

和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。

举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用Redis中的SortedSet结构进行存储。
=============================================================================

redis的应用场景有哪些

1,会话缓存(最常用)
2,消息队列,
比如支付3,活动排行榜或计数
4,发布,订阅消息(消息通知)
5,商品列表,评论列表等

Redis 事务相关的命令有哪几个?

MULTI、EXEC、DISCARD、WATCH

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值