zoukankan      html  css  js  c++  java
  • Spring boot 论坛项目实战_04

    Redis, 一站式高兴能存储方案

    1. Redis 入门

    • Redis 是一款 基于键值对 的NoSQL 数据库, 它的值支持多种数据结构:

      • 字符串(Strings)、哈希(hashes)、列表(lists)、集合(sets)、有序集合(sorted sets)等

    • Redis 将所有的数据都存放在内存中,所以它的读写性能十分惊人。同时,Redis还可以将内存中的数据以快照或日志的形式保存到硬盘上,以保证数据的安全性

    • Redis 典型的应用场景包括:缓存、排行榜、计数器、社交网络、消息队列等。

    • 参考网站:

    • Redis 常用命令:

      • 清空当前数据:flushdb

      • 字符串(Strings):

        • 存储: set key【字符拼接用“ : ”】 value

        • 获取: get key【字符拼接用“ : ”】

        • 数字加:incr key,返回 integer

        • 数字减:decr key,返回 integer

          • 加减运算只能是 整数类型 integer

      • 哈希(hashes):

        • 存储:hset key【字符拼接用“ : ”】 field【hash 的 key】 value

        • 获取:hget key【字符拼接用“ : ”】 field

      • 列表(lists):【可以理解为一个横向的数组】

        • 根据其存取 可左可右,可分别实现 队列 和 栈 的特性

        • 进:可左可右

        • 左进

          • 左存多个数值: lpush key value [value ...]

          • 查看当前列表长度: llen key

          • 查看指定 key 的 list 里面第 index 个值:lindex key index

          • 查看指定范围的值:lrange key start stop

        • 出:可左可右

        • 指定右出: rpop key

      • 集合(sets):

        • 存入集合元素:sadd key member [member ...]

        • 统计集合元素:scard key

        • 随机弹出元素:spop key 【可用于抽奖】

        • 查看集合所剩元素:smembers key

      • 有序集合(sorted sets):

        • 添加元素:zadd key [NX|XX] [CH] [INCR] score member [score member ...] 【分数 元素名】

        • 查询某一个值的分数:zscore key member

        • 返回某个值的排名:zrank key member 【默认由小到大,从 0 开始】

        • 取某个范围的数据:zrange key start stop [WITHSCORES]

      • 全局:keys pattern

        • 查看当前所有的 key :keys *

        • 查看所有 test 开头的 key:keys test*

        • 查看某个 key 的类型:type key

        • 查看某个 key 是否存在:EXISTS key [key ...]

        • 删除某 key:del key [key ...]

        • 设置 某 key 的过期时间【到期自动删除】:expire key seconds

     

    2. Spring 整合 Redis

     

    • 引入依赖

      • spring-boot-starter-data-redis

    • 配置Redis

    • Spring 默认配置的 k-v 中 k:Object

      实际开放常用的 k: String 类型

      所以这里需要自己再手动配置

      • 配置数据库参数

      • 编写配置类,构造RedisTemplate

        • // 基于 Spring 框架的
          import org.springframework.context.annotation.Bean;
          import org.springframework.context.annotation.Configuration;
          import org.springframework.data.redis.connection.RedisConnectionFactory;
          import org.springframework.data.redis.core.RedisTemplate;
          import org.springframework.data.redis.serializer.RedisSerializer;
          ​
          @Configuration
          public class RedisConfig {
          ​
              // 接入连接工厂, 才能创建对象
              @Bean
              public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
                  // 方法的实例化
                  RedisTemplate<String,Object> template = new RedisTemplate<String,Object>();
                  // 给对象设置连接工厂
                  template.setConnectionFactory(factory);
          ​
                  // 设置 key 的序列化方式, 参数是 spring 框架的 redis 下的
                  // 返回一个能够序列化字符串的序列化器
                  template.setKeySerializer(RedisSerializer.string());
          ​
                  // 设置 value 的序列化方式
                  template.setValueSerializer(RedisSerializer.json());
          ​
                  // 设置 hash 的 key 的序列化方式
                  template.setHashKeySerializer(RedisSerializer.string());
          ​
                  // 设置 hash 的 value 的序列化方式
                  template.setHashValueSerializer(RedisSerializer.json());
          ​
                  // 触发设置结束后 生效
                  template.afterPropertiesSet();
                  return template;
              }
          ​
          }
    • 访问Redis

      • redisTemplate.opsForValue()

        • @Test
          public void testStrings(){
              String redisKey = "test:count";
          ​
              // String 类型的值
              redisTemplate.opsForValue().set(redisKey,1);
          ​
              System.out.println(redisTemplate.opsForValue().get(redisKey));
              System.out.println(redisTemplate.opsForValue().increment(redisKey));
              System.out.println(redisTemplate.opsForValue().decrement(redisKey));
          }

           

      • redisTemplate.opsForHash()

        • // 访问 hash
              @Test
              public void  testHashes(){
                  String redisKey = "test:user";
                  redisTemplate.opsForHash().put(redisKey,"id",1);
                  redisTemplate.opsForHash().put(redisKey,"username","张三");
          ​
                  System.out.println(redisTemplate.opsForHash().get(redisKey, "id"));
                  System.out.println(redisTemplate.opsForHash().get(redisKey, "username"));
          ​
              }

           

      • redisTemplate.opsForList()

        • @Test
              public void testLists(){
                  // 左进列表
                  String redisKey = "test:ids";
          ​
                  // 放入数据
                  redisTemplate.opsForList().leftPush(redisKey,101);
                  redisTemplate.opsForList().leftPush(redisKey,102);
                  redisTemplate.opsForList().leftPush(redisKey,103);
          ​
                  // 取出数据
                  // 统计当前元素个数
                  System.out.println(redisTemplate.opsForList().size(redisKey));
                  // 获取指定位置的元素, 获取某个索引所对应的元素
                  System.out.println(redisTemplate.opsForList().index(redisKey,0));
                  // 按照 索引范围获取元素
                  System.out.println(redisTemplate.opsForList().range(redisKey, 0, 2));
                  // 弹出元素, 左出
                  System.out.println(redisTemplate.opsForList().leftPop(redisKey));
                  System.out.println(redisTemplate.opsForList().leftPop(redisKey));
                  System.out.println(redisTemplate.opsForList().leftPop(redisKey));
              }

           

      • redisTemplate.opsForSet()

        • @Test
              public void  testSets(){
                  String redisKey = "test:teachers";
          ​
                  redisTemplate.opsForSet().add(redisKey,"刘备","关羽","张飞","赵云","孔明");
          ​
                  // 统计元素个数
                  System.out.println(redisTemplate.opsForSet().size(redisKey));
                  // 弹出一个数据, 随机弹出
                  System.out.println(redisTemplate.opsForSet().pop(redisKey));
                  // 统计现在集合中的数据都是什么, 展示现在集合中所有元素
                  System.out.println(redisTemplate.opsForSet().members(redisKey));
                  
              }

           

      • redisTemplate.opsForZSet()

        • @Test
              public void testSortedSets(){
                  String redisKey = "test:students";
          ​
                  // 添加 元素, 及其对应的分数
                  redisTemplate.opsForZSet().add(redisKey,"唐僧",80);
                  redisTemplate.opsForZSet().add(redisKey,"悟空",90);
                  redisTemplate.opsForZSet().add(redisKey,"八戒",50);
                  redisTemplate.opsForZSet().add(redisKey,"沙僧",70);
                  redisTemplate.opsForZSet().add(redisKey,"龙马",60);
          ​
                  // 统计元素总数
                  System.out.println(redisTemplate.opsForZSet().zCard(redisKey));
                  // 查询某个元素的分数
                  System.out.println(redisTemplate.opsForZSet().score(redisKey,"八戒"));
                  // 查询某个元素的排名, 默认由小到大
                  System.out.println(redisTemplate.opsForZSet().rank(redisKey,"八戒"));
                  // 倒叙排名, 按分数由大到小
                  System.out.println(redisTemplate.opsForZSet().reverseRank(redisKey,"八戒"));
                  // 从小到大取前三
                  System.out.println(redisTemplate.opsForZSet().range(redisKey,0,2));
                  // 从大到小取前三
                  System.out.println(redisTemplate.opsForZSet().reverseRange(redisKey,0,2));
                  
              }
      • 对 key 的访问

        • // 访问公共类型 key
              @Test
              public void testKeys(){
          ​
                  // 程序中一般不会用 keys * 这个命令, 这个命令一般是直接查询
          ​
                  redisTemplate.delete("test:user");
          ​
                  // 判断 key 是否存在
                  System.out.println(redisTemplate.hasKey("test:user"));
          ​
                  // 设置 key 的过期时间
                  // TimeUnit. 设置时间 单位: 日, 时, 分, 秒, 毫秒...
                  redisTemplate.expire("test:students",10, TimeUnit.SECONDS);
              }
      • 多次访问同一个key , 可采用绑定 key 的方式,来避免复写 redisKey:

        • // 多次访问同一个 key
              @Test
              public void testBoundOperations(){
                  String redisKey = "test:count";
          ​
                  // Bound XXX Operations: XXX : 你具体要访问的数据类型, 这里是String
                  // redisTemplate.bound XXX Ops : 绑定具体的数据类型, 这里是String
                  BoundValueOperations operations = redisTemplate.boundValueOps(redisKey);
                  // 这样就是绑定了 key 不用每次都写了.
                  operations.increment();
                  operations.increment();
                  operations.increment();
                  operations.increment();
                  operations.increment();
                  System.out.println(operations.get());
          ​
              }

     

    Redis 的事务

    Redis 是 非关系型数据库, 所以不用严格遵守 ACID

    在实际生产环境中, 建议用编程式事务实现, 声明式只能针对方法整体不能 对单独代码行控制

    // 编程式事务
     
    @Test
        public void testTransactional() {
            Object obj = redisTemplate.execute(new SessionCallback() {
                @Override
                public Object execute(RedisOperations operations) throws DataAccessException {
                    String redisKey = "test:tx";
                    // 启用事务
                    operations.multi();
    ​
                    operations.opsForSet().add(redisKey,"张三");
                    operations.opsForSet().add(redisKey,"李四");
                    operations.opsForSet().add(redisKey,"王五");
    ​
                    // 事务过程是不能允许读的
                    System.out.println(operations.opsForSet().members(redisKey));
                    //operations.exec() : 提交事务
                    return operations.exec();
                }
            });
    ​
            System.out.println(obj);
        }

    • 打印结果可以看出:

      • 第一次 查询结构为空, 保证了事务的原子性

      • 第二次打印结果: 前面三个 “1”: 每次操作影响的元素个数; 后面就是这个集合内的具体元素

     

    3. 点赞

    将点赞数据存入 Redis 提升性能, 优于存在内存中

    异步请求

    • 点赞

      • 支持对帖子、评论点赞

      • 第1次点赞,第二次取消点赞【双击取消操作】

    • 首页点赞数量

      • 统计帖子的点赞数量

    • 详情页点赞数量

      • 统计点赞数量

      • 显示点赞状态

     

    4. 我收到的赞

    • 重构点赞功能

      • 以用户为key, 记录点赞数量

      • increment(key), decrement(key)

    • 开发个人主页

      • 以用户为key, 查询点赞数量

     

    5. 关注、取消关注

    • 需求

      • 开发关注、取消关注功能

      • 统计用户的关注数、粉丝数

    • 关键

      • 若 A 关注了 B, 则 A 是 B 的 Follower(粉丝) , B 是 A 的 Followee(目标)。

      • 关注的目标可以是用户、帖子、题目等,在实现时将这些目标抽象为实体。

     

    6. 关注列表、粉丝列表

    • 业务层

      • 查询某个用户关注的人,支持分页。

      • 查询某个用户的粉丝,支持分页。

    • 表现层

      • 处理 “查询关注的人”、“查询粉丝” 请求。

      • 编写 “查询关注的人”、“查询粉丝” 模板。

     

    7. 优化登录模块

    • 使用Redis 存储验证码

      • 验证码需要频繁的访问与刷新,对性能要求较高

      • 验证码不需永久保存,通常在很短的时间后就会失效

        • 设置过期时间

      • 分布式部署,存在Session 共享的问题

    • 使用Redis 存储登录凭证【不删,保留用户记录】

      • 处理每次请求时,都要查询用户的登录凭证,访问的效率非常高

    • 使用Redis 缓存用户信息【暂存,要删+】

      • 处理每次请求时,都要根据凭证查询用户信息,访问的频率非常高

  • 相关阅读:
    Windows OS上安装运行Apache Kafka教程
    CSS3 transition属性
    CSS3新增UI样式
    css3响应式布局
    CSS3弹性盒模型
    CSS3
    移动端开发的那点事儿
    git开源项目协作
    移动端(IOS)iframe监听不到 onscroll 事件
    Javascript高级程序设计读书笔记(第10章 DOM)
  • 原文地址:https://www.cnblogs.com/77-is-here/p/13679941.html
Copyright © 2011-2022 走看看