0


Redis入门,手把手学习笔记,超详细

文章目录

Redis入门

1.学习目标

在这里插入图片描述

2.Redis的介绍与安装

2.1.Redis是什么?

Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。

2.2.性能

下面是官方的bonch-mark数据:

测试完成了50个并发执行100000个请求。

设置和获取的值是一个256字节字符串。

结果:读的速度是110000次/s,写的速度是81000次/s

2.3.Redis历史简介

2008年,意大利一家创业公司Merzia的创始人Salvatore Sanfilippo为了避免MySQL的低性能,亲自定做一个数据库,并于2009年开发完成,这个就是Redis。

从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。

从2013年5月开始,Redis的开发由Pivotal赞助。

说明: Pivotal公司是由EMC和VMware联合成立的一家新公司。Pivotal希望为新一代的应用提供一个原生的基础,建立在具有领导力的云和网络公司不断转型的IT特性之上。Pivotal的使命是推行这些创新,提供给企业IT架构师和独立软件提供商。

2.4.支持语言

在这里插入图片描述

2.5.支持的数据类型

string、hash、list、set、sorted set

2.6.安装

2.6.1.下载地址

http://redis.io/

2.6.2.安装至服务器

Xftp上传安装

可以用xftp把下载的安装包上传导服务器,然后解压。

Linux命令安装
  • 下载mkdir redisfilecd redisfile/wget http://download.redis.io/releases/redis-5.0.3.tar.gz
  • 解压tar -zxvf redis-5.0.3.tar.gz
  • 安装cd redis-5.0.3makemkdir -p /usr/local/redismake PREFIX=/usr/local/redis/ install> 如果报错了,可能是少了C语言编译环境,因为redis是用C语言编写的。- 配置C语言环境yum -y install gcc-c++ automake autoconf
  • 启动服务器cd /usr/local/redis/bin./redis-server[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o39JWYPf-1646879571836)(D:\笔记Markdown\images\image-20220306181634910.png)]> 为了更方便操作,可以把redis改成后台运行

2.6.3.redis后台运行

cd redis-5.0.3
cp redis.conf /usr/local/redis/bin/
cd /usr/local/redis/bin/
vim redis.conf
#  找到daemonize no,把no改成yes
  • 启动
./redis-server redis.conf
  • 查看是否启动
# 进入redis-cli客户端
./redis-cli
pingset name zhangsan
get name
# 有返回值说明启动成功

2.6.4.安装RDM可视化客户端

教程:https://blog.csdn.net/baidu_35692846/article/details/118399499

  • 安装完后测试连接连接不上,说明只允许本机连接,这时候需要修改confvim redis.conf- 可以加上:bind 允许访问的ip(工作时的操作)- 或者直接在bind 127.0.0.1前面加个#注释掉(学习时的操作)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRC2vSu8-1646879571836)(D:\笔记Markdown\images\image-20220306185716994.png)]- 然后把protected mode 保护模式的yes改成no[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-48wHlPRO-1646879571837)(D:\笔记Markdown\images\image-20220306190026257.png)]- 然后保存并退出:按esc,输入:wq,按回车。- 杀掉redis-server进程# 查看进程ps -ef|grep redis# 杀掉redis-server进程,注意,端口不一定是6588kill -9 6588[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OldUHCaD-1646879571837)(D:\笔记Markdown\images\image-20220306190422910.png)]- 启动./redis-server redis.conf> 客户端尝试连接,如果还是无法连接则关闭防火墙> > > systemctl stop firewalld.service> > [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJJuueki-1646879571838)(D:\笔记Markdown\images\image-20220306192801266.png)]没有密码不太安全,所以需要加上密码vim redis.conf找到requirepass ,然后取消注释,foobared就是密码,可以更改在这里插入图片描述# 查端口ps aux|grep redis# 杀进程kill -9 进程占用的端口号# 再次启动./redis-server redis.conf[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WOawVhRm-1646879571839)(D:\笔记Markdown\images\image-20220306193522661.png)]
  • 这时候我们已经连上了redis,我们发现有16个数据库,如果想增加可以据需更改conf文件,找到databases修改

在这里插入图片描述

  • 修改完我们要在客户端删除原来的连接,然后重启redis,然后重新连接。在这里插入图片描述

3.关系型数据库与非关系型数据库

3.1.关系型数据库

采用关系模型来组织数据的数据库,关系模型就是二维表格模型。一张二维表的表名就是关系,二维表中的一行就是—条记录,二维表中的一列就是一个字段。

3.1.1.优点

  • 容易理解
  • 使用方便,通用的sql语言
  • 易于维护,丰富的完整性(实体完整性、参照完整性和用户定义的完整性)大大降低了数据冗余和数据不一致的 概率

3.1.2.缺点

  • 磁盘I/O是并发的瓶颈
  • 海量数据查询效率低
  • 横向扩展困难,无法简单的通过添加硬件和服务节点来扩展性能和负载能力,当需要对数据库进行升级和扩展时,需要停机维护和数据迁移
  • 多表的关联查询以及复杂的数据分析类型的复杂sql查询,性能欠佳。因为要保证acid,必须按照三范式设计。 数据库

3.1.3.数据库

  • Orcale
  • Sql Server
  • MySql
  • DB2

3.2.非关系型数据库

非关系型,分布式,一般不保证遵循ACID原则的数据存储系统。键值对存储,结构不固定。

3.2.1.优点

  • 根据需要添加字段,不需要多表联查。仅需id取出对应的value
  • 适用于SNS(社会化网络服务软件。比如facebook,微博)
  • 严格上讲不是一种数据库,而是—种数据结构化存储方法的集合缺点
  • 只适合存储一些较为简单的数据。不合适复杂查询的数据
  • 不合适持久荏储海量数据

3.2.2.数据库

  • K-V: Redis,Memcache
  • 文档:MongoDB
  • 搜索:Elasticsearch,Solr
  • 可扩展性分布式:HBase

3.3.比较

内容关系型数据库非关系型数据库成本有些需要收费(Orcale)基本都是开源查询数据存储存于硬盘中,速度慢数据存于缓存中,速度快存储格式只支持基础类型K-V,文档,图片等扩展性有多表查询机制,扩展困难数据之间没有耦合,容易扩展持久性适用持久存储,海量存储不适用持久存储,海量存储数据一致性事务能力强,强调数据的强—致性事务能力弱,强调数据的最终—致性

4.Redis-cli操作Redis

4.1.Redis-cli连接Redis

  • 先开redis服务器
cd /usr/local/redis/bin/
./redis-server redis.conf
  • redis-clli连接redis
