当前位置: 首页 > article >正文

【Redis】万字整理 Redis 非关系型数据库的安装与操作

文章目录

  • Redis非关系型数据库
    • 简介与安装
    • 常用命令
    • 操作Redis
      • 操作字符串
      • 操作列表
      • 操作集合
      • 操作有序集合
      • StringRedisTemplate
      • Redis 工具类

Redis非关系型数据库

简介与安装

Redis(Remote Dictionary Server)是一个开源的、基于内存的键值(kay-value)存储数据库。它支持多种数据结构,如**字符串(String)、链表(List)、集合(Set)、有序集合(zset / sorted sets)和哈希(Hash)**等。Redis 的主要特点包括:

  • 数据存储在内存中,支持高速读写操作。
  • 支持数据持久化,可以将内存中的数据保存到磁盘中。
  • 提供主从复制(replication)、事务(transactions)、LUA脚本(Lua scripting)等功能。
  • 支持高可用性和分布式,通过哨兵和集群实现。

Redis 能做什么

  1. 缓存,毫无疑问这是 Redis 当今最为人熟知的使用场景。再提升服务器性能方面非常有效;

  2. 排行榜,如果使用传统的关系型数据库来做这个事儿,非常的麻烦,而利用 Redis 的 SortSet 数据结构能够非常方便搞定;

  3. 计算器/限速器,利用 Redis 中原子性的自增操作,我们可以统计类似用户点赞数、用户访问数等,这类操作如果用 MySQL,频繁的读写会带来相当大的压力;限速器比较典型的使用场景是限制某个用户访问某个 API 的频率,常用的有抢购时,防止用户疯狂点击带来不必要的压力;

    注:限速器也是对请求限流的一种实现方式。

  4. 好友关系,利用集合的一些命令,比如求交集、并集、差集等。可以方便搞定一些共同好友、共同爱好之类的功能;

  5. 简单消息队列,除了 Redis 自身的发布/订阅模式,我们也可以利用 List 来实现一个队列机制,比如:到货通知、邮件发送之类的需求,不需要高可靠,但是会带来非常大的 DB 压力,完全可以用 List 来完成异步解耦;

  6. Session 共享,默认 Session 是保存在服务器的文件中,即当前服务器,如果是集群服务,同一个用户过来可能落在不同机器上,这就会导致用户频繁登陆;采用 Redis 保存 Session 后,无论用户落在那台机器上都能够获取到对应的 Session 信息。

Redis 特点

  • Redis 将其数据库完全保存在内存中,仅使用磁盘进行持久化。
  • 与其它键值数据存储相比,Redis 有一组相对丰富的数据类型。
  • Redis 可以将数据复制到任意数量的从机中。

Redis 优点

  • 异常快:Redis 非常快,每秒可执行大约110000次的设置(SET)操作,每秒大约可执行81000次的读取/获取(GET)操作。
  • 支持丰富的数据类型:Redis 支持开发人员常用的大多数数据类型,例如列表,集合,排序集和散列等等。这使得Redis 很容易被用来解决各种问题,因为我们知道哪些问题可以更好使用地哪些数据类型来处理解决。
  • 操作具有原子性:所有 Redis 操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新的值。
  • 多实用工具:Redis 是一个多实用工具,可用于多种用例,如:缓存,消息队列(Redis本地支持发布/订阅),应用程序中的任何短期数据,例如,web应用程序中的会话,网页命中计数等。

安装

Windows 系统下 Redis 的安装过程

首先将安装包解压到 D 盘根目录下

在这里插入图片描述

1、启动 Redis

打开终端,输入 redis-server.exe redis.windows.conf

在这里插入图片描述

或新建记事本文件 startup.bat,输入 redis-server.exe redis.windows.conf

使用服务过程中黑窗口不能关闭

2、修改密码

解压目录下找到 redis.windows.conf 大概在 387 行左右 requirepass 下添加 requirepass 密码,保存,重新运行即可。

requirepass 123456

3、下载安装 RedisDesktopManager

RedisDesktopManager(RDM)是一个用于 Redis 数据库管理的开源跨平台 GUI 工具。它允许用户查看、编辑和监视 Redis 数据库中的数据。

(关闭防火墙并设置允许远程连接此电脑)

