redis基础以及持久化策略

Redis概述

Redis(Remote Dictionary Server),远程字典服务,做持久化的缓存(c语言编写,提供多种语言的开发api),也被人称为结构化数据库,官方测试数据,读110000 ,写80000 在redis中无论什么数据类型,在数据库中都是以key-value形式保存,通过进行对Redis-key的操作,来完成对数据库中数据的操作

Redis特点

Redis 是单线程的,Redis是基于内存操作,cpu不是Redis的性能瓶颈,Redis的瓶颈是根据机器的内存和网络带宽,将所有的数据全部放在内存中,所以说单线程的操作效率是最高的,多线程会进行上下文操作,对于内存系统来说,如果没有上下文切换就是最快的。redis默认有16个数据库,默认使用第一个,也就是0号数据库

  • 内存存储,持久化(内存中断电即失,所以数据的持久化非常重要(rdb,aof))
  • 效率高,可以用于缓存
  • 发布订阅系统
  • 计时器,计数器(浏览量)

五大数据类型(String List Set Hash Zset)三种特殊数据类型(geospatial(地理位置,例如附近的人等) hyperloglog(基数,不重复的元素 可以接受误差 ,可以计算网站访问量等) bitmaps(位存储 ,只有0和1两种状态,记录活动和不活动等,打卡不打卡等))这里不做特殊介绍

常用命令

命令描述
select n切换数据库,默认0号数据库
flushdb清空当前数据库中的键值对
flushall清空所有数据库的键值对
keys *查看当前数据库中所有的key
dbsize键总数
exists key是否存在key
del key [key …]删除key
expire key seconds设置键过期
persist key移除键过期时间,永久有效
ttl key获取键有效时长
randomkey随机返回数据库中一个键
rename key1 key2重命名
renamex key1 key2当key2不存在时,key1重命名

String类型

字符串类型是Redis最基础的数据结构,其它的几种数据结构都是在字符串类型基础上构建的,字符串的值可以是:字符串、数字、二进制,其值最大不能超过512M

适用场景:缓存、计数器、流水号,对象存储缓存(共享session)

String 常用命令

String命令描述
set key value设置一个key的value值
setex key seconds value设置key值的同时设置过期时间
setnx key value仅当key不存在时候设置key(分布式锁的基础命令)
mset key value [key value …]设置多个key value
msetnx key1 value1 [key2 value2…]批量设置键值对仅参数中所有的key都不存在时执行,原子性操作,一起成功,一起失败
get key返回key的value
mget key [key …] 批量获取多个key保存的值
decr/incr key将指定key的value数值进行-1/+1(仅对于数字) 取号
incrby/decrby  key n对指定的key以指定的步长n增加减少
incrbyfloat key n为数值加上浮点型数值
append key value向指定的key的value后追加字符串
strlen key返回key的string类型value的长度
getset key value设置一个key的value,并获取设置前的值,如果不存在则返回null
setrange key offset value设置指定位置的字符
getrange key start end获取存储在key上的值的一个子字符串

String命令演示

127.0.0.1:6379> set name shilei 
127.0.0.1:6379> append name "1" #追加name里存储的值 如果key不存在的话就是直接set
(integer) 7
127.0.0.1:6379> get name
"shilei1"
127.0.0.1:6379> strlen name     #字符串的长度
(integer) 7
127.0.0.1:6379> set num 0
OK
127.0.0.1:6379> incr num        #自动递增(可以做浏览量)
(integer) 1
127.0.0.1:6379> decr num        #自动递减
(integer) 0
127.0.0.1:6379> incrby num 2    #增加设置步长 decrby 一样
(integer) 2

###############字符串截取替换
127.0.0.1:6379> getrange name 0 3  #截取字段串[0,3]
"shil"
127.0.0.1:6379> getrange name 0 -1 #获取全部的字符串
"shilei1"
127.0.0.1:6379> setrange name 1 XX   #替换指定位置字符串
(integer) 7
127.0.0.1:6379> get name 
"aXXdefg"
127.0.0.1:6379> 

############### 如果存在和批量设置
127.0.0.1:6379> setex aaa 30 hello  #设置过期时间 存在的话
OK
127.0.0.1:6379> setnx aaa db        #如果不存在在设置
(integer) 0
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3  #批量设置值,空格分开
OK
127.0.0.1:6379> mget k1 k2 k3    #批量获取值
1) "v1"
2) "v2"
3) "v3"
127.0.0.1:6379> msetnx k1 v1 k4 v4 #msetnx是一个原子性操作,要么一起成功,要么一起失败
(integer) 0

