Redis快速学习

news2024/12/23 6:01:48

Redis快速学习

  • 一、 Redis快速入门
    • 1.1 初始Redis
      • 1.1.1 Redis的存储方式
      • 1.1.2 NoSQL 与 sql 之间的区别
    • 1.2 Redis概述
      • 1.2.1 Redis是什么
      • 1.2.2 Redis有什么用
      • 1.2.3 Redis的特性
    • 1.3 Redis安装测试
      • 1.3.1 Redis Linux安装
    • 1.4 Redis测试工具
    • 1.5 Redis的基本知识
  • 二、Redis的五大数据类型
    • 2.1 Redis-Key的基本命令
    • 2.2 Stirng类型(字符串类型)
    • 2.3 List类型
    • 2.4 Set类型
    • 2.5 Hash类型
    • 2.6 Zset类型 (有序集合)
  • 三 3种特殊数据类型
    • 3.1 geospatial 地理位置
    • 3.2 Hyperloglog类型
    • 3.3 Bitmap
  • 四、Redis事务
    • 4.2 Redis监控
    • 4.3 Jedis连接
    • 4.4 SpringBoot整合
    • 4.5 自定义RedisTemple
    • 4.6 Redis.conf详解
    • 4.7 Redis持久化
      • 4.7.1 RDB
      • 4.7.2 AOF进程
    • 4.8 Redis发布订阅
    • 4.9 Redis主从复制
      • 4.9.1 集群环境配置
      • 4.9.2、复制原理
    • 4.10、宕机后主动变为主机
    • 4.11、哨兵模式
  • 五、Redis缓存穿透和雪崩
    • 5.1、缓存穿透(查不到)
    • 5.2、缓存击穿(量太大,缓存过期!)
    • 5.3、缓存雪崩

一、 Redis快速入门

1.1 初始Redis

1.1.1 Redis的存储方式

Redis的存储方式: 键值对的方式进行存储

在这里插入图片描述

1.1.2 NoSQL 与 sql 之间的区别

SQLNoSql
数据结构结构化非结构化
数据关联关联的无关联的
查询方式SQL查询非SQL
事务类型ACIDBASE
内存存储磁盘内存

1.数据结构

在这里插入图片描述

SQL:当中的字段必须保持一致,名字,类型,限定等
NoSQL: 当中的结构相对宽松,不需要每个字段一一对应。可以出现:键值类型文档类型列类型Graph类型

2.数据关联

SQL:对应的数据相关性强,会通过外键进行关联
NoSQL:相对关联性不强,可以输入任意的字段

3.查询方式

在这里插入图片描述

SQL:固定,简单易上手
NoSQL:不同的软件对应不同的执行命令,不统一

1.2 Redis概述

1.2.1 Redis是什么

Redis(Remote Dictionary Server)远程字典服务

  1. 他是一个用C语言开发的开源项目
  2. 支持网络、可基于内存完成持久化操作的日志型,key-Value数据库
  3. 支持多种语言的API在这里插入图片描述
  4. redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

1.2.2 Redis有什么用

  1. 内存存储、持久化、内存中是断电即失,所以说持久化很重要(rdb、aof实现持久化)
  2. 效率高、可用于高速缓存 (11万读取/s,8万写入/s)
  3. 发布订阅系统
  4. 地图信息分析
  5. 计时器、计数器(主要用于浏览量、点赞量等)

1.2.3 Redis的特性

  1. 多样的数据类型
  2. 持久化
  3. 集群
  4. 事务

1.3 Redis安装测试

打开redis-server.exe ; redis-cli.exe
在这里插入图片描述

1.3.1 Redis Linux安装

1. 在Linux当中输入:

wget http://download.redis.io/releases/redis-5.0.7.tar.gz

等待下载安装

2. 解压安装Redis:

若是第一次安装需要 先安装gcc

  • 安装
yum -y install gcc
  • 检验安装是否正确:
rpm -qa|grep gcc
  • 下载完后,将Redias进行解压
tar -zvxf redis-5.0.7.tar.gz
  • 将解压后的Redis ,移动到/usr/local/bin
mv /root/redis-5.0.7 /opt
  • 创建新的文件夹kconfig,装入复制过后的redis.conf,便于以后的修改删除
mkdir kconfig

cp /opt/redis-5.0.7/redis.conf kconfig
  • 编译
make
make install
  • 修改配置文件
cd /usr/local/bin/kconfig

vim redis.conf

在这里插入图片描述
修改后,点击ESC,然后输入:wq保存并退出

cd /usr/local/bin
redis-server kconfig/redis.conf
  • 启动Redis服务:

在这里插入图片描述

  • 进行测试:
reids-cli -p 6379

ping

若出现PONG,表示连接成功
在这里插入图片描述

1.4 Redis测试工具

redis-benchmark:官方的压力测试工具

工具表:
在这里插入图片描述

我们进行测试,100个并发数,100000个请求

redis-benchmark -h localhost -p 6379 -c 100 -n 100000

在这里插入图片描述
理解其中的含义:
在这里插入图片描述

1.5 Redis的基本知识

Redis是单线程的!

  • 因为官方表示,Redis是内存操作,CPU不是限制Redis性能的瓶颈,限制Redis瓶颈的是根据机器的内存和网络带宽,可以使用单线程就可以实现,所以就是用单线程了!!
  • 因为Redis是用C语言开发的,官方提供的数据为 100000+ 的QPS,完全不比同样使用key-value的Memecahe差!!!
  1. 简单性: 单线程架构使得 Redis 的实现更加简单。不需要处理线程同步、锁定和竞争条件等复杂性问题,降低了系统的复杂性,提高了可靠性和稳定性。
  2. 避免竞态条件: 在多线程或多进程的系统中,如果不正确地处理并发访问,可能会导致竞态条件,从而导致数据不一致或其他问题。通过单线程模型,Redis 可以避免这些问题。
  3. 高效利用CPU: 单线程模型允许 Redis 将所有 CPU 时间用于处理请求,而不需要在线程间切换上花费时间。这样可以最大限度地提高性能,并且使得 Redis 在大部分情况下都能够达到非常高的吞吐量。
    尽管 Redis 主要是单线程的,但它仍然可以利用多核系统的优势。Redis 使用了一些技术来充分利用多核 CPU,比如事件驱动、非阻塞 IO 和多路复用等。这些技术使得 Redis 能够在单线程的基础上实现高并发和高性能。

Redis为什么使用单线程还是这么快:

  1. 误区:我们一致以为多线程比单线程块
  2. 误区: 高性能的服务器一定是多线程的

原因:

  • 运行速度: CPU > 内存 > 硬盘
  • Redis是所有数据都放入在内存当中,所以使用单线程去操作效率是最高的,多线程(CPU上下会切换:会消耗时间!!),对应内存系统来说,如果没有上下切换效率是最高的!!多次读写就是在一个CPU上面,在内存情况下,这个就是最佳的方案!