# 最后一个是redis的密码    
./redis-cli -p 6379 -a password

4.2.Redis 字符串(String)

Redis 字符串数据类型的相关命令用于管理 redis 字符串值,

4.2.1.存一个值

#字段名为name,值为zhangsanset name zhangsan
# 取名为name的字段
get name

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yCrKYcju-1646879571841)(D:\笔记Markdown\images\image-20220306222521899.png)]

4.2.2.存多个值和取多个值

# 存多组值
mset sex 1 address shanghai
# 取多组值
mget name sex address

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4a1cZbng-1646879571841)(D:\笔记Markdown\images\image-20220306222854411.png)]

4.3.Redis 哈希(Hash)

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

Redis 中每个 hash 可以存储 232 - 1 键值对(40多亿)。

4.3.1.存取单个hash

#这里的user是redis的key,name是hash的key,zhangsan是hash的value
hset user name zhangsan
# 取值
hget user name

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAA6zvxQ-1646879571842)(D:\笔记Markdown\images\image-20220306223441617.png)]

4.3.2.存取多个hash

# 存
hmset user age 18 sex 1
# 取
hmget user name age sex

在这里插入图片描述

4.3.3.取整个redis下的hash

# 取key为user的redis的hash值
hgetall user

在这里插入图片描述

4.3.4.删除操作

# 删除user下的name和age
hdel user name age

在这里插入图片描述

4.4.Redis 列表(List)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)

一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

4.4.1.左添加

# 添加 lis在前zhangsan在后,因为是左添加,每次添加都从左边开始
lpush students zhjangsan lis
# 取
lrange students 0 2

在这里插入图片描述

4.4.2.右添加

# wwangwu在前zhaoliu在后,因为是右添加,每次添加都从右边开始
rpush students wangwu zhaoliu
# 取
lrange students 0 3

在这里插入图片描述

4.4.3.查询长度

# 查students的长度
llen students

在这里插入图片描述

4.4.4.删除

# 删除名为students的list的2条lisi(从左往右删)
lrem students 2 lisi

在这里插入图片描述

4.5.Redis 集合(Set)

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

集合对象的编码可以是 intset 或者 hashtable。

Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。

集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。

4.5.1.添加

# 添加名为letters的set,值分别为aaa、bbb...eee
sadd letters aaa bbb ccc ddd eee

4.5.2.获取

# 查询名为letters的set的数据
smembers letters

在这里插入图片描述

4.5.3.查询长度

# 查询名为letters的set的长度
scard letters

4.5.4.删除

# 删除letters的aaa,ccc
srem letters aaa ccc

4.6.Redis 有序集合(sorted set)

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。

不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。

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

集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。 集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。

4.6.1.增删改查

# score为名称
zadd score 7 zhangsan 3 si 6 wangwu 10 zhaoliu 1 tianqi
# 查值
zrange score
# 删除score下的zhangsan,lisi
zrem score zhangsan lisi
# 查长度
zcard score

在这里插入图片描述

4.7.Redis-cli的通用命令

set cart:user01:item01 apple
get cart:user01:item01

在这里插入图片描述

  • RDM客户端的显示

在这里插入图片描述

4.7.1.设置key的失效时间

Redis有四个不同的命令可以用于设置键的生存时间(键可以存在多久)或过期时间(键什么时候会被删除):

EXP1RE :用于将键key的生存时间设置为tt1秒。

PEXPIRE :用于将键key的生存时间设置为tt1毫秒。

EXPIREAT < timestamp>:用于将键key的过期时间设置为timestamp所指定的秒数时间戳。

PEXPIREAT :用于将键key的过期时间设置为timestamp所指定的毫秒数时间戳

TTL:获取的值为-1说明此key没有设置有效期,当值为-2时证明过了有效期。

  • 方法一# 设置个失效时间为10秒的code,值为testset code test ex 10# 10s内可以取到get code# 查看是否过了失效期ttl code# 10s后失效了get code

在这里插入图片描述

  • 方法2# 10000毫秒失效,最后一个参数,xx表示如果code存在就会设置成功set code test px 10000 xx# 最后一个参数,nx表示如果code不存在就会设置成功set code test px 10000 nx---

4.7.2.删除

# 这里的address就是想删除的key
del address

在这里插入图片描述

5.Java操作redis

5.1.新建项目

5.1.1.IDEA新建项目

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5.1.2.pom.xml配置

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.xmjj</groupId><artifactId>redisdemo</artifactId><version>0.0.1-SNAPSHOT</version><name>redisdemo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><dependency><!--            redis依赖--><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><!--            1.×的版本默认采用的连接池技术是Jedis,2.0 以上版本默认连接池是Lettuce,--><!--            如果采用 Jedis,需要排除Lettuce的依赖。--><exclusions><exclusion><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId></exclusion></exclusions></dependency><!--        jedis依赖--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId></dependency><!--        web组件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--test组件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement></project>

5.1.3.application.yml配置文件

# 应用名称spring:redis:#Redis服务器地址host: 你的Redis服务器地址
    #Redis服务器姗口port:6379#Redis服务器密码password: root
    #选择哪个库,默认e库database:0#连接超时时间timeout: 10000ms
    jedis:pool:#最大连接数,默认8max-active:1024#最大连接阻塞等待时间,单位毫秒,默认-1msmax-wait: 10000ms
        #最大空闲连接,默认8max-idle:200#最小空闲连接,默认emin-idle:5

5.1.4.RedisConfig配置类

package com.xmjj.redisdemo.cofig;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolConfig;/**
 * @author 冰咖啡
 * Date:2022/3/7 11:33
 * Description:
 */@ConfigurationpublicclassRedisConfig{//服务器地址@Value("${spring.redis.host}")private String host;//服务器端口@Value("${spring.redis.port}")privateint port;//服务器密码@Value("${spring.redis.password}")private String password;//连接超时时间@Value("${spring.redis.timeout}")private String timeout;//最大连接数@Value("${spring.redis.jedis.popl.max-active}")privateint maxTotal;//最大连接阻塞等待时间,单位毫秒,默认-1ms@Value("${spring.redis.jedis.popl.max-wait}")private String maxWaitMillis;//最大空闲连接@Value("${spring.redis.jedis.popl.max-idle}")privateint maxIdle;//最小空闲连接@Value("${spring.redis.jedis.popl.min-idle}")privateint minIdle;@Beanpublic JedisPool getJedisPool(){
        JedisPoolConfig jedisPoolConfig =newJedisPoolConfig();//最大连接数
        jedisPoolConfig.setMaxTotal(maxTotal);//最大阻塞时间
        jedisPoolConfig.setMaxWaitMillis(Long.valueOf(maxWaitMillis.substring(0,maxWaitMillis.length()-2)));//最大空闲连接
        jedisPoolConfig.setMaxIdle(maxIdle);//最小空闲连接
        jedisPoolConfig.setMinIdle(minIdle);

        JedisPool jedisPool =newJedisPool(jedisPoolConfig,host,port,Integer.valueOf(timeout.substring(0,timeout.length()-2)));return jedisPool;}}