###############对象设置
127.0.0.1:6379> set user {name:zhangsan,age:20}
OK
127.0.0.1:6379> get user
"{name:zhangsan,age:20}"

###############getset如果不存在,在设置,如果存在的话会设置新的值
127.0.0.1:6379> getset db redis  #先获取在设置
(nil)
127.0.0.1:6379> get db
"redis"

List类型

基本的数据类型,列表(栈和队列),在redis里边可以变成栈和队列,Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),也可以获取指定范围指定下标的元素等。一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过40亿个元素)。

  • 列表中的元素是有序的,可以通过索引下标获取某个元素霍某个某个范围内的元素列表
  • 列表中的元素可以是重复的

List常用命令

list命令描述
lpush/rpush key value1[value2…]从左边/右边向列表中PUSH值(一个或者多个)
lpushx/rpushx key value向已存在的列名中push值(一个或者多个)
linsert key before|after pivot value在指定列表元素的前/后 插入value
lindex key index通过索引获取列表元素
lrange key start end获取list 起止元素 (索引从左往右 递增)
llen key查看列表长度
lpop/rpop key从最左边/最右边移除值 并返回
lrem key count value从头部开始搜索 然后删除指定的value 至多删除count个 count < 0:从尾部开始搜索… count = 0:删除列表中所有的指定value。
ltrim key start end通过下标截取指定范围内的列表
rpoplpush source destination将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部
lset key index value通过索引为元素设值
blpop/brpop key1[key2] timout移出并获取列表的第一个/最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
brpoplpush source destination timeout和rpoplpush功能相同,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

List命令演示

127.0.0.1:6379> lpush list one   #将一个值或者多个值插入到列表的头部(从左边放进去)
127.0.0.1:6379> rpush list three #从右边放进去
(integer) 1
127.0.0.1:6379> lpush list two
(integer) 2
127.0.0.1:6379> lrange list 0 -1 #遍历值
1) "two"
2) "one"
127.0.0.1:6379>
#############移除元素
127.0.0.1:6379> lrange list 0 -1
1) "two"
2) "one"
3) "three"
127.0.0.1:6379> lpop list  #从列表的左边移除
"two"
127.0.0.1:6379> rpop list  #从列表的右边移除
"three"
127.0.0.1:6379> lrem list 1 one #移除一个叫one的值
(integer) 1
##############通过下标获取值
127.0.0.1:6379> lindex list 0
"one"
##############长度
127.0.0.1:6379> llen list  #返回列表的长度
(integer) 3
##############修剪ltrim
127.0.0.1:6379> ltrim list 1 2 #截断列表 通过下标截取长度
OK
##############rpoplpush移除列表最后一个元素并添加到新的列表上
127.0.0.1:6379> rpoplpush list otherlist
"hello"
127.0.0.1:6379> lset list 0 item  #如果列表存在我们会更新值,不存在报错
OK
127.0.0.1:6379> lrange list 0 -1
1) "item"
2) "hello1"
#############插入指定的值
127.0.0.1:6379> linsert list before item  other #将other插入列表中某个值的前面或者后面 after
(integer) 3

Set类型

Redis的Set是string类型的无序集合,我们不能通过索引获取元素。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis中集合是通过哈希表实现的

应用场景:共同关注,共同爱好,二度好友,推荐好友(六度分割理论)

Set常用命令