二、Redis的五大数据类型

2.1 Redis-Key的基本命令

  • Redis有16个数据库,默认在第0个数据库当中

在这里插入图片描述

  • 修改不同数据库:
kconfig]$ redis-cli -p 6379
127.0.0.1:6379> select 3
OK
  • 增加、查看数据:
127.0.0.1:6379[3]> set name 123  #增加数据
OK
127.0.0.1:6379[3]> get name    #获取数据
"123"
  • 查看当前库的所有数据:
127.0.0.1:6379> keys *   #查询所有的Key数据
1) "name"
2) "key:__rand_int__"
  • 清除数据
    FLUSHALL |
127.0.0.1:6379> FLUSHALL  #清除所有库当中的数据
OK
127.0.0.1:6379> FLUSHDB   #清除当前库的所有数据
OK
  • 将数据进行转移
    MOVE
move name 2    #移动name在数据库2中
(integer) 1
127.0.0.1:6379> select 2
OK
127.0.0.1:6379[2]> keys *
1) "name"
  • 将数据设置过期时间:
    EXPIRE
127.0.0.1:6379[2]> EXPIRE name 10   #设置过期时间
(integer) 1
127.0.0.1:6379[2]> ttl name     # 查看当前KEY存活的时间
(integer) 6
127.0.0.1:6379[2]> ttl name
(integer) 1
127.0.0.1:6379[2]> ttl name
(integer) 0
127.0.0.1:6379[2]> get name
(nil)
  • 判断当前KEY是否存在:
    EXEISTS
127.0.0.1:6379[2]> EXISTS name
(integer) 0     # 返回1 表示存在 , 返回0 表示不存在

2.2 Stirng类型(字符串类型)

  • 追加字符串,如果数据库当中有这个Key --> 就直接添加在后面,如果不存在这个Key --> 就直接创建一个Key,值就是你追加的值
    APPEND
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> append name 6666  #在对应key值后面加上数字
(integer) 12
127.0.0.1:6379> get name 
"zhangsan6666"
127.0.0.1:6379> append age 18   #数据库没有,就创建了key:age value:18
(integer) 2
127.0.0.1:6379> get age 
"18"
  • 获取字符串的长度:
    STRLEN
127.0.0.1:6379> strlen name
(integer) 12
  • 计数器自增,自减
    INCR| DECR | INCRBY | DECRBY
127.0.0.1:6379> incr age   # age++
(integer) 19
127.0.0.1:6379> decr age   # age--
(integer) 18
127.0.0.1:6379> incrby age 18   # age+18
(integer) 36
127.0.0.1:6379> decrby age 87   # age-87 
(integer) -51
  • 截取字符串:
    GETRANGE
127.0.0.1:6379> getrange name 0 -1  # 截取全部的字符串
"zhangsan6666"
127.0.0.1:6379> getrange name 0 3   # 截取从0到3的字符串
"zhan"
  • 替换字符串:
    SETRANGE
127.0.0.1:6379> setrange name 0 66
(integer) 12
127.0.0.1:6379> get name
"66angsan6666"
  • 设置过期时间:
    SETEX
127.0.0.1:6379> setex name1 11 11   # setex key  过期时间  value
OK
127.0.0.1:6379> ttl name1
(integer) 5
127.0.0.1:6379> get name1
"11"
127.0.0.1:6379> ttl name1
(integer) -2
127.0.0.1:6379> get name1
(nil)
  • 不存在Key的时候进行设置:
    SETNX

127.0.0.1:6379> get name1
"zhangsan666"
127.0.0.1:6379> get name1
(nil)
127.0.0.1:6379> setnx name 111
(integer) 0
127.0.0.1:6379> setnx name1 name1
(integer) 1
127.0.0.1:6379> get name1
"name1"
  • 设置多个值,获取多个值:
    MGET| MSET
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3 
OK
127.0.0.1:6379> keys *
1) "k3"
2) "k2"
3) "k1"
127.0.0.1:6379> mget k1 k2 k3 
1) "v1"
2) "v2"
3) "v3"
  • 首先通过Key获取,返回该值,若含有值就覆盖该值,若不含有该值,就创建该值:
    GETSET
127.0.0.1:6379> get k1
"v1"
127.0.0.1:6379> getset k1 lisi11111
"v1"
127.0.0.1:6379> get k1
"lisi11111"

对象:

set user:{id}:{count}  value 这样设计在Redis当中完全OK

2.3 List类型

结构如图:
在这里插入图片描述

  • 增加数据:
    LPUSH | RPUSH | LRANGE
127.0.0.1:6379> LPush list name1  # 向左添加数据
(integer) 1
127.0.0.1:6379> LPush list name2 
(integer) 2
127.0.0.1:6379> LPush list name3
(integer) 3
127.0.0.1:6379> lrange list 0 -1  # 查看所有的
1) "name3"
2) "name2"
3) "name1"
127.0.0.1:6379> Rpush list name0   # 从右边添加数据
(integer) 4
127.0.0.1:6379> lrange list 0 -1
1) "name3"
2) "name2"
3) "name1"
4) "name0"
  • 删除数据
    LPOP | RPOP
127.0.0.1:6379> lrange list 0 -1
1) "name3"
2) "name2"
3) "name1"
4) "name0"
127.0.0.1:6379> LPOP list  # 移除第一个数据
"name3"
127.0.0.1:6379> lrange list 0 -1
1) "name2"
2) "name1"
3) "name0"
127.0.0.1:6379> RPOP list
"name0"
127.0.0.1:6379> lrange list 0 -1  # 移除最后一个数据
1) "name2"
2) "name1"
127.0.0.1:6379> 
  • 通过下标获取到LIst当中的值
    LINDEX
127.0.0.1:6379> lrange list 0 -1
1) "name2"
2) "name1"
127.0.0.1:6379> LINDEX list 1
"name1"
127.0.0.1:6379> 
  • 精确的移除某个元素:
    LREM
127.0.0.1:6379> lRANGE list 0 -1
1) "name4"
2) "name4"
3) "name3"
4) "name1"
5) "name4"
127.0.0.1:6379> Lrem list 1 name4
(integer) 1
127.0.0.1:6379> lrange list 0 -1 
1) "name4"
2) "name3"
3) "name1"
4) "name4"
127.0.0.1:6379> lrem list 2 name4
(integer) 2
127.0.0.1:6379> lrange list 0 -1 
1) "name3"
2) "name1"
127.0.0.1:6379> 
  • 裁剪LIST,修剪,截取
    LTRIM
127.0.0.1:6379> lrange list 0 -1
1) "name1"
2) "name2"
3) "name3"
4) "name4"
127.0.0.1:6379> ltrim list 0 2
OK
127.0.0.1:6379> lrange list 0 -1
1) "name1"
2) "name2"
3) "name3"
  • 移除最后一个元素,将其放在另外一个列表当中:
    RPOPLPUSH