5.1.5.测试连接

package com.xmjj.redisdemo;import org.junit.After;import org.junit.Before;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.test.context.junit4.SpringRunner;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import java.util.List;@SpringBootTest@RunWith(SpringRunner.class)publicclassRedisdemoApplicationTests{//    @Test//    public void initConnt() {//        //创建Jedis对象,连接redis服务器//        Jedis jedis = new Jedis("192.168.77.134", 6379);//        //设置密码//        jedis.auth("root");//        //指定数据库,默认为0//        jedis.select(1);//        //使用ping命令测试连接是否成功//        String result = jedis.ping();//        System.out.println(result);//        //添加一条数据//        jedis.set("name", "zhangsan");//        //获取数据//        String name = jedis.get("name");//        System.out.println(name);//        //释放资源//        if (jedis != null) {//            jedis.close();//        }//    }//    @Test//    public void initConnt2(){//        //初始化redis连接对象//        JedisPool jedisPool = new JedisPool(new JedisPoolConfig(),"192.168.77.134",6379,10000,"root");//        //从连接池获取jedis对象//        Jedis jedis = jedisPool.getResource();//        //指定数据库,默认为0//        jedis.select(2);//        //使用pingml测试连接是否成功//        String result = jedis.ping();//        System.out.println(result);//        //添加数据//        jedis.set("name","zhangsan");//        //读取数据//        String name = jedis.get("name");//        System.out.println(name);//        //释放资源//        if(jedis!=null){//            jedis.close();//        }//    }@Autowiredprivate JedisPool jedisPool;private Jedis jedis = null;@BeforepublicvoidinitConnt(){try{
            jedis = jedisPool.getResource();}catch(Exception e){
            System.out.println(e);}}@TestpublicvoidtestString(){//使用ping,测试是否正常连接
        String ping = jedis.ping();
        System.out.println(ping);//添加一条数据
        jedis.set("name","zhangsan");//获取一条数据
        String name = jedis.get("name");
        System.out.println(name);//添加多条数据
        jedis.mset("address","sh","sex","1");//获取多条数据、参数的奇数位是key,偶数位是value
        List<String> list = jedis.mget("address","name","sex");
        list.forEach(System.out::println);//删除
        jedis.del("name");}//释放资源@AfterpublicvoidcloseConnt(){if(null!=jedis){
            jedis.close();}}}

在这里插入图片描述

5.1.6.Jedis操作string数据类型

publicvoidtestString(){//使用ping,测试是否正常连接
        String ping = jedis.ping();
        System.out.println(ping);//添加一条数据
        jedis.set("name","zhangsan");//获取一条数据
        String name = jedis.get("name");
        System.out.println(name);//添加多条数据
        jedis.mset("address","sh","sex","1");//获取多条数据、参数的奇数位是key,偶数位是value
        List<String> list = jedis.mget("address","name","sex");
        list.forEach(System.out::println);//删除
        jedis.del("name");}

在这里插入图片描述

5.1.7.Jedis操作Hash数据类型

@TestpublicvoidtestHash(){/**
         * 添加一条数据
         * 第一个参数:   redis的key
         * 第二个参数:   hash的key
         * 第三个参数:   hash的value
         */
        jedis.hset("user","name","zhangsan");/**
         * 获取一条数据
         * 第一个参数:   redis的key
         * 第二个参数:   hash的key
         */
        String name = jedis.hget("user","name");
        System.out.println(name);/**
         * 添加多条数据
         */
        Map<String,String> map =newHashMap<>();
        map.put("age","20");
        map.put("sex","1");
        jedis.hmset("user",map);/**
         * 获取多条数据
         */
        List<String> list = jedis.hmget("user","age","sex");
        list.forEach(System.out::println);/**
         * 获取hash类型的所有的数据
         */
        Map<String, String> user = jedis.hgetAll("user");
        user.entrySet().forEach(e->{
            System.out.println(e.getKey()+"----->"+e.getValue());});/**
         * 删除
         * 一个参数: redis的key
         * 后面的参数: hash的key (可变)
         */
        jedis.hdel("user","name","sex");}

在这里插入图片描述

5.1.8.Jedis操作List数据类型

@TestpublicvoidtestList(){//左添加
        jedis.lpush("students","zhangsan","lisi");//右添加
        jedis.rpush("students","wangwu","zhaoliu");/**
         * 获取数据
         * 第一个参数: redis的key
         * 第二个参数:起始下标
         * 第三个参数:结束下标
         */
        List<String> students = jedis.lrange("students",0,3);
        students.forEach(System.out::println);//获取总套数
        Long total = jedis.llen("students");
        System.out.println(total);/**
         *删除数据
         * 第一个参数:redis的key
         * 第二个参数:删除值的个数
         * 第三个参数:要删除的值
         */
        jedis.lrem("students",1,"lisi");//获取总套数
        total = jedis.llen("students");
        System.out.println(total);//左弹出
        System.out.println(jedis.lpop("students"));//右弹出
        System.out.println(jedis.rpop("students"));//获取总套数
        total = jedis.llen("students");
        System.out.println(total);}

在这里插入图片描述

5.1.9.Jedis操作Set数据类型

@TestpublicvoidtestSet(){//添加数据
        jedis.sadd("letters","aaa","bbb","ccc","ddd","eee");//获取数据
        Set<String> set = jedis.smembers("letters");
        set.forEach(System.out::println);//获取总条数
        Long total = jedis.scard("letters");//删除数据
        jedis.srem("letters","aaa","ccc");
        System.out.println(jedis.scard("letters"));}

在这里插入图片描述

5.1.10.Jedis操作SortedSet数据类型

@TestpublicvoidtestSortedSet(){//添加数据
        Map<String,Double> map =newHashMap<>();
        map.put("zhangsan",7D);
        map.put("lisi",3D);
        map.put("wangwu",5D);
        map.put("zhaoliu",6D);
        map.put("tianqi",1D);
        jedis.zadd("score",map);/**
         * 获取数据
         * 第一个参数:redis的key
         * 第二个参数:起始下标
         * 第三个参数:结束下标
         */
        Set<String> set = jedis.zrange("score",0,4);
        set.forEach(System.out::println);//获取总条数
        Long total = jedis.zcard("score");
        System.out.println(total);//删除
        jedis.zrem("score","zhangsan","wangwu");
        System.out.println(jedis.zcard("score"));}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wpfv4hQM-1646879571862)(D:\笔记Markdown\images\image-20220307180220236.png)]

