redis:

redis的数据类型

redis支持的几种数据类型:
String(字符串) Hash(哈希) List(有序可重复元素集合) Set(无序不重复元素集合) ZSet(有序不重复元素集合)

String:

键和值都是字符串,存储简单字符串数据类型,一个键最大能存储512M 。

命令 功能
set key value 设置值(多次设置相同的key的value值会覆盖)
get key 获取值
setnx key value 不存在则设置值
del key 删除键值
setex key 有效期时间 value 键值有效期之后自动失效
mset key1 value1 key2 value2 …keyn valuen 一次设置多个值
getset key value 一次获取多个值
incr / decr key 对一个值进行自增或自减
incrby / decrby key 步长 对一个值进行步长自增或自减
append key val 给key的value追加val的字符串
strlen key 返回key对应的字符串的长度
mget key1 key2 …keyn 一次获取多个值

Hash:

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

可以理解为:filed=对象,key=对象的属性,value=对象的属性的值

命令 功能
hset filed key value 存一个键值到名字为filed的hash里面
hget filed key 从名为filed的hash里面取出key的value
hsetnx filed key value 不存在则设置value值
hexists filed key hash里面是否存在key
hlen filed hash里面所有键的数量
hdel filed key 删除名称为filed的hash里面可以对应的键值
hkeys filed 获取hash里面所有的key
hvals filed 获取hash里面所有的value
hgetall filed 获取hash里面所有的key,value
hmset filed key1 value1 key2 value2 …keyn valuen 一次设置多个值
hmget filed key1 key2 … keyn 一次获取多个值
hincrby/hdecrby filed key 步长 hash里面对应的key的value的步长自增,自减

list:

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素导列表的头部(左边)或者尾部(右边)。列表最多可存储 使用的底层数据结构是双向链表,所以获取越接近两端的元素就越快。这意味着即使数据量很大,获取两头的少量数据也是极快的。双端链表,可以称为栈,也可以成为队列。

一个key对应多个value:

 lkey:
      value1
      value2
      value3
      ...
      valuen
命令 功能
lpush list_name value 从左边加入元素
rpush list_name value 从右边加入元素
lrange list_name 0 -1 取出list里面的所有元素 0 -1代表所有元素
linsert list_name before value insertvalue 在value之前插入insertvalue
lset list_name 下标 value 将指定下标的值替换为value
itrim list_name 下标 上标 保留下标到上标的元素
lpop list_name 从list的左边删除元素,并返回删除的元素
rpop 从list的右边删除吧元素,并返回删除的元素
rpoplpush list_name1 list_name2 value 先从list_name1的右边取出元素,在将该元素添加到list——name2的左边
lindex liat_name 下标 返回下标处的元素
llen list_name 返回元素的个数

Set:

Redis的Set是string类型的无序集合。
集合是通过哈希表(hashtable)实现的,所以添加,删除,查找的复杂度都是O(1).set还提供了几个集合之间的交集,并集,差集的运算

命令 功能
sadd set_name value 向set_namwe 的set中添加value值
srem set_name value 移除set_name中的value值
spop set_name 移除并返回集合中的一个随机元素
smembers set_name 查看set中的所有元素
sdiff set_name1 set_name2 取两个集合的差集,哪个集合在前面就以哪个集合为标准
sdiffrtore set_name1 set_name2 set_name3 取set1 和set2的差集并储存到set3
sinter set_name1 set_name2 取2个集合的交集
sinterstore set_name3 set_name1 set_name2 取set1 和set2的交集储存到set3
sunion set_name1 set_name2 取2个集合的并集
sunionstore set_name3 set_name1 set_name2 取set1 set2的并集存储到set3
smove set_name1 set_name2 value 将set1的value值移到set2中
scard set_name 查看元素的集合个数
sismember set_name value 查看value是否在set中
srandmember set_name 随机返回set中的一个元素