127.0.0.1:6379> LRange LIST 0 -1
1) "name4"
2) "name3"
3) "name2"
4) "name1"
127.0.0.1:6379> RPOPLPUSH LIST MYLIST
"name1"
127.0.0.1:6379> LRANGE MYLIST 0 -1
1) "name1"
127.0.0.1:6379> 
  • 将列表当中指定下标的值替换成另外一个值,执行更新操作
    LSRT
127.0.0.1:6379> EXISTS LIST
(integer) 0
127.0.0.1:6379> LSET LIST 0 VALUE1   # 如不存在就会报错
(error) ERR no such key
127.0.0.1:6379> LPUSH LIST NAME1 NAME2 NAME3
(integer) 3
127.0.0.1:6379> LSET LIST 0 VALUE1  # 存在就会将其覆盖
OK
127.0.0.1:6379> LRANGE LIST 0 0
1) "VALUE1"
  • 将某个具体的数据插入到某个元素的前面或者后面
    LINSERT
127.0.0.1:6379> lrange LIST 0 -1 
1) "VALUE1"
2) "NAME2"
3) "NAME1"
127.0.0.1:6379> LINSERT LIST AFTER VALUE1 QQQQQQ
(integer) 4
127.0.0.1:6379> lrange LIST 0 -1 
1) "VALUE1"
2) "QQQQQQ"
3) "NAME2"
4) "NAME1"
127.0.0.1:6379> LINSERT LIST BEFORE VALUE1 QQQQQQ
(integer) 5
127.0.0.1:6379> lrange LIST 0 -1 
1) "QQQQQQ"
2) "VALUE1"
3) "QQQQQQ"
4) "NAME2"
5) "NAME1"
127.0.0.1:6379> 
  1. 他实际上是一个链表,before Node after ,left,right 都可以插入值
  2. 如果key 不存在,创建新的链表
  3. 如果key存在,新增内容
  4. 如果移除了所有值,空链表,也代表不存在!在两边插入或者改动值,效率最高!中间元素,相对来说效率会低一点~

2.4 Set类型

Set类型当中的元素不能出现重复的值
SADD |

  • 添加数据、查看指定数据
    SADD | SMEMBER | SMEMBERS
127.0.0.1:6379> SADD set m1    # 添加元素在Set集合当中
(integer) 1
127.0.0.1:6379> SMEMBERs set   # 查看Set集合当中的元素
1) "m1"
127.0.0.1:6379> 
127.0.0.1:6379> SISMEMBER set m1   # 这个Set包含该元素
(integer) 1
127.0.0.1:6379> SISMEMBER set m    # 这个Set集合当中不存在该元素
(integer) 0
  • 获取到set集合当中有几个元素
    SCARD
127.0.0.1:6379> scard set1
(integer) 4
127.0.0.1:6379> 
  • 移除元素:
    SREM
127.0.0.1:6379> smembers set1
1) "k4"
2) "k3"
3) "k2"
4) "k1"
127.0.0.1:6379> srem set1 k1
(integer) 1
127.0.0.1:6379> smembers set1
1) "k4"
2) "k3"
3) "k2"
127.0.0.1:6379> 
  • 随机抽取Set集合当中的元素
    SRANDMEMBER
127.0.0.1:6379> smembers set1
1) "k4"
2) "k3"
3) "k2"
127.0.0.1:6379> SRANDMEMBER set1   # 随机获取到Set集合当中的一个元素
"k2"
127.0.0.1:6379> SRANDMEMBER set1 2   # 随机获取到Set集合当中的两个元素
1) "k3"
2) "k4"
127.0.0.1:6379> SRANDMEMBER set1 3    # 随机获取到Set集合当中的两个元素
1) "k3"
2) "k4"
3) "k2"
127.0.0.1:6379> SMEMBERS set1
1) "k4"
2) "k3"
3) "k2"
127.0.0.1:6379> 
  • 删除指定的key,随机删除:
    SPOP
127.0.0.1:6379> SMEMBERS set1
1) "k4"
2) "k3"
3) "k2"
127.0.0.1:6379> SPOP set1
"k3"
127.0.0.1:6379> SPOP set1  # 随机删除一个元素
"k4"
127.0.0.1:6379> smembers set1
1) "k2"
  • 将某个Set集合当中的元素移动到另外一个元素:
    SMOVE
127.0.0.1:6379> sadd set s1 s2 s3 s4
(integer) 4
127.0.0.1:6379> smove set myset s1
(integer) 1
127.0.0.1:6379> smembers set
1) "s4"
2) "s3"
3) "s2"
127.0.0.1:6379> smembers myset
1) "s1"
127.0.0.1:6379> 
  • 微博,B站,共同关注!(并集)数字集合类:
  • 差集 SDIFF
  • 交集 SINTER
  • 并集 SUNION
127.0.0.1:6379> sadd a 1 2 3 4 5 
(integer) 5
127.0.0.1:6379> sadd b 1 2 23 3 223 323 
(integer) 6
127.0.0.1:6379> Sdiff a b
1) "4"
2) "5"
127.0.0.1:6379> sinter a b
1) "1"
2) "2"
3) "3"
127.0.0.1:6379> sunion  a b
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "23"
7) "223"
8) "323"

2.5 Hash类型

  • 批量添加、删除、获取Hash当中长度、查询所有、查看是否存在
    HSET | HMSET | HGET | HMGET | HEXISTS | HLEN | HDEL
127.0.0.1:6379> hmset hash fields1 value1 fieldss2 value2 fields3 value3     # 批量设置值
(integer) 3
127.0.0.1:6379> hget hash fields1
"value1"
127.0.0.1:6379> hmget hash fields1 fields2 fields3
1) "value1"
2) (nil)
3) "value3"
127.0.0.1:6379> hgetall hash    # 查询到所有的数据key-value
1) "fields1"
2) "value1"
3) "fieldss2"
4) "value2"
5) "fields3"
6) "value3"
127.0.0.1:6379> hlen hash     # 获取到所有的Key数据
(integer) 3
127.0.0.1:6379> hkeys hash     # 获取到所有的Key值
1) "fields1"
2) "fieldss2"
3) "fields3"
127.0.0.1:6379> hvals hash       # 获取所有的value值
1) "value1"
2) "value2"
3) "value3"
127.0.0.1:6379> hdel hash fieldss2    # 删除元素
(integer) 1
127.0.0.1:6379> hmget hash fields1 fieldss2   # 批量查询
1) "value1"
2) (nil)
127.0.0.1:6379> hexists hash fieldss2   # 查看是否存在
(integer) 0
127.0.0.1:6379> hexists hash fields1   # 查看是否存在
(integer) 1
  • 增加数据
    HINCRBY
