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,设置不同的过期时间,让缓存失效的时间点尽量均匀。