在这里插入图片描述

将安装包解压到 D 盘根目录下

双击运行 Another Redis Desktop Manager.exe (右键发送到桌面快捷方式建立快捷方式)

在这里插入图片描述

4、配置 SpringBoot 中 Redis

引入相关 jar

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

Redis 数据库配置

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
# 连接池最大连接数(使用负值表示没有限制)
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

上述步骤完成后,SpringBoot 自动在 Spring 容器中配置一个 redisTemplate 的 Bean,可以直接使用 redisTemplate

常用命令

Redis 命令参考 — Redis 命令参考

命令目录

操作命令常用命令
Key 键KEYS EXISTS TYPE TTL DEL
String 字符串SET GET SETEX SETNX
Hash 哈希表HSET HGET HDEL HKEYS HVALS HGETALL
List 列表LPUSH LRANGE RPOP LLEN BRPOP
Set 集合SADD SMEMBERS SCARD SINTER SUNION SDIFF SREM
SortedSet 有序集合ZADD ZRANGE ZINCRBY ZREM

DEL key [key …] 删除给定的一个或多个 key

KEYS pattern 查找所有符合给定模式 patternkey

TTL key 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。

TYPE key 返回 key 所储存的值的类型。

EXISTS key 检查给定 key 是否存在。

GET key 返回 key 所关联的字符串值。

SET key value [EX seconds] [PX milliseconds] [NX|XX] 将字符串值 value 关联到 key

HSET key field value 将哈希表 key 中的域 field 的值设为 value

HGET key field 返回哈希表 key 中给定域 field 的值。

HDEL key field [field …] 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。

LRANGE key start stop 返回列表 key 中指定区间内的元素,区间以偏移量 startstop 指定。

LPUSH key value [value …] 将一个或多个值 value 插入到列表 key 的表头

LPOP key 移除并返回列表 key 的头元素。

在这里插入图片描述

如上图通过 SET key “value” EX 1000 设置了一个带过期时间的键值对,其中键为 key,值为 value,并且设置该键在 1000 秒后过期。TTL key 查看键的剩余生存时间,返回结果是 987 秒。

操作Redis

操作字符串

@SpringBootTest
public class RedisTest {
    @Resource
    private RedisTemplate redisTemplate;
    @Test
    public void redisTemplateTest(){
        // 使用 Spring 封装的 RedisTemplate 操作字符串
        ValueOperations vo = redisTemplate.opsForValue();
        vo.set("uname","zhangsan");
    }
}
redisTemplate.opsForValue(); // 操作字符串 string
redisTemplate.opsForHash();  // 操作哈希表 hash
redisTemplate.opsForList();  // 操作列表 list
redisTemplate.opsForSet();   // 操作集合 set
redisTemplate.opsForZSet();  // 操作有序集合 zset

存储字符串

redisTemplate.opsForValue().set("name","tom");
redisTemplate.opsForValue().get("name") // 输出:tom

设置失效时间

redisTemplate.opsForValue().set("name","tom",10, TimeUnit.SECONDS);
redisTemplate.opsForValue().get("name")
  1. TimeUnit.DAYS
  2. TimeUnit.HOURS 小时
  3. TimeUnit.MINUTES 分钟
  4. TimeUnit.SECONDS
  5. TimeUnit.MILLISECONDS 毫秒

由于设置的是10秒失效,十秒之内查询有结果,十秒之后返回为 null

支持整型与浮点型(increment)

template.opsForValue().increment("sex",1);
System.out.println(template.opsForValue().get("sex")); // 输出:1(递增)

如果 key 已经存在并且是一个字符串,则该命令将该值追加到字符串的末尾。如果键不存在,则它被创建并设置为空字符串,因此 APPEND 在这种特殊情况下将类似于 SET

template.opsForValue().append("name"," hello");
System.out.println(template.opsForValue().get("name"));  // tom Hello

截取 key 所对应的 value 字符串

System.out.println(template.opsForValue().get("name",0,3)); // tom

返回 key 所对应的 value 值长度

System.out.println(template.opsForValue().size("key"));

存储一个对象(此类必须先序列化实现接口 Serializable)