127.0.0.1:6379> hmset hash a 1 b 2 c 3 d 4
OK
127.0.0.1:6379> hincrby hash a 2
(integer) 3
127.0.0.1:6379> hincrby hash b -1
(integer) 1
127.0.0.1:6379> hgetall hash
1) "a"
2) "3"
3) "b"
4) "1"
5) "c"
6) "3"
7) "d"
8) "4"
  • 当不存在该条数据的时候再添加进去

HSETNX

127.0.0.1:6379> hsetnx hash newData value1 
(integer) 1
127.0.0.1:6379> hget hash newData
"value1"
127.0.0.1:6379> 

Hash变更的数据 user name age , 尤其是用户信息之类的数据 , 经常变动的信息! Hash更适合对象的存储,String更加适合字符串的存储!!

2.6 Zset类型 (有序集合)

在set的基础上,再增加了一个值 set k1 v1 zset k1 scord v1

score就是用于排序的

  • 增加数据
    Zadd
127.0.0.1:6379> zadd salary 100 zhangsan 200 lisi 300 wangwu 
(integer) 3
  • 排序 , 查询元素个数:
    ZCARD | ZREVRANK | ZCOUNT | ZRANGEBYSCORE
127.0.0.1:6379> zrevrank salary zhangsan  # 查询张三的分数排名
(integer) 2
127.0.0.1:6379> zcard salary  # 包含元素的个数
(integer) 3
127.0.0.1:6379> zcount salary -inf +inf   # 查询从负无穷到正无穷之间包含的元素个数
(integer) 3
127.0.0.1:6379> zcount salary 100 200   # 查询从100到200之间包含的元素个数
(integer) 2
127.0.0.1:6379> ZRANGEBYSCORE salary -inf +inf withscores # 从低到高进行排序
1) "zhangsan"
2) "100"
3) "lisi"
4) "200"
5) "wangwu"
6) "300"
127.0.0.1:6379> Zrange salary 0 -1 withscores  # 从低到高进行排序
1) "zhangsan"
2) "100"
3) "lisi"
4) "200"
5) "wangwu"
6) "300"
  • 移除代码:
    ZREM
127.0.0.1:6379> ZREM salary zhangsan 
(integer) 1
127.0.0.1:6379> zrank salary zhangsan
(nil)
127.0.0.1:6379> zrank salary lisi
(integer) 0
127.0.0.1:6379> zrevrange salary 0 -1
1) "wangwu"
2) "lisi"

三 3种特殊数据类型

3.1 geospatial 地理位置

只有六个命令:
在这里插入图片描述

  • 添加地理位置 , 查询地理位置
  • getadd 添加地理位置
  • 规则:两级无法直接添加,我们一般会下载城市数据,直接通过java程序一次性导入!
  • 参数 key 值(纬度、经度、名称)

GEOADD | GEOPOS

127.0.0.1:6379> GEOADD china:city 50 50 shanghai
(integer) 1
127.0.0.1:6379> GEOADD china:city 10 10 beijing 20 20 sichuan 25 25 shenzhen
(integer) 3
127.0.0.1:6379> GEOPOS china:city shanghai
1) 1) "49.99999970197677612"
   2) "49.99999957172130394"
127.0.0.1:6379> GEOPOS china:city shanghai beijing
1) 1) "49.99999970197677612"
   2) "49.99999957172130394"
2) 1) "10.00000208616256714"
   2) "10.00000092823272979"

  • 查看两地之间的距离:
    GEODIST

单位
· m 表示单位为米。
· km 表示单位为干米。
· mi表示单位为英里,
· ft 表示单位为英尺。

127.0.0.1:6379> geodist china:city shanghai sichuan km  # 两者之间的直线距离
"4256.6209"
  • 查询附近的人
    GEORADIUS
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEORADIUS Sicily 15 37 200 km WITHDIST  #获取到15 37 半径为200km的城市,相距的直线距离
1) 1) "Palermo"
   2) "190.4424"
2) 1) "Catania"
   2) "56.4413"
redis> GEORADIUS Sicily 15 37 200 km WITHCOORD  #获取到15 37 半径为200km的城市经纬度
1) 1) "Palermo"
   2) 1) "13.361389338970184"
      2) "38.115556395496299"
2) 1) "Catania"
   2) 1) "15.087267458438873"
      2) "37.50266842333162"
redis> GEORADIUS Sicily 15 37 200 km WITHDIST WITHCOORD  #获取到15 37 半径为200km的城市经纬度和相距距离
1) 1) "Palermo"
   2) "190.4424"
   3) 1) "13.361389338970184"
      2) "38.115556395496299"
2) 1) "Catania"
   2) "56.4413"
   3) 1) "15.087267458438873"
      2) "37.50266842333162"
redis> 
  • 找出位于指定范围内的元素,中心点是由给定的位置元素决定
    GEORADIUSBYMEMBER
redis> GEOADD Sicily 13.583333 37.316667 "Agrigento"
(integer) 1
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEORADIUSBYMEMBER Sicily Agrigento 100 km
1) "Agrigento"
2) "Palermo"
redis> 
  • 返回11个字符的Geohash字符串
    GEOHASH
redis> GEOADD Sicily 13.361389 38.115556 "Palermo" 15.087269 37.502669 "Catania"
(integer) 2
redis> GEOHASH Sicily Palermo Catania
1) "sqc8b49rny0"
2) "sqdtr74hyu0"
redis> 

底层就是一个Zset
所以可以通过Zrem,将数据进行删除

3.2 Hyperloglog类型

  1. 什么是基数: 就是不重复的数,和set差不多
  2. 为什么要使用Hyperloglog而不是用Set呢: 因为占用的内存是固定的,2^64 不同元素的技术,只需要12KB的内存
  3. Hyperloglog 存在一定的误差。就是0.81%,但是一般在统计的时候就会忽略这个误差,如果忽略不掉,就可以使用Set方法去实现
  • 添加数据 , 查看数据个数 , 合并数据,将相同的删除
    PFADD | PFCOUNT | PFMERGE
127.0.0.1:6379> pfadd pf1 1 2 3 4 5 65
(integer) 1
127.0.0.1:6379> pfadd pf2 12 2 3 4 51 615
(integer) 1
127.0.0.1:6379> pfcount pf1
(integer) 6
127.0.0.1:6379> pfcount pf2
(integer) 6
127.0.0.1:6379> PFMERGE mypf pf1 pf2
OK
127.0.0.1:6379> pfcount mypf
(integer) 9
127.0.0.1:6379> 

3.3 Bitmap

这是一个位存储

位存储:统计用户信息、登录,未登录等等。只要是两个状态的都可以使用Bitmap去实现。就只有0 1 两个状态