5.1.11.层级目录+失效时间

  • 层级目录
@TestpublicvoidtestDDir(){
        jedis.set("cart:user01:item01","apple");
        System.out.println(jedis.get("cart:user01:item01"));}

在这里插入图片描述

  • 失效时间
@TestpublicvoidtestExpire(){//给已经存在在key设置失效时间//        jedis.set("code","test");//设置失效时间,单位秒//        jedis.expire("code",30);//设置失效时间,单位毫秒//        jedis.pexpire("code",30000);//        //查看失效时间,-1为未失效,-2为已失效//        Long ttl = jedis.ttl("code");//        System.out.println(ttl);//设置失效时间,单位秒
        jedis.setex("code",30,"test");//设置失效时间,单位毫秒
        jedis.psetex("code",30000,"test");//查看失效时间,单位毫秒
        Long pttl = jedis.pttl("code");
        System.out.println(pttl);//        SetParams setParams = new SetParams();//        //不存在则设置成功//        setParams.nx();//        //存在你则设置成功        setParams.xx();//        //设置失效时间,单位秒        setParams.ex(30);//        //设置失效时间,单位毫秒//        setParams.px(30000);//        jedis.set("code","test",setParams);}

在这里插入图片描述

5.1.12.获取所有的key+事务

  • 获取key
@TestpublicvoidtestAllKey(){//当前数据库key的数量
        Long size = jedis.dbSize();
        System.out.println(size);//查询当前数据库所有的key
        Set<String> set = jedis.keys("*");
        set.forEach(System.out::println);}
  • 事务
@TestpublicvoidtestMulti(){//开启事务
        Transaction tx = jedis.multi();
        tx.set("tel","10086");//提交事务
        tx.exec();//回滚事务//        tx.discard();}

5.1.13.Jedis获取操作byte数组

  • 系列化工具类
package com.xmjj.redisdemo.util;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/**
 * @author 冰咖啡
 * Date:2022/3/7 22:13
 * Description:
 */publicclassSerializeUtil{/**
     * 将java对象转换为byte数组,序列化过程
     * @param object
     * @return
     */publicstaticbyte[]serialize(Object object){
        ObjectOutputStream oos = null;
        ByteArrayOutputStream baos = null;try{// 序列化
            baos =newByteArrayOutputStream();
            oos =newObjectOutputStream(baos);
            oos.writeObject(object);byte[] bytes = baos.toByteArray();return bytes;}catch(Exception e){
            e.printStackTrace();}return null;}/**
     * 将byte数组转换为java对象,反序列化
     * @param bytes
     * @return
     */publicstatic Object unserialize(byte[] bytes){
        ByteArrayInputStream bais = null;try{// 反序列化
            bais =newByteArrayInputStream(bytes);
            ObjectInputStream ois =newObjectInputStream(bais);return ois.readObject();}catch(Exception e){
            e.printStackTrace();}return null;}}
  • User实体类
package com.xmjj.redisdemo.pojo;import java.io.Serializable;/**
 * @author 冰咖啡
 * Date:2022/3/7 22:16
 * Description:
 */publicclassUserimplementsSerializable{privatestaticfinallong serivalVersionUID =9148937431079191022L;private Integer id;publicUser(){}@Overridepublic String toString(){return"User{"+"id="+ id +", username='"+ username +'\''+", password='"+ password +'\''+'}';}publicstaticlonggetSerivalVersionUID(){return serivalVersionUID;}public Integer getId(){return id;}publicvoidsetId(Integer id){this.id = id;}public String getUsername(){return username;}publicvoidsetUsername(String username){this.username = username;}public String getPassword(){return password;}publicvoidsetPassword(String password){this.password = password;}publicUser(Integer id, String username, String password){this.id = id;this.username = username;this.password = password;}private String username;private String password;}
  • 测试
@TestpublicvoidtestByte(){
        User user =newUser();
        user.setId(2);
        user.setUsername("zhangsan");
        user.setPassword("123456");//序列化为byte数组byte[] userKey = SerializeUtil.serialize("user:"+ user.getId());byte[] userValue = SerializeUtil.serialize(user);//        存入redis
        jedis.set(userKey,userValue);//取出byte[] bytes = jedis.get(userKey);//反序列化
        User user1 =(User) SerializeUtil.unserialize(bytes);
        System.out.println(user1);}

在这里插入图片描述

5.1.14.redis持久化方案

redis作为一个内存数据库,最担心的,就是万一机器死机宕机,数据就会消失掉,为了缓解这个问题,我们有三个方案。

BGSAVE机制
# 在后台异步保存当前数据库的数据到磁盘。
bgsave

优点:简单,缺点:繁琐


Redis 提供了 RDB 和 AOF 两种持久化方案,将内存中的数据自动保存到磁盘中
AOF持久化以独立日志的方式记录每次写命令,并在 Redis 重启时在重新执行。AOF 文件中的命令以达到恢复数据的目的。AOF 的主要作用是解决数据持久化的实时性RDB把当前 Redis 进程的数据生成时间点快照( point-in-time snapshot ) 保存到存储设备的过程。

RDB持久化

RDB是Redis用来进行持久化的一种方式,是把当前内存中的数据集快照写入磁盘,也就是 Snapshot 快照(数据库中所有键值对数据)。恢复时是将快照文件直接读到内存里。

RDB 有两种触发方式,分别是自动触发和手动触发。

  • 进入redis.conf配置文件,修改save
save 900 1:表示900 秒内如果至少有 1 个 key 的值变化,则保存
save 300 10:表示300 秒内如果至少有 10 个 key 的值变化,则保存
save 60 10000:表示60 秒内如果至少有 10000 个 key 的值变化,则保存

在这里插入图片描述

修改完保存重启即可。

AOF持久化

AOF通过4点实现持久化
写入缓存每次执行命令后,进行append操作写入AOF缓存同步磁盘AOF 缓冲区根据对应的策略向硬盘进行同步操作AOF重写随着 AOF 文件越来越大,需要定期对 AOF 文件进行重写,达到压缩的目的重启加载当 Redis 重启时,可以加载 AOF 文件进行数据恢复

  • 进入redis.conf配置文件,修改appendonly,no改成yes

在这里插入图片描述

  • 重启 Redis 之后就会进行 AOF 文件的载入。
  • 异常修复命令:redis-check-aof --fix

6.Redis主从复用

6.1.读写分离

#引用公共配置
include /opt/redis/conf/redis-common.conf
#进程编号记录文件
pidfile "/var/run/redis-6380.pid"#进程端口号
port 6380
#日志记录文件
logfile "/opt/redis/log/redis-6380.log"#数据记录文件
dbfilename "dump-6380.rdb"#追加文件名称
appendfilename "appendonly-6380.aof"#下面的配置无需在 6379 里配置#备份服务器从属于 6379 推荐配置配局域网 IP
slaveof 192.168.40.100 6379

6.1.1.启动

cd /usr/local/redis/bin
./redis-server /opt/redis/conf/redis-6379.conf
./redis-server /opt/redis/conf/redis-6380.conf
./redis-server /opt/redis/conf/redis-6381.conf

6.1.2.进入客户端

#分别在不同会话打开
./redis-cli -p 6379 -a root
./redis-cli -p 6380 -a root
./redis-cli -p 6381 -a root

6.1.3.主从状态查看

info replication

在这里插入图片描述

6.1.4.读写操作

主服务器可读可写从服务器只读
在主服务器设置的值,在从服务器可以读取。

但是从服务器不能写,只能读。

在这里插入图片描述

6.2.哨兵配置

6.2.1.简介

当主服务器宕机后,需要手动把一台从服务器切换为主从服务器,这就需要人工干预,既费时费力,还会造成一段时间内服务不可用,这不是一种推荐的方式,因此笔者没有介绍主从切换技术。

更多的时候,我们优先考虑哨兵模式,它是当前企业应用的主流方式。

Redis可以存在多台服务器,并且实现了主从复制的功能。哨兵模式是一种特殊的模式,首先 Redis 提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。

其原理是哨兵通过发送命令,等待 Redis 服务器响应,从而监控运行的多个 Redis 实例

这里的哨兵有两个作用:

  • 通过发送命令,让 Redis 服务器返回监测其运行状态,包括主服务器和从服务器。
  • 当哨兵监测到 master 宕机,会自动将 slave 切换成 master,然后通过发布订阅模式通知到其他的从服务器,修改配置文件,让它们切换主机。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OcYrfbaW-1646879571866)(D:\笔记Markdown\images\未命名文件 (1)].png)