RedisSerializer rs = new StringRedisSerializer();
redisTemplate.setStringSerializer(rs);
ValueOperations vo = redisTemplate.opsForValue();
vo.set("user",user);//放入redis
//取出对象
User setuser = (User) redisTemplate.opsForValue().get("user");

操作列表

将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从左边插入)

redisTemplate.opsForList().leftPush("names","张三");
redisTemplate.opsForList().leftPush("names","李四");

将所有指定的值插入存储在键的列表的头部。如果键不存在,则在执行推送操作之前将其创建为空列表。(从右边插入)

redisTemplate.opsForList().rightPush("names","王五");
redisTemplate.opsForList().rightPush("names","马六");

获取集合长度

redisTemplate.opsForList().size("names");

返回存储在键中的列表的指定元素

System.out.println(redisTemplate.opsForList().range("names",0,-1));
// [李四, 张三, 王五, 马六]

在列表中 index 的位置设置 value 值(如果 index 不存在则报错)

redisTemplate.opsForList().set("names",1,"岳不群");

批量把一个数组插入到列表中

String[] stringarrays = new String[]{"1","2","3"};
redisTemplate.opsForList().leftPushAll("listarray",stringarrays);

从存储在键中的列表中删除等于值的元素的第一个计数事件

  • count > 0:删除等于从头到尾移动的值的元素。
  • count <0:删除等于从尾到头移动的值的元素。
  • count = 0:删除等于value的所有元素
redisTemplate.opsForList().remove("names",1,"王五");

根据下标获取列表中的值(下标从 0 开始)

redisTemplate.opsForList().index("names",2);

操作集合

// 无序集合中添加元素,返回添加个数
String[] strarrays = new String[]{"strarr1","sgtarr2"};
redisTemplate.opsForSet().add("setTest", strarrays);

// 返回集合中的所有成员
redisTemplate.opsForSet().members("setTest");

// 移除集合中一个或多个成员
String[] strarrays = new String[]{"strarr1","sgtarr2"};
redisTemplate.opsForSet().remove("setTest",strarrays);

//无序集合的大小长度
redisTemplate.opsForSet().size("setTest");

// 判断 ccc 元素是否是集合 key 的成员
redisTemplate.opsForSet().isMember("setTest","ccc");

// 随机获取key无序集合中的一个元素
redisTemplate.opsForSet().randomMember("setTest");

操作有序集合

// 新增一个有序集合,存在的话为false,不存在的话为true
template.opsForZSet().add("zset1","zset-1",1.0);

// 从有序集合中移除一个或者多个元素
System.out.println(template.opsForZSet().range("zset1",0,-1)); // 打印所有元素
System.out.println(template.opsForZSet().remove("zset1","zset-6"));
System.out.println(template.opsForZSet().range("zset1",0,-1));
// [zset-1, zset-2, zset-3, zset-4, zset-5, zset-6]
// 1
// [zset-1, zset-2, zset-3, zset-4, zset-5]

// 增加元素的score值,并返回增加后的值
template.opsForZSet().incrementScore("zset1","zset-1",1);

// 通过分数返回有序集合指定区间内的成员,其中有序集成员按分数值递增(从小到大)顺序排列
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-1",1.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-2",6.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-3",8.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-4",4.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-5",10.0));
System.out.println(redisTemplate.opsForZSet().add("zset1","zset-6",2.0));
System.out.println(redisTemplate.opsForZSet().rangeByScore("zset1",0,5));
// [zset-1, zset-6, zset-4]

// 通过分数返回有序集合指定区间内的成员个数
System.out.println(template.opsForZSet().rangeByScore("zset1",0,5));
System.out.println(template.opsForZSet().count("zset1",0,5));

// 获取有序集合的成员数(zCard与size一样)
System.out.println(template.opsForZSet().size("zset1"));

// 获取指定成员的score值
System.out.println(template.opsForZSet().score("zset1","zset-1"));

StringRedisTemplate

StringRedisTemplateRedisTemplate 的一个具体实现,专门用于处理字符串(String)类型的键值对。StringRedisTemplate 默认使用字符串序列化方式来序列化键和值,因此它可以直接与字符串进行交互,而不需要进行复杂的序列化和反序列化操作。