在这里插入图片描述

  • 设置、 获取、统计打卡状态
    SETBIT | GETBIT | BITCOUNT
127.0.0.1:6379> SETBIT BIT 1 0
(integer) 0
127.0.0.1:6379> SETBIT BIT 2 1 
(integer) 0
127.0.0.1:6379> SETBIT BIT 3 0 
(integer) 0
127.0.0.1:6379> SETBIT BIT 4 1 
(integer) 0
127.0.0.1:6379> SETBIT BIT 5 1 
(integer) 0
127.0.0.1:6379> SETBIT BIT 6 1 
(integer) 0
127.0.0.1:6379> GETBIT BIT 1
(integer) 0
127.0.0.1:6379> GETBIT BIT 2
(integer) 1
127.0.0.1:6379> GETBIT BIT 3
(integer) 0
127.0.0.1:6379> BITCOUNT BIT 
(integer) 4
127.0.0.1:6379> BITCOUNT BIT 0 4
(integer) 4

四、Redis事务

Redis在单条命令保持原子性的,但是事务保持原子性的!

Redis事务本质:一组命令的集合! 一个事务当中的所有命令都会被序列化,在事务执行的过程中,会按照着顺序进行执行!

一次性、顺序性、 排他性! 执行一些列的命令!

Redisd的事务:

  • 开启事务(MULTI)
  • 命令入列(……)
  • 执行事务(EXEC)

正常执行事务!!!!

127.0.0.1:6379> set k1 v1 
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> getset k1 123
QUEUED
127.0.0.1:6379> get k1 
QUEUED
127.0.0.1:6379> exec
1) OK
2) OK
3) "v1"
4) "123"

编译型错误,执行代码时就会报错,其他的代码也不会再次执行(因为代码写错了)

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1 
QUEUED
127.0.0.1:6379> set k2 v2 
QUEUED
127.0.0.1:6379> getset k2   # 代码写出,所有的代码都不会执行
(error) ERR wrong number of arguments for 'getset' command
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> set k4 v4
QUEUED
127.0.0.1:6379> get k4
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get k4  # 其他代码就没有执行
(nil)

运行时异常:如果事务队列当中存在语法性错误,那么在执行命令的时候,其他的命令也可以正常执行,错误的命令就抛出错误就行

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set k1 v1
QUEUED
127.0.0.1:6379> incr k1 
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> set k3 v3
QUEUED
127.0.0.1:6379> get k3 
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) ERR value is not an integer or out of range  # 抛出错误
3) OK
4) OK
5) "v3"
127.0.0.1:6379> get k3
"v3"

4.2 Redis监控

  • 悲观锁: 无论什么时候都加锁,认为什么时候都会出现问题
  • 乐观锁: 无论什么时候都不加锁,认为什么时候都不会出现问题只有在跟新数据的时候会判断一下,是否有人修改过数据

Redis检测测试:

  • 正常运行
