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
相关推荐
bug菌¹26 分钟前
滚雪球学Oracle[4.2讲]:PL/SQL基础语法
数据库·oracle
逸巽散人36 分钟前
SQL基础教程
数据库·sql·oracle
月空MoonSky1 小时前
Oracle中TRUNC()函数详解
数据库·sql·oracle
momo小菜pa1 小时前
【MySQL 06】表的增删查改
数据库·mysql
向上的车轮2 小时前
Django学习笔记二:数据库操作详解
数据库·django
编程老船长2 小时前
第26章 Java操作Mongodb实现数据持久化
数据库·后端·mongodb
全栈师3 小时前
SQL Server中关于个性化需求批量删除表的做法
数据库·oracle
Data 3173 小时前
Hive数仓操作(十七)
大数据·数据库·数据仓库·hive·hadoop
BergerLee3 小时前
对不经常变动的数据集合添加Redis缓存
数据库·redis·缓存
gorgor在码农4 小时前
Mysql 索引底层数据结构和算法
数据结构·数据库·mysql