@Resource
private StringRedisTemplate stringRedisTemplate;
@Test
public void stringRedisTemplateTest(){
    HashOperations ho = stringRedisTemplate.opsForHash();
    ho.put("map4072","1001","张三");
    ho.put("map4072","1002","李四");
    ho.put("map4072","1003","王五");
}

在这里插入图片描述

public class StringRedisTemplate extends RedisTemplate<String, String> {
    public StringRedisTemplate() {
        this.setKeySerializer(RedisSerializer.string());
        this.setValueSerializer(RedisSerializer.string());
        this.setHashKeySerializer(RedisSerializer.string());
        this.setHashValueSerializer(RedisSerializer.string());
    }
    public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
        this();
        this.setConnectionFactory(connectionFactory);
        this.afterPropertiesSet();
    }
    protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}

StringRedisTemplate 与 RedisTemplate 区别

StringRedisTemplateRedisTemplate 都是Spring框架为了简化Redis操作而提供的模板类。这两者之间主要的区别在于它们操作的数据类型不同。

数据类型

  • StringRedisTemplate:专门用于字符串(String)类型的数据操作。它继承了RedisTemplate,并且在其基础上进行了专门的配置,默认使用字符串序列化方式。
  • RedisTemplate:是一个通用的模板类,可以用于操作多种数据类型,如字符串(String)、列表(List)、集合(Set)、哈希(Hash)和有序集合(ZSet)等。

序列化方式

  • StringRedisTemplate:默认使用StringRedisSerializer进行序列化,这意味着它存储和读取的数据都是字符串。
  • RedisTemplate:默认使用JdkSerializationRedisSerializer进行序列化,因此它可以存储和读取任何类型的对象,但是这些对象需要实现Serializable接口。

使用场景

  • 如果只处理字符串类型的数据,使用StringRedisTemplate会更方便,因为它不需要考虑序列化问题。
  • 如果需要操作多种数据类型,或者需要存储对象,那么使用RedisTemplate会更合适。

两者数据不共通。即 StringRedisTemplate 只能管理 StringRedisTemplate 里面的数据,RedisTemplate 只能管理 RedisTemplate 中的数据。

StringRedisTemplate 常用操作

存入数据和设置缓存时间

stringRedisTemplate.opsForValue().set("test", "100", 60*10, TimeUnit.SECONDS);
// 将字符串 "100" 存储在键 "test" 下,并设置过期时间为 10 分钟。
stringRedisTemplate.expire("red_123", 1000, TimeUnit.MILLISECONDS);
// 设置键 "red_123" 的过期时间为 1 秒。

值加减操作

stringRedisTemplate.boundValueOps("test").increment(-1); // 将键 "test" 对应的值减一。
stringRedisTemplate.boundValueOps("test").increment(1); // 将键 "test" 对应的值加一。

获取数据和过期时间

stringRedisTemplate.opsForValue().get("test"); // 获取键 "test" 对应的值。
stringRedisTemplate.getExpire("test");
stringRedisTemplate.getExpire("test", TimeUnit.SECONDS);
// 获取键 "test" 的剩余过期时间和指定单位(以秒为单位)的剩余过期时间。

删除缓存

stringRedisTemplate.delete("test"); // 删除键为 "test" 的缓存。

检查键是否存在

stringRedisTemplate.hasKey("546545"); // 检查键 "546545" 是否存在。

Set 集合操作

stringRedisTemplate.opsForSet().add("red_123", "1","2","3");
// 向键 "red_123" 的 Set 集合中添加元素 "1", "2", "3"。
stringRedisTemplate.opsForSet().isMember("red_123", "1")
// 查键 "red_123" 的 Set 集合中是否存在元素 "1"。
stringRedisTemplate.opsForSet().members("red_123");
// 获取键 "red_123" 的 Set 集合中的所有元素。

UserResource 控制器中,注入了 StringRedisTemplate 并在 countNum 方法中使用它来获取和设置用户数量。

@RestController
@RequestMapping("/user")
public class UserResource {
    @Autowired
    public StringRedisTemplate stringRedisTemplate;
    @RequestMapping("/num")
    public String countNum() {
        String userNum = stringRedisTemplate.opsForValue().get("userNum");
        if(StringUtils.isNull(userNum)){
            stringRedisTemplate.opsForValue().set("userNum",
                    userService.countNum().toString());
        }
        return userNum;
    }
}