127.0.0.1:6379> set money 100 
OK
127.0.0.1:6379> set out 0
OK
127.0.0.1:6379> multi 
OK
127.0.0.1:6379> decrby money 20
QUEUED
127.0.0.1:6379> incrby out 20
QUEUED
127.0.0.1:6379> exec
1) (integer) 80
2) (integer) 20
  • 测试多线程修改,监视失败(相当于乐观锁watch
    监视线程:
127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> INCR money
QUEUED
127.0.0.1:6379> decr out
QUEUED
127.0.0.1:6379> exec  # 另外一个修改了,导致了事务执行失败
(error) EXECABORT Transaction discarded because of previous errors.

另外一个执行的线程:

127.0.0.1:6379> set  money 1000000 # 在另外一个事务提交前就修改数据了
OK

4.3 Jedis连接

Jedis是 Redis 官方推荐的 java连接开发工具!使用|ava 操作Redis 中间件!如果你要使用java操作redis,那么一定要对jedis十分熟悉

  • 首先创建一个新的空项目
  • 导入坐标
<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>5.2.0-beta1</version>
        </dependency>
  • 连接Redis
public class tests {
    public static void main(String[] args) {
        Jedis jedis = new Jedis("127.0.0.1", 6379);
        System.out.println(jedis.ping());
    }
}
  • 测试连接
    jedis.ping()
    在这里插入图片描述

4.4 SpringBoot整合

  1. SpringBoot操作数据:spring-data ipa jdbc mongodb redis !
  2. SpringData 也是和 SpringBoot 齐名的项目!

说明:在 SpringBoot2.x之后,原来使用的jedis 被替换为了 lettuce?

  1. jedis:采用的直连,多个线程操作的话,是不安全的,如果想要避免不安全的,使用jedis pool连接池! 更像 BIO
  2. lettuce:采用netty实例可以再多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据了,更像 NIO 模式

源码解释:

在这里插入图片描述

在这里插入图片描述

  • 首先引入坐标:
 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  • 配置类
spring.data.redis.port = 6379
spring.data.redis.host= 127.0.0.1
  • 执行方法:
    在这里插入图片描述
 @Test
    void contextLoads() {
        redisTemplate.opsForValue().set("zhangsan",123)
       System.out.println(redisTemplate.opsForValue().get("zhangsan"));
    }

4.5 自定义RedisTemple

为序列化会报错:

@Autowired
    private RedisTemplate redisTemplate;
    @Test
    void contextLoads() {
        User user = new User("zhangsan",123);
        redisTemplate.opsForValue().set("zhangsan",user);
        System.out.println(redisTemplate.opsForValue().get("zhangsan"));
    }

在这里插入图片描述

设置其序列化:
在这里插入图片描述

  • 配置属于自己的Config
@Configuration
public class redisConfig {

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        //JSON序列化配置
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(ObjectMapper.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(stringRedisSerializer);
        template.setValueSerializer(jackson2JsonRedisSerializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        template.setHashValueSerializer(jackson2JsonRedisSerializer);

        template.afterPropertiesSet();
        return template;
    }
}
  • 还可以配置自己的Redisutil就可以简单的将数据获取或设置
    在这里插入图片描述

4.6 Redis.conf详解

  • 单位设置:
    在这里插入图片描述
  1. 配置文件 unit单位对大小都是一样的,不敏感
  • 文件组合:可以结合多个conf文件
    在这里插入图片描述

  • 网络配置(NETWORK):

bind 127.0.0.1      #绑定的Ip
protect-mode yes    #保护模式
port 6357           #端口设置

保护模式

当启用保护模式时,Redis将只允许执行少数安全命令,如SHUTDOWN、PING和INFO,而禁止执行可能会对数据造成不可逆影响危险命令,如FLUSHDB、FLUSHALL和CONFIG等。这有助于防止意外数据丢失或损坏,以及对Redis实例的未经授权的访问。

  • 通用配置( GENERAL):
daemonize yes  # 以守护进程的方式运行,默认为no,我们需要自己开启yes!
pidfile /var/run/redis_6379.pid   # 如果以后台的方式运行,我们就需要指定一个pid文件
#日志
# Specify the server verbosity level.
# This can be one of:
# debug (a lot of information, useful for development/testing)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
loglevel notice
logfile ""  # 日志的文件名
databases 16  # 16个数据库
always-show-logo yes # 是否总是显示LOGO(就是开启服务时的RedisLOGO)
  • 快照(SNAPSHOTTING)
  1. 持久化,在规定时间内,执行了多少次操作,就会持久化文件 ----> rdb,aof
  2. Redis是内存数据库,如果没有持久化就会导致,断电及失
# 如果在900s内,至少有1个key进行了修改,我们就进行持久化的操作
save 900 1
# 如果在300s内,至少有10个key进行了修改,我们就进行持久化的操作
save 300 10
# 如果在60s内,至少有1000个key进行了修改,我们就进行持久化的操作
save 60 10000

stop-writes-on-bgsave-error yes   # 持久化如果出错,是否继续工作
rdbcompression yes    # 是否压缩 rdb文件,需要消耗一些cpu资源
rdbchecksum yes       # 保存rdb文件的时候,进行错误的检验
dir ./                # 文件保存的目录
  • 安全
    在这里插入图片描述
  • 限制(CLENTS)

在这里插入图片描述

4.7 Redis持久化

Redis是内存数据库,如果没有持久化就会导致,断电及失!

4.7.1 RDB

RDB: 它是一种开源的内存数据库,用于存储键值对数据,并提供多种数据结构的支持
在这里插入图片描述

在指定的时间间隔内将内存中的数据集快照写入磁盘,也就是行话讲的snapshot快照,它恢复时是将快照文件直接读到内存里。

Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何! 0操作的。这就确保了极高的性能。如果需要进行大规模数据的恢复,目对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。我们默认的就是RDB,一般情况下不需要修改这个配置!

默认生成的文件名:dump.rdb
在这里插入图片描述

  • 可以在这里来设置我们的触发条件
    在这里插入图片描述

  • 触发RDB机制

  1. 满足配置文件,save规则就会触发
  2. 执行FLUSHALL,也会触发RDB
  3. 退出Redis也可以触发,产生rdb文件

在这里插入图片描述

  • 如何恢复RDB文件
  1. 我们只需要将rdb文件放在Redis的启动文件当中就可以,因为在启动Redis的时候就会自动的去识别dump.rdb
  2. 查看需要存在的位置
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/bin"   # 放在这个位置就可以识别到rdb文件

优点:

  1. 适合大规模的数据恢复
  2. 对数据的完整性不高

缺点:

  1. 需要一定的时间间隔进行操作!如果Redis意外宕机了,最后一次修改数据就没有了
  2. fork进程的时候,由于是开启了另外一个线程,所以会占用一定的内容空间

所以一般我们在开发的时候都会备份 dump.rdb文件

4.7.2 AOF进程

在这里插入图片描述

以日志的形式来记录每个写操作,将Redis执行过的所有指令记录下来(读操作不记录),只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据,换言之,redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作

AOF默认保存的是 appendonly.aof默认为no,不开启的,需要手动开启

在这里插入图片描述
重启redis就会出现aof

  • Redis当中的aof文件被破坏了,Redis是启动不了的
    可以使用redis-check-aof --fix appendonly.aof 自动进行修复
  • 在这里插入图片描述
  • 重写规则:
    在这里插入图片描述

优点:

  1. 每一次修改都同步,优化了文件的完整性
  2. 每秒同步一次,可能会丢失那一秒的数据
  3. 从不同步开启,这样的效率是最高的!

缺点:

  1. 对于数据文件来说,aof远远大于rdb,修复的速度比rdb慢
  2. aof的执行操作比rbd的慢

4.8 Redis发布订阅

发布订阅: 是Redis提供的一种消息传递模式,用于实现消息的发布和订阅机制。在这种模式下,消息的发送者(pub发布者)将消息发送到指定的频道,而消息的接收者(sub订阅者)则订阅对应的频道,从而接收发布到该频道的消息。

在这里插入图片描述

  • 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
    在这里插入图片描述
  • 当有新消息通过 PUBLISH 命令发送给频道 channel1 时,这个消息就会被发送给订阅它的三个客户端
    在这里插入图片描述

主要命令:
在这里插入图片描述

  • 测试:
    订阅端:
127.0.0.1:6379> subscribe zhangsan  # 订阅名称
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "zhangsan"
3) (integer) 1
1) "message"
2) "zhangsan"       # 订阅发布者的名称
3) "helloword"      # 发布者发布的内容
1) "message"
2) "zhangsan"
3) "hellow!Redis"

发布端:

127.0.0.1:6379> publish zhangsan helloword  # 创建发布者
(integer) 0
127.0.0.1:6379> publish zhangsan helloword  # 发布消息
(integer) 1
127.0.0.1:6379> publish zhangsan hellow!Redis
(integer) 1

4.9 Redis主从复制

概念:

  • 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave以读为主。
  • 默认情况下,每台Redis服务器都是主节点;且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。