6.2.2.哨兵的安装

cd /root/redisfile/redis-5.0.3

# 把sentinel.conf复制到/opt/redis/conf/目录下cp sentinel.conf /opt/redis/conf/

cd /opt/redis/conf
#改名mv sentinel.conf sentinel-common.conf
#编辑配置文件
vim sentinel-common.conf
  • 把port注释掉

在这里插入图片描述

  • 把daomonize后台启动设置为yes

在这里插入图片描述

  • 把pidfile进程文件注释掉

在这里插入图片描述

  • 把logfile日志文件注释掉

在这里插入图片描述

  • 修改哨兵模式的iip改成redis服务器的ip

在这里插入图片描述

  • 主服务器的密码,这里的root是密码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tdNlBZr0-1646879571868)(D:\笔记Markdown\images\image-20220309144306890.png)]
  • 在主服务器30秒没有ping-pong响应时,重新选取[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O2WwLtvX-1646879571869)(D:\笔记Markdown\images\image-20220309144537689.png)]
  • 当选取服务器超过180秒都没有超过。那么将放弃此次选取。进行新的选取[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WOJyiLNJ-1646879571869)(D:\笔记Markdown\images\image-20220309144715025.png)]

touch sentinel-26379.conf
touch sentinel-26380.conf
touch sentinel-26381.conf
#分别配置
vim sentinel-26379.conf
vim sentinel-26380.conf
vim sentinel-26381.conf
  • sentinel-26379.conf
#引用公共配置
include /opt/redis/conf/sentinel-common.conf
#进程端口号
port 26379
#进程编号记录文件
pidfile /var/run/sentinel-26379.pid
#日志记录文件(为了方便查看日志,先注释掉,搭好环境后再打开)
logfile "/opt/redis/log/sentinel-26379.log"
  • sentinel-26380.conf
#引用公共配置
include /opt/redis/conf/sentinel-common.conf
#进程端口号
port 26380
#进程编号记录文件
pidfile /var/run/sentinel-26380.pid
#日志记录文件(为了方便查看日志,先注释掉,搭好环境后再打开)
logfile "/opt/redis/log/sentinel-26380.log"
  • sentinel-26381.conf
#引用公共配置
include /opt/redis/conf/sentinel-common.conf
#进程端口号
port 26381
#进程编号记录文件
pidfile /var/run/sentinel-26381.pid
#日志记录文件(为了方便查看日志,先注释掉,搭好环境后再打开)
logfile "/opt/redis/log/sentinel-26381.log"

6.2.3.主备切换

cd /usr/local/redis/bin
# 启动
./redis-sentinel /opt/redis/conf/sentinel-26379.conf
./redis-sentinel /opt/redis/conf/sentinel-26380.conf
./redis-sentinel /opt/redis/conf/sentinel-26381.conf
# 查看是否启动成功ps -ef|grep redis

在这里插入图片描述

tail -f /opt/redis/log/sentinel-26379.log

在这里插入图片描述

  • 将主服务器杀死
ps -ef|grep redis
kill 3113

在这里插入图片描述

  • 查看6379的log

在这里插入图片描述

# 查看服务器状态
./redis-cli -p 6379 -a root
info replication

在这里插入图片描述

  • 重启6379服务器./redis-server /opt/redis/conf/redis-6379.conf- 6379的状态[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IDnE1pmx-1646879571872)(D:\笔记Markdown\images\image-20220309162126673.png)]- 6380的状态[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pq5urWre-1646879571873)(D:\笔记Markdown\images\image-20220309162338871.png)]

此时6380是主服务器

在6380设置一个值,在6379可以读取,但是6379设置一个值显示ERROR,因为此时6379是从服务器,6379是只读的状态了。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PRKB689s-1646879571873)(D:\笔记Markdown\images\image-20220309162544494.png)]


7.SpringDataRedis

SpringDataRedis是Spring大家族中的一个成员,提供了在srping应用中通过简单的配置访问redis服务,对reids底层开发包(Jedis, JRedis, and RJC)进行了高度封装,RedisTemplate提供了redis各种操作、异常处理及序列化,支持发布订阅,并对spring 3.1 cache进行了实现。 spring-data-redis针对jedis提供了如下功能:

  • 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
  • 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口

官网:https://spring.io/projects/spring-data-redis

