Redis

Redis

  • 1.什么是Redis
  • [2 Redis为什么这么快](#2 Redis为什么这么快)
  • 3.Redis有哪些数据类型
    • [3.1 string](#3.1 string)
      • [3.1.1 可以存储的值](#3.1.1 可以存储的值)
      • [3.1.2 操作](#3.1.2 操作)
      • [3.1.3 命令](#3.1.3 命令)
      • [3.1.4 使用场景](#3.1.4 使用场景)
    • [3.2 List列表](#3.2 List列表)
      • [3.2.1 命令](#3.2.1 命令)
      • [3.2.2 使用技巧](#3.2.2 使用技巧)
      • [3.2.3 使用场景](#3.2.3 使用场景)
    • [3.3 Set集合](#3.3 Set集合)
      • [3.3.1 命令](#3.3.1 命令)
      • [3.3.2 使用场景](#3.3.2 使用场景)
    • [3.4 Zset集合](#3.4 Zset集合)
      • [3.4.1 特点](#3.4.1 特点)
      • [3.4.2 命令](#3.4.2 命令)
  • 4.Redis实现Session共享
    • [4.1 演示session不共享的情况](#4.1 演示session不共享的情况)
    • [4.2 Redis实现session共享的具体操作(SpringBoot框架下演示)](#4.2 Redis实现session共享的具体操作(SpringBoot框架下演示))
    • [4.3 优点](#4.3 优点)
  • 5.Redis的16种常见应用场景
    • [5.1 缓存](#5.1 缓存)
    • [5.2 session共享](#5.2 session共享)
    • [5.3 分布式锁](#5.3 分布式锁)
      • [5.3.1 方式一](#5.3.1 方式一)
  • 6.SpringBoot整合Redis
    • [6.1 依赖引入](#6.1 依赖引入)
    • [6.2 API](#6.2 API)
    • [6.3 配置文件](#6.3 配置文件)

1.什么是Redis

Redis是一个用C语言编写, 开源高性能非关系型(NoSQL)的键值对数据库
Redis五种基本数据类型

2 Redis为什么这么快

  • 数据存在内存中: 内存的读写操作是磁盘(数据库)的一百倍左右
  • 使用C语言实现: C语言更底层, 执行速度更快
  • 单线程执行
    • 没有多线程竞争锁的性能消耗。
    • 没有多线程导致的切换而消耗CPU

3.Redis有哪些数据类型

3.1 string

3.1.1 可以存储的值

  • 字符串
  • 整数
  • 浮点型

当我们将整数、浮点数存储为String类型时, Redis会将其转换为字符串形式(浮点数不会发生精度丢失)

3.1.2 操作

  • 对字符串或者字符串的一部分进行操作
  • 对整数、浮点数执行自增或者自减操作

3.1.3 命令

  • 基本操作
    • set (key) (value):设置键值对
    • setnx (key) (value):设置键值对(防止覆盖)
      • 如果key存在, 不设置, 返回0
      • 如果key不存在, 设置, 返回1
    • get(key): 获取key对应的value
    • getset (key) (value): 先get再set, 返回旧值, 如果没有旧值则返回nil
    • append (key) (value): 向指定的key的value后追加字符串
    • del (key): 删除key
    • strlen (key): 获得key对应值的字符串的长度
  • 数字value的加减
    • incr (key) :value + 1
    • decr (key):value - 1
    • incrby (key) (number):value + number
    • decrby (key) (number):value - number
  • 获取或者设置指定范围内的值
    • getrange (key) (begin) (end) :获取[begin,end]下标范围内的值,如果是(0,1)就是获取所有值
    • setrange (key) (begin) (xxxx) :从begin下标开始设置xxx值,将原有的替换掉
  • 设置键值过期时间
    • setex (key) (seconds) expire:设置键过期时间
    • ttl (key) :查看key剩余存活时间
  • 同时设置或获取多个key-value
    • mset (key1) (value1) (key2) (value2) :用于同时设置一个或多个 key-value 对
    • mget (key1) (key2) :返回所有(一个或多个)给定 key 的值(如果某个key不存在,不存在的key返回null)
    • msetnx(key1) (value1) (key2) (value2) :当所有 key 都成功设置,返回 1 。 如果有一个key设置失败,所有的key设置都会失败,返回 0 。原子操作

3.1.4 使用场景

1.缓存

最常见的使用场景, 主要得益于其高性能(内存操作)、灵活的数据结构(支持5种结构)等等

  • 频繁读取的数据库查询: 当某个数据库查询被频繁执行, 可以将查询结果缓存到Redis中, 较少对数据库的访问
  • 会话缓存: 将用户会话存储到Redis中, 特别是大规模的web应用, 这可以提高会话管理的性能和可扩展性
  • 用户身份证验证信息缓存: 缓存用户的身份证令牌或者其他身份验证信息, 减少身份验证请求对数据库的负担
  • 页面内存缓存: 对于动态生成的页面内容, 可以将完整的HTML缓存到Redis中, 降低渲染时间, 提高网站性能

哪些内容适合进行缓存

  • 频繁访问: 通常是应用中频繁访问的数据
  • 相对稳定: 在一段时间内不经常变化
  • 持续时间长: 通常具备较长的生命周期

2.计数器

Redis是基于内存的键值对存储系统, 读写速度非常快, 性能很高, 能以非常低的延迟处理大量的计数请求, 同时Redis是单线程的, 一个命令执行完才会执行下一个, 不会出现线程安全问题

  • 网站访问计数: 统计网站、特定页面的点击次数, 每当用户访问网站或点击某个页面时, 通过Redis的INCR递增
  • 用户行为计数: 跟踪用户在应用中的各种行为, 例如发布文章、点赞、评论等. 对每种行为创建一个计数器, 可以方便记录用户活动
  • 实时统计: 在实时应用中,需要快速地获取某个指标的实时统计数据,例如活跃用户数、实时销售额等。使用Redis的计数器可以很容易地实现这些实时统计。

3.session共享

session + redis实现session共享

3.2 List列表

List其实是双端链表, 使用List结构可以轻松实现消息排队功能, 它可以轻松的完成对链表首位的操作, 来实现各个数据结构的功能

3.2.1 命令

  • RPUSH (KEY) (VALUE): 将指定值推到列表右端
  • LPUSH (KEY) (VALUE): 将指定值推到列表左端
  • RPOP (KEY): 从列表右端弹出一个值, 并返回被弹出的值
  • LPOP (KEY): 从列表坐端弹出一个值, 并返回被弹出的值
  • LRANGE (KEY) 0 1: 获取列表在指定索引范围上的所有值(此处使用0 1演示)
  • LINDEX (KEY) 1: 通过索引获取列表中的元素(此处使用1演示)
  • LTRIM (KEY) 1 2: 对一个列表进行修剪, 让列表只保留指定区间内的元素, 不在指定区间之内的元素都被删除
  • BRPOP (key) 3: 阻塞式右端弹出操作

3.2.2 使用技巧

  • LPUSH + LPOP = Stack(栈) 先进后出
  • LPUSH + RPOP = Queue(队列) 先进先出
  • LPUSH + LTRIM = Capped Collection(有限集合)
  • LPUSH + BRPOP=Message Queue(消息队列)

3.2.3 使用场景

  • 微博TimeLine: 有人发布微博, 用lpush加入时间轴, 展示新的列表信息
  • 简易的消息队列(不支持重复消费、不支持消费确认等功能)

3.3 Set集合

Redis的Set是String类型的无序集合, 集合成员不可重复, 底层是通过哈希表实现

3.3.1 命令

  • SADD key value: 向集合添加一个或多个成员
  • SCARD key: 获取集合中的成员个数
  • SMEMBER key member: 返回集合中所有的成员
  • SISMEMBER key member: 判断member元素是否是集合中的成员

3.3.2 使用场景

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

3.4 Zset集合

Zset和Set基本一致, 不过Zset的每个元素会关联一个double类型的权重参数(score), 使得集合中的元素可以按score进行有序排列

3.4.1 特点

有序集合的成员是唯一的, 但是分数score是可以重复的

3.4.2 命令

  • ZADD key score member [score member ...]: 将一个或多个成员及其分数值加入到有序集合中
  • ZREM key member [member ...]: 从有序集合中移除一个或多个成员
  • ZCARD key: 获取有序集合的成员数
  • ZRANGE key start end [WITHSCORES]: 按照socre从小到大的顺序, 返回有序集合中索引值在start和end范围内的成员以及其分数
  • ZREVRANGE key start end [WITHSCORES]: 按照socre从大到小的顺序, 返回有序集合中索引值在start和end范围内的成员以及其分数
  • ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]: 返回有序集合中指定分数区间的成员, 按分值从小到大进行返回, LIMIT offset count用于限制返回结果的数量, 可以指定offset(偏移量)和count(返回的成员数量)
  • ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]: 返回有序集合中分数在给定范围内的所有成员,按分数从大到小排序。
  • ZREMRANGEBYRANK key start stop: 移除有序集合中给定排名范围内的所有成员。
  • ZREMRANGEBYSCORE key min max: 移除有序集合中给定分数范围内的所有成员。

4.Redis实现Session共享

session是存储在服务器内存中的, 不同服务器之间无法共享session

如果想要实现session共享, 解决思路: 一台服务器登录成功后, 生成的session存入共享介质中

4.1 演示session不共享的情况

有下面这段代码, 提供了两个接口, 一个往session中塞值, 一个从session中取值

java 复制代码
@GetMapping("set")
public ApiResponse<Void> set(HttpServletRequest request) {
    HttpSession session = request.getSession();
    session.setAttribute("name", "jack");
    return ApiResponse.success();
}

@GetMapping("get")
public ApiResponse<String> get(HttpServletRequest request) {
    HttpSession session = request.getSession();
    String name = (String)session.getAttribute("name");
    return ApiResponse.success(name);
}

我们启动两个服务, 端口分别为8090和8091(模拟两台服务器分别运行)

  • 在不同的浏览器上分别访问这两个端口, 防止cookie覆盖
  • 先访问8090端口的set接口, 再访问8090端口的get接口, 得到'jack'
  • 换一个浏览器访问8091端口的get接口, 得到null

这个结果很直白的说明了两个服务器中的session是独立的

注意: 必须使用两个浏览器, 不然会导致cookie覆盖, 请求8091端口的get方法时, 得到的cookie会覆盖之前得到的cookie, 导致再访问8090端口的get方法, 携带的是8091端口返回的cookie

4.2 Redis实现session共享的具体操作(SpringBoot框架下演示)

  • 引入redis依赖以及spring-session-data-redis(自动将session存储到redis)
xml 复制代码
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
	<!-- <version>交给SpringBoot</version> -->
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.session/spring-session-data-redis -->
<dependency>
	<groupId>org.springframework.session</groupId>
	<artifactId>spring-session-data-redis</artifactId>
	<!-- <version>交给SpringBoot</version> -->
</dependency>
  • 在application.yml文件中配置连接redis和session相关配置
yml 复制代码
server:
  port: 8091
spring:
  # session配置
  session:
    timeout: 86400 # 设置session失效时间
    store-type: redis # 修改spring-session存储配置,默认存储到服务器内存中,现在设置存到redis中(关键)
  # redis配置
  redis:
    port: 6379 # redis的端口号
    host: 127.0.0.1 # 我的云服务器ip
    database: 0 # 设置存入redis的哪一个库(默认是0)

关键配置就一个: store-type: redis, 只要配置了这个, 那么代码中产生的session都会存放到redis中, 而非自己内存中

  • 模拟单个会话, 多个服务器的场景, 使用同一个浏览器访问8090服务8091服务, 首先访问8090服务set接口, 再访问8090服务get接口, 得到'jack'
  • 紧接着, 使用同一个浏览器, 访问8091服务get接口, 这样是为了保证两个请求属于同一个会话. 本次访问携带的cookie是访问8090服务时响应的cookie, 我们看看同一个cookie, 能不能在不同的服务中找到同一个session, 结果表明, 得到了"jack"

其实在我们第一个请求时, 就会生成session, 并将session存入了Redis, 我们可以通过Redis看到这个session的结构, 它是以Hash结构存储的

4.3 优点

使用redis依赖以及spring-session-data-redis实现多服务实例session共享的优点在于:

  • 操作简单: 跟平常在servlet中操作session一样, 不需要知道怎么将session存入redis、session在redis中的存储结构是什么、怎么从redis中取session这一类的问题
  • 性能高: redis是基于内存的, 读写速度很快, 通过将Session数据存储在Redis中,可以显著提高Session访问的速度,特别是在处理大量用户并发访问的情况下

5.Redis的16种常见应用场景

5.1 缓存

例如报表数据、明星热搜缓存、对象缓存、全页缓存、可以提升热点数据的访问数据.

5.2 session共享

如上例

5.3 分布式锁

多线程竞争同一资源时, 我们通常会使用锁机制(synchronized或者ReentrantLock)来保证线程安全, 但在微服务架构下, 多个服务同时对一条数据进行修改, 就不用使用这些方法了

通常我们会使用Redis来实现分布式锁, 因为Redis是一个单独的非业务服务, 不受其他业务的影响.

所有服务都可以向redis发送写入命令, 但只有一个可以写入成功, 那么这个写入成功的命令即获得了锁, 其他未成功写入的服务, 则进行其他处理

5.3.1 方式一

使用redis的setnx命令来实现分布式加锁

  • 使用setnx命令进行加锁
    • 如果key不存在, 就往redis中, 设置值, 并返回true
java 复制代码
@Service
public class RedisService {

    @Autowired
    private RedisTemplate<String, Object> template;

    // 分布式锁的Key, 类似于lock(Obj)中的Obj, 这个锁的唯一把钥匙
    private static final String REDIS_LOCK = "lock";

    public String redisLock() {
        try {
            /**
             * 加锁, 实质调用了setnx指令
             *    如果key不存在, 设置成功, 并返回true
             *    如果key存在, 设置失败, 并返回false
             * value可以使用随机值, 此处用0, 并不会影响锁
             */
            Boolean flag = template.opsForValue().setIfAbsent(REDIS_LOCK, "0");

            // 加锁失败, 执行相应业务
            if (!flag) {
                return "抢锁失败";
            }

            // ---------- 抢锁成功, 执行业务逻辑 ---------

            // ---------- 执行业务逻辑结束 --------------
            return "执行结束";
        } finally {
            // 需要在finally中释放锁, 这样即使执行业务逻辑时发生异常, 依旧可以释放锁
            template.delete(REDIS_LOCK);
        }
    }
}

6.SpringBoot整合Redis

Spring Boot 整合Redis

SpringDataRedis提供了统一API RedisTemplate来操作Redis, 支持Redis的发布订阅模式、Redis哨兵、Redis集群等等

6.1 依赖引入

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- redis依赖commons-pool 这个依赖一定要添加 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

SpringDataRedis使用了Apache Commons Pool2作为连接池的实现, 重用数据库连接池, 提高性能和资源利用率

6.2 API

6.3 配置文件

yml 复制代码
spring:
  redis:
    # Redis服务器地址
    host: 19.1.5.11
    # Redis服务器端口号
    port: 6379
    # 使用的数据库索引,默认是0
    database: 0
    # 连接超时时间
    timeout: 1800000
     # 设置密码
    password: "123456"
    lettuce:
      pool:
        # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认-1  
        max-wait: -1
        # 连接池中的最大空闲连接 默认8
        max-idle: 8
        # 连接池中的最小空闲连接 默认0
        min-idle: 0
        # 连接池最大连接数 默认8 ,负数表示没有限制
        max-active: 20
相关推荐
小陈工2 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花7 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸7 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain7 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希7 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神7 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员7 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java8 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿8 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴8 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存