set命令描述
sadd key member1[member2…]向集合中无序增加一个/多个成员
srem key member1[member2…]移除集合中一个/多个成员
scard key获取集合的成员数
smembers key返回集合中所有的成员
sismember key membe查询member元素是否是集合的成员,存在返回1,不存在返回0
srandmember key [count]随机返回集合中count个成员,count缺省值为1
spop key [count]随机移除并返回集合中count个成员,count缺省值为1
sinter key1 [key2…]返回所有集合的交集(共同好友等
sinterstore destination key1[key2…]在SINTER的基础上,存储结果到集合中。覆盖
sunion key1 [key2…]返回所有集合的并集
sunionstore destination key1 [key2…]在SUNION的基础上,存储结果到集合中。覆盖
sdiff key1[key2…]返回所有集合的差集 key1- key2 - …
sdiffstore destination key1[key2…]在SDIFF的基础上,将结果保存到集合中。覆盖
smove source destination member将source集合的成员member移动到destination集合
sscan key [MATCH pattern] [COUNT count]在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分

Set命令演示

127.0.0.1:6379> sadd myset hello   #添加一个元素
(integer) 1
127.0.0.1:6379> sadd myset shilei
(integer) 1
127.0.0.1:6379> smembers myset    #查看元素
1) "shilei"
2) "hello"
127.0.0.1:6379> scard myset      #获取set集合中内容元素的个数
(integer) 2
127.0.0.1:6379> srem myset hello  #移除某一个元素
(integer) 1
127.0.0.1:6379> spop myset        #随机移除一个元素
"hello"
127.0.0.1:6379> srandmember myset  #随机抽出一个元素
"shilei"
127.0.0.1:6379> srandmember myset 2 #随机抽出指定的个数
1) "shilei"
127.0.0.1:6379> smove myset myset1 shilei  #将集合的元素shilei移动到myset1
(integer) 1
###############集合操作
127.0.0.1:6379> sadd set a
(integer) 1
127.0.0.1:6379> sadd set b
(integer) 1
127.0.0.1:6379> sadd set c
(integer) 1
127.0.0.1:6379> sadd set1 c
(integer) 1
127.0.0.1:6379> sadd set1 d
(integer) 1
127.0.0.1:6379> sadd set1 e
(integer) 1
127.0.0.1:6379> sdiff set set1   #两个集合的差集(去掉共同的)
1) "b"
2) "a"
127.0.0.1:6379> sinter set set1 #两个集合的交集(共同好友)
1) "c"
127.0.0.1:6379> sunion set set1 #两个集合的并集
1) "b"
2) "c"
3) "a"
4) "d"
5) "e"

Hash类型

几乎所有的编程语言都提供了哈希(hash)结构,Redis中 hash 是一个string类型的field和value的映射表value={{field1,value1},{field2,value2}…},可以将一个Hash表作为一个对象进行存储,表中存放对象的信息。

应用场景: 用户信息缓存,hash变更的数据 尤其是用户的信息之类的,经常变动的信息,hash更适合对象的存储,string偏向于字符创

Hash类型命令

hash命令描述
hset key field value将哈希表 key 中的字段 field 的值设为 value。重复设置同一个field会覆盖,返回0
hmset key field1 value1 [field2 value2…]同时将多个 field-value (域-值)对设置到哈希表 key 中
hsetnx key field value只有在字段 field不存在时,设置哈希表字段的值
hget key field value获取存储在哈希表中指定字段的值
hmget key field1 [field2…]获取所有给定字段的值
hexists key field查看哈希表 key 中,指定的字段是否存在
hdel key field1 [field2…]删除哈希表key中一个/多个field字段
hlen key获取哈希表中字段的数量
hkeys key获取所有字段field
hvals key获取哈希表中所有值value
hgetall key获取在哈希表key 的所有字段和值
hincrby key field n为哈希表 key 中的指定字段的整数值加上增量n
hincrbyfloat key field n为哈希表 key 中的指定字段的浮点数值加上增量 n
hscan key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对

Hash命令演示

127.0.0.1:6379> hset myhash file shilei  #存值
(integer) 1
127.0.0.1:6379> hget myhash file         #取值
"shilei"
127.0.0.1:6379> 
127.0.0.1:6379> hmset myhash filed shilei filed1 shilei1  #设置多个值
OK
127.0.0.1:6379> hmget myhash filed filed1  #取出多个值
1) "shilei"
2) "shilei1"
127.0.0.1:6379> hgetall myhash   #获取所有值
1) "file"
2) "shilei"
3) "filed"
4) "shilei"
5) "actor"
6) "shilei1"
127.0.0.1:6379> hdel myhash file  #删除指定的值
(integer) 1
127.0.0.1:6379> hlen myhash    #获取字段的长度
(integer) 2
127.0.0.1:6379> hexists myhash shilei  #判断hash的指定字段是否存在
(integer) 0
127.0.0.1:6379> hkeys myhash   #获取所有的key
1) "filed"
2) "actor"
127.0.0.1:6379> hvals myhash    #获取所有的值
1) "shilei"
2) "shilei1"
127.0.0.1:6379> hincrby myhash ab 1 #自动增加
(integer) 1
hsetnx myhash name hello   #如果不存在创建

Zset类型