7.1.创建项目

7.1.1.新建项目

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3jsvscfO-1646879571874)(D:\笔记Markdown\images\image-20220309163959351.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rC93uBMZ-1646879571874)(D:\笔记Markdown\images\image-20220309164052855.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dGE0Mdp1-1646879571875)(D:\笔记Markdown\images\image-20220309164112261.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRLkDP74-1646879571875)(D:\笔记Markdown\images\image-20220309164137143.png)]

7.1.2.pom.xml配置文件

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.xmj</groupId><artifactId>springdataredis-demo</artifactId><version>0.0.1-SNAPSHOT</version><name>springdataredis-demo</name><description>Demo project for Spring Boot</description><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><spring-boot.version>2.3.7.RELEASE</spring-boot.version></properties><dependencies><!--        SpringDataRedis依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--        commons-pool2 对象连接池依赖--><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><dependency><!--            web组件--><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--            test组件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><source>1.8</source><target>1.8</target><encoding>UTF-8</encoding></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.3.7.RELEASE</version><configuration><mainClass>com.xmj.springdataredisdemo.SpringdataredisDemoApplication</mainClass></configuration><executions><execution><id>repackage</id><goals><goal>repackage</goal></goals></execution></executions></plugin></plugins></build></project>

7.1.3.application.yml配置文件

spring:redis:# Redis服务器地址host: 192.168.77.134
    # Redis服务器端口port:6380# Redis服务器密码password: root
    # Redis服务器数据库database:0# 连接超时时间timeout: 10000ms
    lettuce:pool:#最大连接数max-active:1024# #最大连接阻塞等待时间,单位毫秒,默认-1msmax-wait: 10000ms
        ##最大空闲连接,默认8max-idle:200##最小空闲连接,默认0min-idle:5

7.1.4.测试连接

package com.xmj.springdataredisdemo;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.data.redis.core.ValueOperations;@SpringBootTestclassSpringdataredisDemoApplicationTests{@Autowiredprivate RedisTemplate<String,String> redisTemplate;@Autowiredprivate StringRedisTemplate stringRedisTemplate;@TestpublicvoidinitCoon(){
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("name","zhangsan");
        System.out.println((String) ops.get("name"));
        ValueOperations<String, String> stringStringValueOperations = stringRedisTemplate.opsForValue();
        stringStringValueOperations.set("age","20");
        String age = stringStringValueOperations.get("age");
        System.out.println(age);}}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qaoTzpcw-1646879571876)(D:\笔记Markdown\images\image-20220309170315130.png)]


7.2.SpringDataRedis序列化模板

7.2.1.自定义模板解决序列化问题

默认情况下的模板RedisTemplate<Object, Object>,默认序列化使用的是JdkserializationRedisSerializer,存储二进制字节码。这时需要自定义模板,半自定义模板后又想存储String 字符串时,可以使StringRedisTemplate的方式,他们俩并不冲突。

序列化问题:
要把 domain object做为key-value对保存在redis 中,就必须要解决对象的序列化问题。Spring Data Redis给我们提供了一些现成的方案:

jdkserializationRedisSerializer使用JDK提供的序列化功能。优点是反序列化时不需要提供类型信息(class),但缺点是序列化后的结果非常庞大,是]SON格式的5倍左右,这样就会消耗Redis 服务器的大星内存。

lackson23sonRedisSerializer使用Jackson库将对象序列化为SON字符串。优点是速度快,序列化后的字符串短小精悍。但缺点也非常致命,那就是此类的构造函数中有一个类型参数,必须提供要序列化对象的类型信息(.class对象)。通过查看源代码,发现其只在反序列化过程中用到了类型信息。

Generic]ackson2]sonRedisserializer通用型序列化,这种序列化方式不用自己手动指定对象的Class。

RedisConfig配置类
package com.xmj.springdataredisdemo.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.lettuce.LettuceConnection;import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;import org.springframework.data.redis.serializer.StringRedisSerializer;/**
 * @author 冰咖啡
 * Date:2022/3/9 17:11
 * Description:
 */@ConfigurationpublicclassRedisConfig{@Beanpublic RedisTemplate<String,Object>redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
        RedisTemplate<String,Object> redisTemplate =newRedisTemplate<>();//为String类型的key设置序列化
        redisTemplate.setKeySerializer(newStringRedisSerializer());//为string类型的value设置序列化
        redisTemplate.setValueSerializer(newGenericJackson2JsonRedisSerializer());//为hash类型的key设置序列化
        redisTemplate.setHashKeySerializer(newStringRedisSerializer());//为hash类型的value设置序列化
        redisTemplate.setHashValueSerializer(newGenericJackson2JsonRedisSerializer());
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);return redisTemplate;}}
User实体类
package com.xmj.springdataredisdemo.pojo;import java.io.Serializable;/**
 * @author 冰咖啡
 * Date:2022/3/9 17:18
 * Description:
 */publicclassUserimplementsSerializable{private Integer id;private String name;private Integer age;@Overridepublic String toString(){return"User{"+"id="+ id +", name='"+ name +'\''+", age="+ age +'}';}public Integer getId(){return id;}publicvoidsetId(Integer id){this.id = id;}public String getName(){return name;}publicvoidsetName(String name){this.name = name;}public Integer getAge(){return age;}publicvoidsetAge(Integer age){this.age = age;}}
测试
/**
     * 测试序列化
     */@TestpublicvoidtestSerial(){
        User user =newUser();
        user.setId(1);
        user.setName("zhangsan");
        user.setAge(20);
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("user",user);
        Object user1 = ops.get("user");
        System.out.println(user1);}
SpringDataRedis操作string数据类型
@TestpublicvoidtestString(){
        ValueOperations ops = redisTemplate.opsForValue();//添加一条数据
        ops.set("name","zhangsan");//获取一条数据
        Object name = ops.get("name");
        System.out.println(name);//层级关系,目录形式存储数据
        ops.set("user:01","list");//添加多条数据
        Map<String,String> map=newHashMap<>();
        map.put("age","20");
        map.put("address","sh");
        ops.multiSet(map);//
        List<String> keys =newArrayList<>();
        keys.add("name");
        keys.add("age");
        keys.add("address");
        List list = ops.multiGet(keys);
        list.forEach(System.out::println);//删除数据
        redisTemplate.delete("name");
        list = ops.multiGet(keys);
        list.forEach(System.out::println);}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TKKGbU4w-1646879571877)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220309235021777.png)]