ZSet:

Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。
不同的是每个元素都会关联一个double类型的分数(相当于每一个元素的一个权重)。redis正是通过分数来为集合中的成员进行从小到大的排序。
zset的成员是唯一的,但分数(score)却可以重复,ZSet也是使用hashtable实现的。

命令 功能
zadd set_name score value 向集合中添加元素并给一个分数
zrange set_name 0 -1 列出集合中的所有元素,并显示分数
zrem set_name value 删除set_name中的value
zincrby set_name increment member 步长 以步长自动递增或递减
zrangebyscore set_name min max 返回分数在min 和 max之间的元素
zremrangebyrank set_name start stop 删除set中指定排名区间rank区间的所有数据
zrank set_name value 返回元素的分数
zrevrank set_name value 返回value在集合中的排名,有序的元素集合按照分数从大到小排名
zcard set_name 返回集合中元素的个数
zcount set_name min max 返回所有符合条件min<score<=max的成员个数

redis的持久化方案

redis的持久化方案有2种:rdb和aof方式

rdb方式:

rdb的持久化方式是通过快照实现的当符合一定条件时redis自动将内存中的数据进行快照并持久化到磁盘.快照生成的文件名为dump.rdb

rdb是redis默认的持久化方式

参数设置:

持久化条件设置

sava 开头的一行就是持久化配置,可以配置多个条件(每行配置一个条件)


sava 900 1       每隔900秒有1次增删改操作就生成rdb文件
sava 300 10      每隔300秒有10次增删改操作就生成rdb文件
sava 60 10000    每隔60秒有10000次增删改操作就生成rdb文件

redis启动后以正当行为停机了,最后修改的数据会持久化到dump文件中,下次redis启动时会读取dump文件,将数据加载到内存(数据不丢失)。如果redis异常中断,则最后修改的数据不会持久化到磁盘(数据丢失)

存在问题 通过rbd方式实现持久化,一旦redis异常退出,就会丢失最后一次快照以后更改的数据,如果数据很重要以至于无法承受任何损失,考虑使用aof方式进行持久化。

优点:减少磁盘的I/O次数,性能会更好

aof方式

默认情况下redis没有开启aof的持久化方式
可以通过修改redis.conf配置文件中的appendonly参数开启。

appendonly yes

开启aof持久化化后每执行一条会更改redis的数据的命令,redis就会将该命令写入磁盘中的aof文件,aof文件和rdb的文件位置相同,都是通过dir参数设置的。默认的文件名是appendonly.aof

:如果开启了aof持久化方式,则rdb会和aof持久化方式同时生效。

缓存穿透

缓存穿透本质上是查询一个缓存和数据库中都不存在的key值,redis的缓存层无法命中,导致请求再次指向执行数据库层的情况。
由于是查询到值不存在(当然也不存在redis缓存中),导致请求总是“穿透”redis发往数据库层,因此缓存层失去了“保护”关系数据库层的意义。

缓存击穿

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

缓存击穿和缓存穿透的解决方案:

1.布隆过滤

对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询。
布隆过滤参考我的另一篇文章:https://editor.csdn.net/md/?articleId=102906737

2.缓存空对象

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

缺点:

1.如果空值能够被缓存起来,这就意味着缓存需要更多的空间存储更多的键,因为这当中可能会有很多的空值的键;

2.即使对空值设置了过期时间,还是会存在缓存层和存储层的数据会有一段时间窗口的不一致,这对于需要保持一致性的业务会有影响

缓存雪崩

缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i3UHWJNn-1583396788019)(https://i.loli.net/2020/03/05/mCDAhOfpUEFrdbi.png)]

缓存雪崩解决方案

1.redis搭建高可用集群

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

要保持一致性的业务会有影响

缓存雪崩

缓存雪崩是指,缓存层出现了错误,不能正常工作了。于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会挂掉的情况

[外链图片转存中…(img-i3UHWJNn-1583396788019)]

缓存雪崩解决方案

1.redis搭建高可用集群

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

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