在有序集合中保留了不能有重复成员的特性,但其中的成员是可以排序的,每一个元素都会关联一个double类型的分数(score)作为排序依据,score相同时按字典顺序排序。redis正是通过分数来为集合中的成员进行从小到大的排序

应用场景: 排行榜系统,成绩单,工资表

Zset类型命令

zset命令描述
zadd key score member1 [score2 member2]向有序集合添加一个或多个成员,或者更新已存在成员的分数
zcard key获取有序集合的成员数
zscore key member返回有序集中,成员的分数值
zcount key min max计算在有序集合中指定区间score的成员数
zlexcount key min max在有序集合中计算指定字典区间内成员数量
zincrby key n member有序集合中对指定成员的分数加上增量 n
zscan key cursor [MATCH pattern] [COUNT count]迭代有序集合中的元素(包括元素成员和元素分值)
zrank key member返回有序集合中指定成员的索引
zrevrank key member返回有序集合中指定成员的索引,从大到小排序
zrange key start end通过索引区间返回有序集合成指定区间内的成员
zrevrange key start end通过索引区间返回有序集合成指定区间内的成员,分数从高到底
zrangebylex key min max通过字典区间返回有序集合的成员
zrevrangebylex key max min按字典顺序倒序返回有序集合的成员
zrangebyscore key min max返回有序集中指定分数区间内的成员 -inf 和 +inf分别表示最小最大值,只支持开区间
zrevrangebyscore key max min返回有序集中指定分数区间内的成员,分数从高到低排序
zrem key member1 [member2…]移除有序集合中一个/多个成员
zremrangebylex key min max移除有序集合中给定的字典区间的所有成员
zremrangebyrank key start stop移除有序集合中给定的排名区间的所有成员
zremrangebyscore key min max移除有序集合中给定的分数区间的所有成员
zinterstore destination numkeyskey1 [key2 …]计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys:表示参与运算的集合数,将score相加作为结果的score
zunionstore destination numkeys key1 [key2…]计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中

Zset命令演示

127.0.0.1:6379> zadd myset 1 one   #添加值,指定一个标识,方便排序
(integer) 1
127.0.0.1:6379> zadd myset 2 two 
(integer) 1
127.0.0.1:6379> zadd myset 3 three 
(integer) 1
127.0.0.1:6379> zrange myset 0 -1  #遍历
1) "one"
2) "two"
3) "three"
#############实现排序
127.0.0.1:6379> zadd salary 2500 xiaohong
(integer) 1
127.0.0.1:6379> zadd salary 5000 zhangsan
(integer) 1
127.0.0.1:6379> zadd salary 500 shilei
(integer) 1
127.0.0.1:6379> zrangebyscore salary -inf +inf #从小到大排序,从负无穷到正无穷
1) "shilei"
2) "xiaohong"
3) "zhangsan"
127.0.0.1:6379> zrangebyscore salary -inf +inf withscores  #排序时带上钱
1) "shilei"
2) "500"
3) "xiaohong"
4) "2500"
5) "zhangsan"
6) "5000"
127.0.0.1:6379> zrangebyscore salary -inf 2500 withscores  #工资小于等于2500的员工排序
1) "shilei"
2) "500"
3) "xiaohong"
4) "2500"
127.0.0.1:6379> zrevrange salary 0 -1  #从大到小排序
1) "zhangsan"
2) "shilei"
127.0.0.1:6379> zrem salary xiaohong  #移除有序集合中指定的元素
(integer) 1
127.0.0.1:6379> zcount salary salary 1 2' #获取区间的成员数量
Invalid argument(s)

Redis事务
redis的事务中,一次执行多条命令,本质是一组命令的集合,一个事务中所有的命令将被序列化,即按顺序执行而不会被其他命令插入

  • MULTI:使用该命令,标记一个事务块的开始,通常在执行之后会回复OK,(但不一定真的OK),这个时候用户可以输入多个操作来代替逐条操作,redis会将这些操作放入队列中。
  • EXEC:执行这个事务内的所有命令
  • DISCARD:放弃事务,即该事务内的所有命令都将取消
  • WATCH:监控一个或者多个key,如果这些key在提交事务(EXEC)之前被其他用户修改过,那么事务将执行失败,需要重新获取最新数据重头操作(类似于乐观锁)。
  • UNWATCH:取消WATCH命令对多有key的监控,所有监控锁将会被取消。

redis事务是一个队列,放到队列中不会被执行,只有发起执行的时候才会被执行,如果执行之前,redis检测到语法错误,则不会执行命令,如果,在运行之前没有检测出错误,则只会失败一条,其他条成功