SpringDataRedis操作hash数据类型
@TestpublicvoidtestHash(){
        HashOperations hashOperations = redisTemplate.opsForHash();/**
         * 添加一条数据
         * 第一个数据:redis的key
         * 第二个数据:hash的key
         * 第三个数据:hash的value
         */
        hashOperations.put("user","name","zhangsan");/**
         * 获取一条数据
         * 第一个参数:redis的key
         * 第二个参数:hash的key
         */
        hashOperations.get("user","name");//添加多条数据
        Map<String,String> map=newHashMap<>();
        map.put("age","20");
        map.put("address","sh");
        hashOperations.putAll("user",map);//获取多条数据
        List<String> keys =newArrayList<>();
        keys.add("name");
        keys.add("age");
        keys.add("address");
        List user =  hashOperations.multiGet("user", keys);
        user.forEach(System.out::println);//获取hash类型的所有数据
        Map<String, String> entries = hashOperations.entries("user");
        entries.entrySet().forEach(e->{
            System.out.println(e.getKey()+"--->"+e.getValue());});//hash的删除
        hashOperations.delete("user2","name","age");}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GWpmnLlH-1646879571877)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310001353744.png)]

SpringDataRedis操作list
@TestpublicvoidtestList(){
        ListOperations listOperations = redisTemplate.opsForList();//左添加
        listOperations.leftPush("students","wangwu");
        listOperations.leftPush("students","lisi");/**
         * 左添加
         * 第一个参数:redis的key
         * 第二个参数:被左添加的数据
         * 第三个参数:添加的数据,添加到第二个数据左边
         */
        listOperations.leftPush("students","wangwu","zhangsan");//右添加
        listOperations.rightPush("students","zhaoliu");
        listOperations.rightPush("students","tianqi");//获取数据
        List list = listOperations.range("students",0,2);
        list.forEach(System.out::println);//获取总条数
        Long size = listOperations.size("students");
        System.out.println(size);//删除数据
        listOperations.remove("students",1,"lisi");//左弹出
        listOperations.leftPop("students");//右弹出
        listOperations.rightPop("students");

        list = listOperations.range("students",0,2);
        list.forEach(System.out::println);}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hs1zgVOH-1646879571878)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310002649242.png)]

SpringDataRedis操作set数据类型
@TestpublicvoidtestSet(){
        SetOperations setOperations = redisTemplate.opsForSet();//添加数据
        String[] letters =newString[]{"aaa","bbb","ccc","ddd"};
        setOperations.add("letters",letters);
        setOperations.add("letters",letters);//获取数据
        Set set = setOperations.members("letters");
        set.forEach(System.out::println);//删除数据
        setOperations.remove("letters","aaa","bbb");
        set = setOperations.members("letters");
        set.forEach(System.out::println);}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-602KZQ8k-1646879571878)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310003216045.png)]

SpringDataRedis操作sortedset数据类型
@TestpublicvoidtestSortedSet(){
        ZSetOperations zSetOperations = redisTemplate.opsForZSet();//添加数据
        ZSetOperations.TypedTuple<Object> objectTypedTuple1 =newDefaultTypedTuple<>("zhangsan",3D);
        ZSetOperations.TypedTuple<Object> objectTypedTuple2 =newDefaultTypedTuple<>("lisi",4D);
        ZSetOperations.TypedTuple<Object> objectTypedTuple3 =newDefaultTypedTuple<>("wangwu",5D);
        ZSetOperations.TypedTuple<Object> objectTypedTuple4 =newDefaultTypedTuple<>("zhaoliu",6D);
        ZSetOperations.TypedTuple<Object> objectTypedTuple5 =newDefaultTypedTuple<>("tianqi",7D);
        Set<ZSetOperations.TypedTuple<Object>> tuples =newHashSet<>();
        tuples.add(objectTypedTuple1);
        tuples.add(objectTypedTuple2);
        tuples.add(objectTypedTuple3);
        tuples.add(objectTypedTuple4);
        tuples.add(objectTypedTuple5);
        zSetOperations.add("score",tuples);
        Set score = zSetOperations.range("score",0,4);
        score.forEach(System.out::println);
        Long size = zSetOperations.size("score");
        System.out.println(size);
        zSetOperations.remove("score","zhangsan","lisi");
        score = zSetOperations.range("score",0,4);
        score.forEach(System.out::println);}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpiEJHP2-1646879571879)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310004613247.png)]

SpringData获取所有key+设置key失效时间
/**
     * 获取所有的key
     */@TestpublicvoidtestAllKey(){
        Set<String> keys = redisTemplate.keys("*");
        keys.forEach(System.out::println);}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-84BRHHFt-1646879571879)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310100049525.png)]

/**
     * 失效时间
     */@TestpublicvoidtestExpire(){//        ValueOperations ops = redisTemplate.opsForValue();//        ///方法一,添加key的时候设置失效时间//        ops.set("code","test",30, TimeUnit.SECONDS);
        redisTemplate.expire("name",30,TimeUnit.SECONDS);//查看失效时间
        Long expire = redisTemplate.getExpire("code");
        System.out.println(expire);
        expire = redisTemplate.getExpire("name");
        System.out.println(expire);}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5KTT2be-1646879571880)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310100021207.png)]

SpringData整合哨兵模式
  • appllication.yml配置
