Redis

Redis(Remote Dictionary Server)是一种支持key-value等多种数据结构的存储系统。可用于缓存,事件发布或订阅,高速队列等场景。支持网络,提供String、List、Set、Zset、Hash结构直接存取,基于内存,可持久化。

1.Redis为什么快

  • 纯内存访问,读写性能优异
  • 支持数据类型丰富
  • 单线程避免上下文切换
  • 渐进式ReHash,缓存时间戳(获取时间会有上下文切换,redis无法承受,要对时间缓存。所以redis有定时任务,每ms更新时间缓存,所以获取时间就直接从缓存获得,避免上下文切换)
  • 使用了多路IO复用模型,非阻塞IO

2.基础数据结构详解

String字符串

String是redis中最基本的数据类型,一个key对应一个value。

实战场景:

  • 缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。
  • 计数器:redis是单线程模型,一个命令执行完才会执行下一个,同时数据可以一步落地到其他的数据源。
  • session:常见方案spring session + redis实现session共享

List列表

Redis中的List其实就是链表(Redis用双端链表实现List)。

实战场景:

  • 微博TimeLine: 有人发布微博,用lpush加入时间轴,展示新的列表信息。
  • 消息队列

Set集合

Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。

实战场景: 

  • 标签(tag),给用户添加标签,或者用户给消息添加标签,这样有同一标签或者类似标签的可以给推荐关注的事或者关注的人。
  • 点赞,或点踩,收藏等,可以放到set中实现

Hash散列

Redis hash 是一个 string 类型的 field(字段) 和 value(值) 的映射表,hash 特别适合用于存储对象。

实战场景: 

  • 缓存: 能直观,相比string更节省空间,的维护缓存信息,如用户信息,视频信息等。

Zset有序集合:

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

实战场景:

  • 排行榜:有序集合经典使用场景。例如小说视频等网站需要对用户上传的小说视频做排行榜,榜单可以按照用户关注数,更新时间,字数等打分,做排行。

3.特殊类型详解

HyperLogLogs(基数统计)

举个例子,A = {1, 2, 3, 4, 5}, B = {3, 5, 6, 7, 9};那么基数(不重复的元素)= 1, 2, 4, 6, 7, 9; (允许容错,即可以接受一定误差)

实战场景:

这个结构可以非常省内存的去统计各种计数,比如注册 IP 数、每日访问 IP 数、页面实时UV、在线用户数,共同好友数等。

Bitmap (位存储)

Bitmap 即位图数据结构,都是操作二进制位来进行记录,只有0 和 1 两个状态。

实战场景: 

统计用户信息,活跃,不活跃! 登录,未登录! 打卡,不打卡! 两个状态的,都可以使用 Bitmaps!

geospatial (地理位置)

实战场景: 

推算地理位置的信息: 两地之间的距离, 方圆几里的人

4.Redis key 的过期时间和永久有效分别怎么设置

EXPIRE 和 PERSIST 命令

5.什么是redis事务

Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。

总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令

6.SpringBoot集成redisTemplate

1.增加maven依赖

<!--        redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

2.增加redis配置

spring:
  redis:
    # Redis数据库索引(默认为0)
    database: 0
    # Redis服务器地址
    host: localhost
    # Redis服务器连接端口
    port: 6379
    # Redis服务器连接密码(默认为空)
    password:
    lettuce:
      pool:
        # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-active: 8
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-wait: -1
        # 连接池中的最大空闲连接 默认 8
        max-idle: 8
        # 连接池中的最小空闲连接 默认 0
        min-idle: 0
    # 连接超时时间(毫秒)
    timeout: 1000

3.写一个redis配置类 

  • 以上是 RedisAutoConfiguration 类中的源码片段,可以看出 SpringBoot 对 Redis 做自动化配置的时候,在容器中注入了 redisTemplate stringRedisTemplate
  • 其中,RedisTemplate<Object, Object> 表示,key 的类型为 Object,value 的类型为 Object,但是我们往往需要的是 RedisTemplate<String, Object>,这就需要我们重新注入一个 RedisTemplate 的 Bean,它的泛型为 RedisTemplate<String, Object>,并设置 key和value 的序列化方式
  • 看到这个@ConditionalOnMissingBean注解后,就知道如果Spring容器中有了RedisTemplate对象了,这个自动配置的RedisTemplate不会实例化。因此我们可以直接自己写个配置类,配置RedisTemplate。

 4.主要的业务代码

public ResponseResult<Void> deleteByIdRedis(Long userId) {
        int result = userService.deleteById(userId).getCode();
        String key = "user_"+userId;
        if(result == ResponseCode.SERVER_SUCCESS.id){
            boolean hasKey = redisTemplate.hasKey(key);
            if(hasKey){
                redisTemplate.delete(key);
                System.out.println("删除了Redis中的key:"+key);
            }
        }
        return ResponseResult.ok();
    }

    public ResponseResult<Void> updateByIdRedis(Long userId, UserUpdateRequest updateRequest) {
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        // 更新对象 转成 实体
        User user = userDTOAssembler.assembler(updateRequest);
        // 设置主键
        user.setId(userId);
        int result = userService.updateById(user).getCode();
        if (result == ResponseCode.SERVER_SUCCESS.id) {
            String key = "user_" +userId;
            boolean haskey = redisTemplate.hasKey(key);
            if (haskey) {
                redisTemplate.delete(key);
                System.out.println("删除Redis中的key-----------> " + key);
            }
            // 再将更新后的数据加入缓存
            UserResponse userNew = this.queryByIdRedis(userId).getData();
            if (!ObjectUtils.isEmpty(userNew)) {
                operations.set(key, user, 3, TimeUnit.HOURS);
            }
        }
        return ResponseResult.ok();
    }

    public ResponseResult<UserResponse> queryByIdRedis(Long userId) {
        String key = "user_" + userId;
        ValueOperations<String, User> operations = redisTemplate.opsForValue();
        //判断redis中是否有键为key的缓存
        boolean hasKey = redisTemplate.hasKey(key);
        if (hasKey) {
            User user = operations.get(key);
            log.info("从Redis中获取数据:" + user.getName());
            return ResponseResult.success(userDTOAssembler.toResponse(user));
        } else {
            User user = userService.queryById(userId).getData();
            log.info("查询数据库获取数据:" + user.getName());
            //写入缓存
            operations.set(key, user, 1000, TimeUnit.HOURS);
            return ResponseResult.success(userDTOAssembler.toResponse(user));
        }
    }

5.启动成功,但是调用接口报错

出现原因:实体类中的LocalDate类型的数据不能序列化

解决办法:

①导入依赖

<!--        localdate序列化依赖-->
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.13.2</version>
        </dependency>

 ②在相关字段上加注解

    /**
     * 出生日期
     */
    @JsonSerialize(using = LocalDateSerializer.class)
    @JsonDeserialize(using = LocalDateDeserializer.class)
    private LocalDate birth;

7.启动成功,调用查询接口

 

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sunnxin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值