文章目录
1. Redis中某个key设置了过期时间,是到了这个时间内存就会被回收吗?
Redis中的某个key虽然设置了过期时间,但是并不是到了时间就会被回收,这涉及了Redis的过期删除策略
和内存淘汰机制
2. 为什么要用Redis/为什么要用缓存
主要从高性能和高并发来看
2.1 高性能
假如用户第一次访问数据库中的某些数据时,这个过程会比较慢,因为是从硬盘上读取,将该用户访问的数据存在缓存中,这样下一次访问这些数据就可以从缓存中获取了,操作缓存就是直接操作内存,所以速度相当快,如果数据库中的对应数据改变之后,同步改变缓存中的数据即可
2.2高可用
直接操作缓存能够承受的请求远大于直接访问数据库的,所以我们可以考虑把这些数据库中的部分数据转移到缓存中去,这样用户的一部分请求会直接到缓存这里面,而不用经过数据库
3.为什么要用redis而不用map做缓存?
- 缓存分为本地缓存和分布式缓存,以Java为例,使用自带的map实现的是本地缓存,最主要的特点是轻量获以及快速,生命周期随着jvm的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性
- 使用redis获memcached之类的称之为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性,缺点是需要保持redis或memcached服务的高可用性,整个程序架构上较为复杂;
4.redis常见数据结构以及使用场景分析
4.1 String
常用命令: set、get、decr、incr、mget等
String的数据结构是简单的key-value类型,value类型其实不仅可以是Sting,亦可以是数字,常规key-value缓存ji
应用:常规技术,微博数、粉丝数等;
4.2 List
常用命令:lpush,rpush,lpop,rpop,lrange等
list就是链表,Redis list的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的list结构来实现
Redis list的实现为一个双向链表,即可以支持方向查找和遍历,更方便操作,不过带来了部分额外的内存开销
另外还可以通过lrange命令,就是从某个元素开始读取多少个元素,可以基于list实现分页查询,这是一个很棒的功能,基于redis实现简单的高性能分页,可以做微博那种下拉不断分页的东西,性能高
4.3 Set
常用命令:sadd,spop,smembers,sunion等
set对外提供的功能与list相似,类似是一个列表功能,特殊之处set是可以自动排重的
当年需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的,可以基于set轻易实现交集,并集,差集的操作SINTER \ SUNION \ SDIFF
比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合,Redis可以非常方便的实现如同共同关注,共同喜好等功能,这个过程就是求交集的过程,具体命令为sinter store key1 key2 key3将交集存在key1内
4.4 Hash
常用命令:hget,hset, incr, hgetall等
Hash是一个String类型的filed和value的映射表,hash特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值,比如我们可以Hash数据结构存储用户信息,商品信息等等。
4.5 Sorted Set
常用命令:zdd,zrangre,zren,zcard等
和set相比,sorted set增加了一个权重参数score,使得集合中元素能够按score进行有序的排列。
举例:在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息,可以理解为按消息维度的消息排行榜等信息,适合用Redis中的sortedset结构进行存储
5. Redis设置过期时间
Redis中有个设置过期时间的功能,即对存储在redis数据库中的值可以设置一个过期时间,作为一个缓存数据库,这是非常实用的,如我们一般项目中的token或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据处理方式,一般都是自己判断过期,这样无疑会严重影响项目的性能
我们set key的时候,都可以给一个expire time就是过期时间,通过过期时间我们可以指定这个key可以存活的时间
如果假如你设置了一批key只能存活一个小时,那么接下来一小时后,redis是怎么对这批key进行删除的?定期删除+惰性删除
6. 删除策略
6.1 定期删除:
定期删除:redis默认是隔离100ms就随机抽取一些设置了过期时间的key,检查是否过期,如果过期就删除,注意这里是随机抽取的
6.2 惰性删除
惰性删除:定期删除可能会导致很多过期key到了时间并没有删除掉,所以就有了惰性删除,假如你的过期key,靠定期删除没有删除掉,还停留在内存里,除非你的系统去查下下那个key,才会被redis给删除掉,这就是所谓的惰性删除
6.3 存在问题解决方法
但是仅仅通过设置过期时间还是有问题的,我们想一下,如果定期删除漏掉了很多过期的key,然后你也没及时去查,也就没走惰性删除,此时会怎样?如果大量过期key堆积在内存里,导致redis内存块耗尽了,怎么解决这个问题呢?------ 内存淘汰策略
7. redis 内存淘汰机制
redis 提供了6种数据淘汰策略:
- volatile-lru: 从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
- volatile-ttl:从以设置过期时间的数据集(server.db[i].experies) 中挑选将要过期的数据淘汰
- volatile-random:从已设置过期时间的数据集(server.db[i].experies) 中任意选择数据淘汰
- allkey-lru:但内存不足以容纳新写入数据时,在键空间中,移除最近最少用的key(这个时最常用的)
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
- no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错,这个应该没人使用吧。
7. redis持久化机制
很多时候我们需要持久化数据也就是内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。Redis支持两种不同的持久化操作,Redis的一种持久化方式叫快照(RDB),另一种方式是只追加问价(AOF)
7.1 快照(snapshotting)持久化(RDB)
Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本,Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis的性能),还可以将快照停留在原地以便重启服务器的时候使用
RDB持久化是Redis默认采用的持久方式,在redis.conf配置文件中默认有此下配置:
默认每隔5分钟创建一个快照副本,这种方式占用空间大,而且会丢失间隔时间分钟之内的数据,但是他适合做备份,恢复时,可以根据需要恢复任意间隔的数据
7.2 AOF(append-only file)持久化
与RDB持久化相比,AOF持久化的实时性更好,因此已成为主流的持久化方案,默认情况下Redis没有开启AOF方式的持久化,可以通过appendonly参数开启
appendonly yes
开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入到硬盘中的AOF文件
在Redistribution的配置文件中存在三种不同的AOF持久化方式,他们分别是:
- appendfsync always // 每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
- appendfsync everysec // 每秒钟同步一次,显示的将多个命令同步到硬盘
- appendfsync no // 让操作系统决定何时进行同步
为了兼顾数据和写入性能,用户可以考虑appenfsync everysec选项,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响,而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据,当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以适应硬盘的最大写入速度。
7.3 Redis 4.0对于持久化机制的优化
Redis开始支持RDB 和AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdb-preamble 开启)。
如果混合持久打开,AOF重写的时候就直接把RDB的内容写到AOF文件开头,这样做的好处是可以结合RDB和AOF的优点,快速加载同时避免丢失过多的数据,当时缺点也是有的,AOF里面的RDB部分是压缩格式不在是AOF格式,可读性较差
8. redis事务
Redis通过MULTI,EXEC,WATCH等命令来实现事务功能,事务提供了一种将多个命令请求打包,然后一次性,按顺序执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,他会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求
在传统的关系数据库中,常常用ACID性质来检验事务功能的可靠性和安全性,在Redis中,事务总具有原子性,一致性,隔离性,并且当Redis运行在某种特定的持久化模式下时,事务也具有持久性
redis对事务的支持是指可以一次执行多个命令,本质是一组命令的集合,一个事务中的所有命令都会序列化,按顺序地串行化执行而不会被其他命令插入,不许加塞
涉及的命令:
mulit:开启一个事务
exec:执行一个事务
discard:取消事务,如果watch监控 取消watch
watch:监控一个key或多个key是否发送变化
unwatch:取消监控
在项目中,我们经常配合使用watch+mulit+exec+discard达到一个乐观锁的目的处理页面,比如在秒杀项目中我们的预减库存操作就是利用这个方式,用watch监控一个商品库存,开启事务尝试修改库存,如果在我们修改期间我们的这条数据被其他人修改过,那么这个事务就会提交不成功,达到一个乐观锁的目的,redis处理事务的机制稍弱,需要我们在代码中多加控制
9. Redis使用过程中遇到的具体问题
9.1 缓存雪崩和缓存穿透问题的解决方案
9.1.1 缓存雪崩
- 简介:缓存同一时间大面积失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量的请求而崩掉
- 解决办法
- 事前:尽量保证整个redis集群的高可用性,发现机器宕机尽快补上,选择合适的内存淘汰策略
- 事中:本地ehcache缓存+hystrix限流&降级,避免MySQL崩掉
- 事后:利用redis持久化 机制保存的数据尽快恢复缓存
9.1.2 缓存穿透
- 简介:一般是黑客故意请求缓存中不存在的数据,导致所有的请求都落到数据库上,短时间内承受大量的请求而崩掉
- 解决办法:有很有种方法可以有效解决缓存穿透的问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被bitmap拦截掉,从而避免了对底层存储的查询压力,还有一种办法就是,如果查询返回的数据为空,我们仍然把这个空结果进行缓存,但他的过期时间会很短,最长不超过5分钟
9.1.3 缓存击穿
- 简介:这里需要注意和缓存穿透的区别,缓存击穿,是指一个key非常热点,在不停扛着并发,大并发几种对这一个点进行访问,但这个key在失效的瞬间,持续大的并发就穿破缓存,就像在一个屏障上 凿开了一个洞
- 当某个key在过期的瞬间,有大量的请求并发访问,这类数据一般是热点数据,由于缓存过期,会同时访问数据库来查询最新数据,并且回写缓存,导致数据库瞬间压力过大
- 解决方案
- 设置热点数据永不过期
从数据层面来看,没有设置过期时间,所以不会出现热点key过期后产生的问题 - 加互斥锁
分布式锁:使用分布式锁,保证对于每个key同时只有一个线程去查询后端服务,其他线程没有获得分布式锁的权限,因此只需要等待即可,这种方式将高并发的压力转移到了分布式锁,因此对分布式锁的考验很大
10.如何保证缓存与数据库双写时数据一致性
你只要用缓存,就可能会涉及到缓存与数据库存储双写,你只要双写,就就一定会有数据一致性的问题,那么你如何解决一致性问题?
一般来说,就是如果你的系统不是严格要求缓存+数据据库必须一致性的话,最好不要做这个方案,如果不得不做,我们可以使用读请求和写请求串行化,串到一个内存队列里去,这样就可以保证一定不会出现不一致的情况,串行化之后,就会导致系统的吞吐量会大幅度的降低,用比正常情况下多几倍的机器去支撑线的一个请求
11. 主从复制(读写分离)
主从复制的好处有2点:
- 避免redis的单点故障
- 构建读写分离架构,满足读多写写少的场景
11.1 概念
主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器,前者称为主节点(master/leader)后者称为(slave/follower);数据的复制是单向的,只能由主节点到从节点,master以写为主,slave以读为主
11.2 主从复制的作用主要包括:
- 数据冗余:主从复制实现了 数据的热备份,是持久之外的一种数据的冗余方式
- 故障恢复:当主节点出现问题时,可以由从结点提供服务,实现快速的故障恢复;实际上是一种服务的冗余
- 负载均衡:在主从复制的基础上,配合读写分离,可以由主节点提供写服务,由从结点提供读服务(即写redis数据时应用连接主节点,读redis数据时应该用从节点),分担服务器负载;尤其是在写少读多的场景下,通过多个从节点分担读负载,可以大大提高redis服务器的并发量
- 高可用:主从复制还是哨兵和集群能够实施的基础,因此说主从复制是redis高可用的基础
12. 哨兵模式(自主选举老大的模式)
12.1 什么是哨兵
顾名思义:哨兵的作用就是对redis的系统运行情况的监控,他是一个独立的功能,他的功能有两个
- 监控主数据库和从数据是否运行正常
- 主数据出现故障后自动将从数据库转换为主数据库
12.2 原理
单个哨兵的架构
多个哨兵的架构
多个哨兵,不仅同时监控主从数据库,而且哨兵之间互为监控
13. 集群中的主从复制架构
出现故障