spring:redis:# Redis服务器地址host: 192.168.77.134
    # Redis服务器端口port:6380# Redis服务器密码password: root
    # Redis服务器数据库database:0# 连接超时时间timeout: 10000ms
    lettuce:pool:#最大连接数max-active:1024# #最大连接阻塞等待时间,单位毫秒,默认-1msmax-wait: 10000ms
        ##最大空闲连接,默认8max-idle:200##最小空闲连接,默认0min-idle:5# 哨兵模式sentinel:# 主节点名称master: mymaster
      # 节点nodes: 192.168.77.134:26379,192.168.77.134:26380,192.168.77.134:26381
  • RedisConfig配置类的RedisSentinelConfiguration方法@Beadnpublic RedisSentinelConfiguration redisSentinelConfiguration(){ RedisSentinelConfiguration redisSentinelConfiguration =newRedisSentinelConfiguration()//主节点名称.master("mymaster")//哨兵.sentinel("192.168.77.134",26379).sentinel("192.168.77.134",26380).sentinel("192.168.77.134",26381); redisSentinelConfiguration.setPassword("root");return redisSentinelConfiguration;}

8.如何应对缓存穿透、缓存击穿、缓存雪崩问题

8.1.Key的过期淘汰机制

Redis可以对存储在Redis中的缓存数据设置过期时间,比如我们获取的短信验证码一般十分钟过期,我们这时候就需要在验证码存进Redis时添加一个key的过期时间,但是这里有一个需要格外注意的问题就是:并非key过期时间到了就一定会被Redis给删除。

8.1.1.定期删除

Redis默认是每隔100ms就随机抽取一些设置了过期时间的Key,检查其是否过期,如果过期就删除。为什么是随机抽取而不是检查所有key?因为你如果设置的key成千上万,每100毫秒都将所有存在的key检查一遍,会给CPU带来比较大的压力。

8.1.2.惰性删除

定期删除由于是随机抽取可能会导致很多过期Key到了过期时间并没有被删除。所以用户在从缓存获取数据的时候,redis会检查这个key是否过期了,如果过期就删除这个key。这时候就会在查询的时候将过期key从缓存中清除。

8.1.3.内存淘汰机制

仅仅使用定期删除+惰性删除机制还是会留下一个严重的隐患:如果定期删除留下了很多已经过期的key,而且用户长时间都没有使用过这些过期key,导致过期key无法被惰性删除,从而导致过期key一直堆积在内存里,最终造成Redis内存块被消耗殆尽。那这个问题如何解决呢?这个时候Redis内存淘汰机制应运而生了。Redis内存淘汰机制提供了6种数据淘汰策略:

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  • allkeys-lru:当内存不足以容纳新写入数据时移除最近最少使用的key。
  • allkeys-random:从数据集中任意选择数据淘汰。
  • no-enviction:当内存不足以容纳新写入数据时,新写入操作会报错。

一般情况下,推荐使用volatile-lru策略,对于配置信息等重要数据,不应该设置过期时间,这样Redis就永远不会淘汰这些重要数据。对于一般数据可以添加一个缓存时间,当数据失效则请求会从DB中获取并重新存入Redis中。

8.2.缓存击穿

首先我们来看下请求是如何取到数据的:当接收到用户请求,首先先尝试从Redis缓存中获取到数据,如果缓存中能取到数据则直接返回结果,当缓存中不存在数据时从DB获取数据,如果数据库成功取到数据,则更新Redis,然后返回数据,如果DB无数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOrBwOto-1646879571880)(C:\Users\19664\AppData\Roaming\Typora\typora-user-images\image-20220310101849416.png)]

8.2.1.定义

高并发的情况下,某个热门key突然过期,导致大星请求在Redis未找到缓存数据,进而全部去访问DB请求数据,引起DB压力瞬间增大。

8.2.2.解决方案

缓存击穿的情况下一般不容易造成DB的宕机,只是会造成对DB的周期性压力。对缓存击穿的解决方案—般可以这样:

  • Redis中的数据不设置过期时间,然后在缓存的对象上添加一个属性标识过期时间,每次获取到数据时,校验对象中的过期时间属性,如果数据即将过期,则异步发起一个线程主动更新缓存中的数据。但是这种方案可能会导致有些请求会拿到过期的值,就得看业务能否可以接受,
  • 如果要求数据必须是新数据,则最好的方案则为热点数据设置为永不过期,然后加一个互斥锁保证缓存的单线程写。

8.3.缓存穿透

8.3.1.定义

缓存穿透是指查询缓存和DB中都不存在的数据。比如通过id查询商品信息,id一般大于0,攻击者会故意传id为-1去查询,由于缓存是不命中则从DB中获取数据,这将会导致每次缓存都不命中数据导致每个请求都访问DB,造成缓存穿透。

8.3.2.解决方案:

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠
  • 段时间重试
  • 采用异步更新策略,无论key是否取到值,都直接返回。value值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。
  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的key。迅速判断出,请求所携带的Key是否合法有效。如果不合法,则直接返回。
  • 如果从数据库查询的对象为空,也放入缓存,只是设定的缓存过期时间较短,比如设置为60秒

8.4.缓存雪崩

8.4.1.定义

缓存中如果大量缓存在一段时间内集中过期了,这时候会发生大量的缓存击穿现象,所有的请求都落在了DB上,由于查询数据量巨大,引起DB压力过大甚至导致DB宕机。

8.4.2.解决方案

  • 给缓存的失效时间,加上一个随机值,避免集体失效。如果Redis是集群部署,将热点数据均匀分布在不同的Redis库中也能避免全部失效的问题
  • 使用互斥锁,但是该方案吞吐量明显下降了。
  • 设置热点数据永远不过期。
  • 双缓存。我们有两个缓存,缓存A和缓存B。缓存A的失效时间为20分钟,缓存B不设失效时间。自己做缓存预热操作。然后细分以下几个小点 - 1.从缓存A读数据库,有则直接返回- 2.A没有数据,直接从B读数据,直接返回,并且异步启动一个更新线程。- 3.更新线程同时更新缓存A和缓存B。 到了就一定会被Redis给删除。
定期删除

Redis默认是每隔100ms就随机抽取一些设置了过期时间的Key,检查其是否过期,如果过期就删除。为什么是随机抽取而不是检查所有key?因为你如果设置的key成千上万,每100毫秒都将所有存在的key检查一遍,会给CPU带来比较大的压力。

惰性删除

定期删除由于是随机抽取可能会导致很多过期Key到了过期时间并没有被删除。所以用户在从缓存获取数据的时候,redis会检查这个key是否过期了,如果过期就删除这个key。这时候就会在查询的时候将过期key从缓存中清除。

内存淘汰机制

仅仅使用定期删除+惰性删除机制还是会留下一个严重的隐患:如果定期删除留下了很多已经过期的key,而且用户长时间都没有使用过这些过期key,导致过期key无法被惰性删除,从而导致过期key一直堆积在内存里,最终造成Redis内存块被消耗殆尽。那这个问题如何解决呢?这个时候Redis内存淘汰机制应运而生了。Redis内存淘汰机制提供了6种数据淘汰策略:

  • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。
  • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
  • volatile-random:从已设置过期时间的数据集中任意选择数据淘汰。
  • allkeys-lru:当内存不足以容纳新写入数据时移除最近最少使用的key。
  • allkeys-random:从数据集中任意选择数据淘汰。
  • no-enviction:当内存不足以容纳新写入数据时,新写入操作会报错。

一般情况下,推荐使用volatile-lru策略,对于配置信息等重要数据,不应该设置过期时间,这样Redis就永远不会淘汰这些重要数据。对于一般数据可以添加一个缓存时间,当数据失效则请求会从DB中获取并重新存入Redis中。

标签: redis 学习 java

本文转载自: https://blog.csdn.net/qq_25015861/article/details/123395203
版权归原作者 冰咖啡iii 所有, 如有侵权,请联系我们删除。

“Redis入门,手把手学习笔记,超详细”的评论:

还没有评论