首先尝试从 Redis 获取键 "userNum" 的值。如果该值不存在(或者为空),则调用 userService.countNum() 方法来获取用户数量,并将其设置回 Redis。最后,返回用户数量。

Redis 工具类

@Component
public class RedisUtil {
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    RedisTemplate<Object, Object> redisTemplate;
    @Resource(name = "stringRedisTemplate")
    ValueOperations<String, String> valOpsStr;
    @Resource(name = "redisTemplate")
    ValueOperations<Object, Object> valOpsObj;
    /**
     * 根据指定key获取String
     * @param key
     * @return
     */
    public String getStr(String key){
        return valOpsStr.get(key);
    }

    /**
     * 设置Str缓存
     * @param key
     * @param val
     */
    public void setStr(String key, String val){
        valOpsStr.set(key,val);
    }

    /***
     * 设置Str缓存
     * @param key
     * @param val
     * @param expire 超时时间
     */
    public void setStr(String key, String val,Long expire){
        valOpsStr.set(key,val,expire, TimeUnit.MINUTES);
    }

    /**
     * 删除指定key
     * @param key
     */
    public void del(String key){
        stringRedisTemplate.delete(key);
    }

    /**
     * 根据指定o获取Object
     * @param o
     * @return
     */
    public Object getObj(Object o){
        return valOpsObj.get(o);
    }

    /**
     * 设置obj缓存
     * @param o1
     * @param o2
     */
    public void setObj(Object o1, Object o2){
        valOpsObj.set(o1, o2);
    }

    /**
     * 删除Obj缓存
     * @param o
     */
    public void delObj(Object o){
        redisTemplate.delete(o);
    }
    /***
     * 加锁的方法
     * @return
     */
    public boolean lock(String key,Long expire){
       RedisConnection redisConnection=redisTemplate.getConnectionFactory().getConnection();
       //设置序列化方法
       redisTemplate.setKeySerializer(new StringRedisSerializer());
       redisTemplate.setValueSerializer(new StringRedisSerializer());
       if(redisConnection.setNX(key.getBytes(),new byte[]{1})){
           redisTemplate.expire(key,expire,TimeUnit.SECONDS);
           redisConnection.close();
           return true;
       }else{
           redisConnection.close();
           return false;
       }
    }
    /***
     * 解锁的方法
     * @param key
     */
    public void unLock(String key){
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.delete(key);
    }
}

http://www.kler.cn/a/460083.html

相关文章:

  • Python实现接口签名调用
  • 网络分析工具-tcpdump
  • 【深度学习】卷积网络代码实战ResNet
  • 需求上线,为什么要刷缓存?
  • 【欢迎讨论方案一的可行性】SpringBoot集成netty,在handler中调用@Component注解的类
  • Linux实验报告12-Apache服务器的配置
  • Android笔试面试题AI答之Android基础(4)
  • elementui的默认样式修改
  • 备战春招 | 数字IC FPGA笔试题
  • 【从零开始入门unity游戏开发之——C#篇32】C#其他不常用的泛型数据结构类、顺序存储和链式存储
  • Python 爬虫中的反爬策略及详细应对方法
  • 【LLM】Langflow 的简单使用
  • SQL实现新年倒计时功能
  • golang标准库SSH操作示例
  • PHP语言laravel框架中基于Redis的异步队列使用实践与原理
  • 【每日学点鸿蒙知识】中间产物版本号问题、Toast应用外不生效、Dialog键盘问题、WebView cookie、本地缓存
  • 如何判断服务器是否被网络攻击?
  • 【C++】九九乘法表编程题详解与多角度对比分析
  • 整合版canal ha搭建--基于1.1.4版本
  • CSS系列(39)-- Shapes详解
  • 服务器反应慢,秒杀设计
  • 实验七 函数2
  • Flutter-插件 scroll-to-index 实现 listView 滚动到指定索引位置
  • 第7章:基于EfficientNet v2网络实现的迁移学习图像分类任务:人体皮肤病识别
  • C语言自定义类型与文件操作
  • 洛谷 P1614 爱与愁的心痛 C(滑动窗口)