主从复制的作用主要包括:

  1. 数据冗余:主从复制实现了数据的热备份,是持久化之外的一种数据冗余方式。
  2. 故障恢复:当主节点出现问题时,可以由从节点提供服务,实现快速的故障恢复;实际上是一种服务的冗余。
  3. 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从节点提供读服务(即写Redis数据时应用连接主节点,读Redis数据时应用连接从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高Redis服务器的并发量。
  4. 高可用(集群)基石:除了上述作用以外,主从复制还是哨兵和集群能够实施的基础,因此说主从复制是Redis高可用的基础,。

一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下:

  • 结构上,单个Redis服务器会发生单点故障,并且一台服务器需要处理所有的请求负载,压力较大;
  • 容量上,单个Redis服务器内存容量有限,就算一台Redis服务器内存容量为256G,也不能将所有内存用作Redis存储内存一般来说,单台Redis最大使用内存不应该超过20G。

电商网站上的商品,一般都是一次上传,无数次浏览的,说专业点也就是多读少写

主从复制,读写分离! 80%的情况下都是在进行读操作!减缓服务器的压力!架构中经常使用! 一主二从(最低标准)

4.9.1 集群环境配置

查看环境信息info replication

127.0.0.1:6379> info replication # 查看当前库的信息
# Replication
role:master # 角色 master
connected_slaves:0 # 没有从机
master_replid:20cbd141dbd0a1a45f20841f5dc80f06ddc08730
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

复制3个配置文件,然后修改对应的信息

  1. 端口
  2. pid 名字
  3. log文件名字
  4. dump.rdb 名字

4.9.2、复制原理

默认情况下,每台Redis服务器都是主节点; 一般情况下只需要配置从机

slaveof host port # host和port都是主机的地址以及主机开启redis的port

真实的从主配置应该在配置文件中配置,这样的话是永久的,我们这里使用的是命令,暂时!

replicaof <masterip> <masterport>   # 配置主机的主机地址,端口号
masterauth <master-password>        # 有密码还需要把密码配上

细节

  1. 主机可以写,从机不能写只能读!主机中的所有信息和数据,都会自动被从机保存!
  1. 测试:主机断开连接从机依旧连接到主机的,但是没有写操作,这个时候,主机如果回来了,从机依旧可以直接获取主机写的信息!
  1. 如果是使用命令行,来配置的主从,这个时候如果重启了,就会变回主机!只要变为从机,立马就会从主机中获取值!

复制原理

Slave 启动成功连接到 master 后会发送一个sync同步命令

Master 接到命令,启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,并完成一次完全同步。

  • 全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中
  • 增量复制:Master 继续将新的所有收集到的修改命令依次传给slave,完成同步

但是只要是重新连接master,一次完全同步(全量复制)将被自动执行!我们的数据一定可以在从机上看到!

4.10、宕机后主动变为主机

层层链路

上一个M连接下一个S
在这里插入图片描述

这时候也可以完成我们的主从复制!

如果没有主机了,这时候需要选出一个主机

如果主机断开了连接,我们可以使用SLAVEOF no one让自己变成主机!其他节点可以手动连接到最新的这个节点。如果主机修复了,需要重新配置

4.11、哨兵模式

(自动选取主机模式)

主从切换技术的方法是:当主服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费事费力,还会造成一段时间内服务不可用。这不是一种推荐的方式,更多时候,我们优先考虑哨兵模式。Redis从2.8开始正式提供了Sentinel(哨兵)架构来解决这个问题。

谋朝篡位的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。

哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。

在这里插入图片描述

这里的哨兵有两个作用

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

然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。

实现原理:

  • 假设主服务器宕机,哨兵1先检测到这个结,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线
  • 当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover[故障转移]操作。
  • 切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线

测试

  1. 配置哨兵配置文件 sentinel.conf
sentinel monitor 被监控的名称 hsot prot 1
sentinel monitor myredis 127.0.0.1 6379 1

后面的这个数字1,代表主机挂了,slave投票看让谁接替成为主机,票数最多的,就会成为主机!

如果Master 节点断开了,这个时候就会从从机中随机选择一个服务器!(这里面有一个投票算法!)

哨兵模式

如果主机此时回来了,只能归并到新的主机下,当做从机,这就是哨兵模式的规则!

  • 优点:
  1. 哨兵集群,基于主从复制模式,所有的主从配置优点,它全有
  2. 主从可以切换,故障可以转移,系统的可用性就会更好
  3. 哨兵模式就是主从模式的升级,手动到自动,更加健壮!
  • 缺点:
  1. Redis 不好在线扩容的,集群容量一旦到达上限,在线扩容就十分麻烦!
  2. 实现哨兵模式的配置其实是很麻烦的,里面有很多选择!

五、Redis缓存穿透和雪崩

Redis缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。

另外的一些典型问题就是,缓存穿透、缓存雪崩和缓存击穿。目前,业界也都有比较流行的解决方案。

5.1、缓存穿透(查不到)

概念

缓存穿透的概念很简单,用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询。发现也没有,于是本次査询失败。当用户很多的时候,缓存都没有命中(秒杀!),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力,这时候就相当于出现了缓存穿透。

在这里插入图片描述

解决方案

  1. 布隆过滤器

布隆过滤器是一种数据结构,对所有可能査询的参数以hash形式存储,在控制层先进行校验,不符合则丢弃,从而避免了对底层存储系统的查询压力;
在这里插入图片描述

  1. 缓存空对象

当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源
在这里插入图片描述

但是这种方法会存在两个问题:

  1. 如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;
  2. 即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响。

5.2、缓存击穿(量太大,缓存过期!)

  • 概述

这里需要注意和缓存击穿的区别,缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一个屏障上凿开了一个洞。

当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,会导使数据库瞬间压力过大。

解决方案:

  1. 设置热点数据永不过期

从缓存层面来看,没有设置过期时间,所以不会出现热点 key 过期后产生的问题。

  1. 加互斥锁

分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去査询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大。
在这里插入图片描述

5.3、缓存雪崩

缓存雪崩,是指在某一个时间段,缓存集中过期失效。Redis 宕机!

产生雪崩的原因之一,比如在写本文的时候,马上就要到双十二零点,很快就会迎来一波抢购,这波商品时间比较集中的放入了缓存,假设缓存一个小时。那么到了凌晨一点钟的时候,这批商品的缓存就都过期了。而对这批商品的访问査询,都落到了数据库上,对于数据库而言,就会产生周期性的压力波峰。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况。
在这里插入图片描述

其实集中过期,倒不是非常致命,比较致命的缓存雪崩,是缓存服务器某个节点宕机或断网。因为自然形成的缓存雪崩,一定是在某个时间段集中创建缓存,这个时候,数据库也是可以顶住压力的。无非就是对数据库产生周期性的压力而已。而缓存服务节点的宕机,对数据库服务器造成的压力是不可预知的,很有可能瞬间就把数据库压垮。

解决方案

  1. redis高可用

这个思想的含义是,既然redis有可能挂掉,那我多增设几台redis,这样一台挂掉之后其他的还可以继续工作,其实就是搭建的集群。

  1. 限流降级

这个解决方案的思想是,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个kev只允许一个线程查询数据和写缓存,其他线程等待。

  1. 数据预热

数据加热的含义就是在正式部署之前,我先把可能的数据先预先访问一遍,这样部分可能大量访问的数据就会加载到缓存中。在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1655716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Elasticsearch运维系列】Elasticsearch7.12.1启动指定版本JDK:你学废了吗?

一、背景 一套生ES集群&#xff0c;版本为7.12.1&#xff0c;近期频繁告警&#xff0c;频繁出现索引分片异常&#xff0c;索引状态异常&#xff0c;导致应用无法正常写入ES&#xff0c;另外&#xff0c;也经常出现节点掉问题。通过分析相关ES日志&#xff0c;显示和当前JAVA G…

C++ | Leetcode C++题解之第77题组合

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> temp;vector<vector<int>> ans;vector<vector<int>> combine(int n, int k) {// 初始化// 将 temp 中 [0, k - 1] 每个位置 i 设置为 i 1&#xff0c;即 [0, k - 1] 存…

细说夜莺监控系统告警自愈机制

虽说监控系统最侧重的功能是指标采集、存储、分析、告警&#xff0c;为了能够快速恢复故障&#xff0c;告警自愈机制也是需要重点投入建设的&#xff0c;所有可以固化为脚本的应急预案都可以使用告警自愈机制来快速驱动。夜莺开源项目从 v7 版本开始内置了告警自愈模块&#xf…

2024/5/9 英语每日一段

With runoff from this year’s snow and rain boosting the levels of California’s reservoirs, state water managers on Tuesday announced plans to increase deliveries of supplies from the State Water Project to 40% of full allotments, up from 30% last month. …

如何用opencv去掉单元格的边框线,以提高Tesseract识别率?

在OpenCV中处理从表格切割下来的图片&#xff0c;并去掉单元格的边框线&#xff0c;以提升Tesseract的识别准确率&#xff0c;确实是一个具有挑战性的任务。在这种情况下&#xff0c;我们需要采取一种策略来预处理图像&#xff0c;使得数字与背景之间的对比度增强&#xff0c;同…

聚观早报 | 苹果新款iPad Pro发布;国产特斯拉4月交付量

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 5月9日消息 苹果新款iPad Pro发布 国产特斯拉4月交付量 iOS 18新功能爆料 真我GT Neo6续航细节 三星Galaxy Z F…

楼宇自控远程I/O革新BACnet/IP模块在暖通空调系统

在现代智能建筑的浪潮中&#xff0c;BACnet/IP分布式远程I/O控制器正逐步成为暖通空调&#xff08;HAVC&#xff09;系统升级转型的得力助手。本文将以某大型商业综合体为例&#xff0c;揭示BACnet/IP I/O模块如何在复杂多变的环境中发挥其独特优势&#xff0c;实现HVAC系统的智…

Flink 算子

Flink 算子 用户通过算子能将一个或多个 DataStream 转换成新的 DataStream&#xff0c;在应用程序中可以将多个数据转换算子合并成一个复杂的数据流拓扑。 这部分内容将描述 Flink DataStream API 中基本的数据转换 API&#xff0c;数据转换后各种数据分区方式&#xff0c;以…

企业网盘竟还能这样用,可道云teamOS:三大冷门使用技巧分享

在日常工作中&#xff0c;大家是否有为海量的文件的管理感到头疼&#xff1f; 每当急需某个重要文件时&#xff0c;总是在各种文件夹中寻寻觅觅半天。这种困扰&#xff0c;我相信许多人都有过。 在这种时候&#xff0c;专业的文件管理软件能帮助我们解决大部分的麻烦。 今天我…

【数据库原理及应用】期末复习汇总高校期末真题试卷09

试卷 一、填空题(每空1分&#xff0c;共10分) 数据的完整性是指数据的________、有效性和相容性。数据模型通常由________、数据操作以及数据约束条件等三要素组成。在关系的有关术语中&#xff0c;关系表中的每一行称作________&#xff0c;每一列称作属性。信息的三种世界是…

WPS二次开发系列:一文快速了解WPS SDK功能场景

作者持续关注 WPS二次开发专题系列&#xff0c;持续为大家带来更多有价值的WPS开发技术细节&#xff0c;如果能够帮助到您&#xff0c;请帮忙来个一键三连&#xff0c;更多问题请联系我&#xff08;QQ:250325397&#xff09; 目录 SDK功能介绍 功能详解&#xff1a; 打开文档…

yaml配置文件的在深度学习中的简单应用

1 .创作灵感 小伙伴们再阅读深度学习模型的代码的时候&#xff0c;经常会遇到yaml格式的配置文件。用这个配置文件是因为我们在训练模型的时候会涉及很多的参数&#xff0c;如果这些参数东一个&#xff0c;西一个&#xff0c;我们调起来的时候就会很不方便&#xff0c;所以用y…

社交媒体数据恢复:飞月

首先&#xff0c;请注意&#xff0c;任何数据恢复操作都不能保证100%找回丢失的数据。因此&#xff0c;在进行数据恢复前&#xff0c;请做好备份&#xff0c;并谨慎操作。 以下是一般性的数据恢复步骤&#xff1a; 导出聊天记录&#xff1a;首先尝试导出飞月的聊天记录。这可以…

全网最全:一文入门最热的LLM应用开发框架LangChain

f#### 1. LangChain 简介 1.1. LangChain 发展史 LangChain 的作者是 Harrison Chase&#xff0c;最初是于 2022 年 10 月开源的一个项目&#xff0c;在 GitHub 上获得大量关注之后迅速转变为一家初创公司。2017 年 Harrison Chase 还在哈佛上大学&#xff0c;如今已是硅谷的…

Promise.all和 race

Promise.all() all方法可以完成并行任务&#xff0c; 它接收一个数组&#xff0c;数组的每一项都是一个promise对象。返回值&#xff1a; 成功时&#xff1a;当数组中所有的promise的状态都达到resolved的时候&#xff0c;就返回包含所有 Promise 结果的数组&#xff0c;并且…

短视频矩阵系统贴牌---saas源头开发

一、短视频矩阵运营注意事项&#xff1a; 如&#xff1a;房产行业 短视频矩阵运营是一个系统化的项目&#xff0c;涉及多个平台和账号的管理&#xff0c;以及内容的创作、发布和优化等多个方面。 以下是短视频矩阵运营的注意事项文档的概要以及结果运营数据 一周持续运营量 二…

Java | Leetcode Java题解之第75题颜色分类

题目&#xff1a; 题解&#xff1a; class Solution {public void sortColors(int[] nums) {int n nums.length;int p0 0, p2 n - 1;for (int i 0; i < p2; i) {while (i < p2 && nums[i] 2) {int temp nums[i];nums[i] nums[p2];nums[p2] temp;--p2;}i…

Driftingblues靶机系列Driftingblues4

获得靶机ip&#xff1a;192.168.108.36 扫描靶机的端口服务&#xff1a; 看到存在&#xff1a;ftp服务&#xff0c;ssh服务和web的http服务&#xff0c;先扫描一下web服务&#xff1a; 访问该网址&#xff1a; 在源代码中看到一串base64编码&#xff1a; Z28gYmFjayBpbnRydW…

抖音小店怎么找达人带货的?分享几个成功率超高的沟通话术!

哈喽~我是电商月月 做抖音小店&#xff0c;特别是无货源的商家想要更多的流量&#xff0c;必定会尝试直播卖货&#xff0c;不会自己直播卖货&#xff0c;就会开通精选联盟&#xff0c;在里面找达人合作 那精选联盟到底是怎样找达人带货的呢&#xff1f; 有的达人打招呼了根本…

【多客校园圈子系统】校园圈子校园论坛社区,多校园微社区交友 校园圈子系统-论坛,跑腿

校园生活服务平台已然成为校园创业的好选择&#xff0c;因为校园人口基数大&#xff0c;人口比聚集&#xff0c;并且现在的学生消费能力还是不错的&#xff0c;所以现在在校园里创业&#xff0c;那真是一个明智的选择&#xff0c;尤其是大学校园创业&#xff0c;但是校园生活服…