#开启事务
127.0.0.1:6379> multi
OK
#命令入队
127.0.0.1:6379> set name shilei
QUEUED
127.0.0.1:6379> set age 20
QUEUED
127.0.0.1:6379> get name
QUEUED
127.0.0.1:6379> get age
QUEUED
#执行事务
127.0.0.1:6379> exec    
1) OK
2) OK
3) "shilei"
4) "20"
#放弃事务discard
127.0.0.1:6379> discard
OK
###命令有错,事务中所有命令都不会执行,如果事务中存在与发行,可以执行。

1. 单独的隔离操作:事务中的所有命令会被序列化、按顺序执行,在执行的过程中不会被其他客户端发送来的命令打断
2. 没有隔离级别的概念:队列中的命令在事务没有被提交之前不会被实际执行
3. 不保证原子性:redis中的一个事务中如果存在命令执行失败,那么其他命令依然会被执行,没有回滚机制
 

Redis持久化

Redis是内存数据库,内存断电即失,所以要将数据持久化保存到磁盘。 redis持久化的意义,在于数据备份和故障恢复。比如你部署了一个redis,作为cache缓存,当然也可以保存一些较为重要的数据。如果没有持久化的话,redis遇到灾难性故障的时候,就会丢失所有的数据。如果通过持久化将数据持久化一份儿在磁盘上去,然后定期比如说同步和备份到一些云存储服务上去,那么就可以保证数据不丢失全部,还是可以恢复一部分数据回来的。redis中的持久化分为 RDB持久化和AOF持久化,

在生产中,一般采用两种方式结合的方式混合模式redis4.0版本以上做持久化方案

RDB持久化机制

RDB全称Redis Database Backup file (Redis数据备份文件),也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。是一个压缩二进制文件,当Redis实例故障重启后,默认从磁盘读取快照文件,恢复数据

RDB执行机制

RDB在停机时执行一次RDB

或者手动执行RDB 

  • save 主线程执行,阻塞其他命令
  • bgsave 开启子线程执行,避免主线程受到影响

RDB持久化配置

redis.conf文件,也就是/etc/redis/6379.conf,去配置持久化

  • save 60 1000:每隔60s,如果有超过1000个key发生了变更,那么就生成一个新的,save可以设置多个
  • dbfilename dump.rdb :配置文件名
  • dump.rdb文件,就是当前redis内存中完整的数据快照文件名,重启之后redis自动恢复数据
  • dir ./ 文件保存的路径目录
  • rdbcompression yes 是否压缩,建议不开启,压缩也会消耗cpu,磁盘的话不值钱

RDB持久化机制的工作流程

bgsave开始时会fork主进程得到子进程,子进程共享主进程的内存数据。完成fork后读取内存数据并写入 RDB文件fork采用的是copy-on-write技术

  • 当主进程执行读操作时,访问共享内存
  • 当主进程执行写操作时,则会拷贝一份数据,执行写操作

​​​​

dump.rdb始终只会有一个。因为每次生成一个新的快照,都会覆盖之前的老快照

AOF持久化

就是追加文件,将我们的所有命令都记录下来,history,恢复的时候就把这个文件全部在执行一遍,恢复速度慢

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

  • AOF默认关闭 appendonly yes 开启
  • 配置AOF文件名 appendfilename"appendonlv.aof"
  • 执行频率:appendfsync everysec 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓存

AOF持久化步骤以及文件同步策略 

AOF 持久化的实现主要是以上三步:命令追加、文件写入、文件同步

命令追加: 将 redis 写操作命令追加到 aof_buf 缓冲区
文件写入: 周期性地将 aof_buf 缓冲区的命令写入 AOF 文件的内核缓冲区。
文件同步:根据配置同步策略,将 AOF 文件缓冲区的内容同步到磁盘

文件同步策略是将文件刷新进磁盘,redis有以下配置

always:每次有命令写入时都立即同步。这提供了最高的数据安全性,但效率最低。

everysec:每秒同步一次。这是一个权衡安全性和效率的策略。最多只丢失 1 秒 的数据

no:让操作系统决定最佳的同步时间。这可能导致数据丢失,但提供了最高的效率

AOF重写

随着追加命令的不断增加,这个 AOF 文件可能会变得很大和冗长。面对这样的问题,Redis 提供了一个非常有效的优化手段,那就是 AOF 重写

AOF 重写,可以看作是对 AOF文件 进行的一次“精简”操作。它的目的是减少AOF文件的大小,并去除那些冗余的、不再必要的命令,使得该文件只包含恢复当前数据集所需的最小命令集

RDB与AOF的区别

RDB文件是间隔保存,可能会造成数据丢失,但丢失量不大,AOF是无限追加文件,恢复数据比较慢,会从头到尾执行一遍所有写操作,AOF运行效率也要比RDB要慢很多

RDB+AOF混合模式

当RDB,AOF都存在时,redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文供保存的数据集要完整。RDB的数据不实时,同时使用两者时服务器重启也只会找AOF文件

开启混合模式:在redis.conf中redis默认开启

缺点AOF 文件中添加了 RDB 格式的内容,使得 AOF 文件可读性变的更差,支持4.0以上版本

详细了解:redis持久化详解

Redis 缓存穿透缓存击穿缓存雪崩

缓存穿透

在某一时刻,用户查询了一个根本查不到的id,这个id缓存中没有,于是查询数据库,但是数据库中也没有,当用户很多的时候,缓存都没有命中(秒 杀),于是都去请求了持久层数据库。这会给持久层数据库造成很大的压力 ,这就是缓存穿透

解决方案:

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

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

缓存击穿

缓存击穿,是指一个key非常热点,在不停的扛着大并发,大并发集中 对这一个点进行访问,当这个key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库,就像在一 个屏障上凿开了一个洞。 当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访 问数据库来查询新数据,并且回写缓存,会导使数据库瞬间压力过大。

解决方案:

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

2、加互斥锁 ,分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布 式锁的权限,因此只需要等待即可。这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考 验很大。setnx命令

缓存雪崩

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

解决方案:

1、部署集群,异地多活

2、限流降级,在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对 某个key只允许一个线程查询数据和写缓存,其他线程等待。

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

SpringBoot 整合Redis

1、导入依赖

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

2、添加配置

spring.redis.host = 127.0.0.1
spring.redis.port = 6379
spring.redis.password = 123456

 3、整合template 存储数据

package com.Bivin;
 
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.ValueOperations;
 
@SpringBootTest
class Springboot09RedisApplicationTests {
 
    /**
     *   第三步:自动装配RedisTemplate对象
     */
    @Autowired
    private RedisTemplate redisTemplate;
 
 
    /**
     *  首先我们知道Redis是一款key,value存储结构的数据库
     *
     *   因此我们假定这个set()测试方法以key,value的形式往Redis数据库中存储数据
     *   
     *   key,value形式的存储命令:set 
     *   key,value形式的取数据命令:get
     *   
     */
 
    @Test
    void set() {
        ValueOperations ops = redisTemplate.opsForValue();    // 首先redisTemplate.opsForValue的目的就是表明是以key,value形式储存到Redis数据库中数据的
        ops.set("address1","zhengzhou");// 到这里就表明Redis数据库中存储了key为address1,value为zhengzhou的数据了(取的时候通过key取数据)
    }
 
    /**
     *  取数据
     */
    @Test
    void get() {
        ValueOperations ops = redisTemplate.opsForValue();  // 表明取的是key,value型的数据
        Object o = ops.get("address1");  // 获取Redis数据库中key为address1对应的value数据
        System.out.println(o);
    }
 
}

上述存储,可以在redis客户端 取出address1的值为 zhengzhou,但是数据没有真正存储到redis数据库中,因此我们需要把第三步中的自动装配的RedisTemplate对象换成StringRedisTemplate对象即可储存成功了。(因为Redis数据库都是字符串形式的数据,所以用StringRedisTemplate对象)

4、整合工具Jedis

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.3.0</version>
</dependency>

需要改写配置 spring.redis.client-name = jedis //默认是lettuce

在springboot 2.x之后,原来使用过的jedis被改为了lettuce

jedis:底层直连,如果多个线程操作的话,是不安全的,如果想要安全使用jredis连接池,更像BIO模式

lettuce:底层采用netty,实例可以再多个线程中共享,不存在线程不安全的情况,可以减少线程数据,更像NIO模式

5、redis存储对象的方式

1、.将对象序列化后保存到Redis 序列化字节数组存储,取出数据反序列化

2、将对象用FastJSON转为JSON字符串后存储

3、将对象用Hash数据类型存储

详情见:redis存储对象三种方式​​​​​​​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值