1.为什么要用Redis作缓存
高性能
因为如果是用户第一次访问某些数据,那么这些数据就会从硬盘上读取,读取的速度比较缓慢。
这时redis可以把读取的数据存到缓存中,那么下次读取数据的时候就可以直接从缓存中获取。
操作缓存就是直接操作内存,所以速度快。
高并发
直接操作缓存的承载能力是远远大于直接访问数据库的,所以设置缓存可以把部分数据存到缓存,那么部分的请求会直接访问缓存,不用经过数据库
2.为什么用redis而不用map/guava做缓存?
缓存分为本地缓存和分布式缓存。map和guava是本地缓存,快速且轻量级,但是其生命周期会随着jvm的销毁而结束。并且在多实例的情况下,每个实例都会有自己的缓存,缓存不具有一致性
但是对于redis等分布式缓存,多个实例共享一份缓存数据,缓存具有一致性。 缺点是,为了保持redis的高可用性,结构上比较复杂
3.redis的线程模型
redis是单线程模型。
这是因为redis中使用的文件事件处理器 file event handler是单线程,所以redis才算是单线程模型
文件事件处理器包含四个部分
1.IO多路复用
2.多个socket
3.文件事件分配器
4.事件处理器
执行过程: 由于是多个socket,那么会并发产生不同的操作,而IO多路复用会将产生的多个操作放进队列中,然后文件事件分派器从队列中取出一个事件,交给事件处理器处理。
4.redis和memecached的区别
- redis的数据类型丰富,而memecached只有单一的k-v类型
- redis支持数据的持久化,而memecached是把数据全部放在内存
- memecached没有原生的集群模式,只能后期自己往集群分片写入数据;但是redis原生支持集群模式
redis数据类型
1.String
字符串类型 就是简单的key-value,value可以是String 也可以是数字类型
2.Hash
可以在val中存储多个key-val的数据,类似于对象的属性。
所以hash 类型主要用于存储对象
3.List
链表,底层是一个双向链表,支持反向查找和遍历
(lpush lpop rpush rpop lrange)
lrange命令是指从某个元素开始读取多少个元素,所以可以基于list的lrange实现分页
4.Set
主要方法:sadd spop smembers sunion
set的主要特点就是不能存重复的元素,而且set还提供 smember方法可以用于判断元素是不是在集合内。可以用set实现交集、并集、差集的操作
5.Zset
有序集合,相比Set多了一个score权重参数。可以按照score进行排序
redis设置过期时间
在set key的 时候可以设置一个expire time 用于指定key 的过期时间。
在expiretime到时的时候,redis怎么具体删除的呢?:定期删除+惰性删除
定期删除:redis默认的是每隔100ms就从库中随机抽取key,检查是否过期,过期了则删除。完全随机的机制避免了遍历redis中所有的key
惰性删除:定期删除会导致一部分过期的key没被删除。但是redis是不会主动删除的,只有当系统主动去检查过期的key的时候,才能删除过期的key。
redis的内存淘汰机制
上面说的定期删除+惰性删除不能将全部过期的key删除,那么就会导致一个问题:大量过期的key存在于内存中,导致redis内存耗尽。所以redis的内存淘汰机制用于解决redis内存耗尽的问题
- volatile-lru:从设置过期时间的数据集中区最少使用的数据进行淘汰
- volatile-ttl:从设置过期时间的数据集中找 将要过期的数据淘汰
- volatile-random:从设置过期时间的数据集中 随机找数据淘汰
- allkeys-lru:从数据集中找是用最少的数据淘汰
- allkeys-random:从数据集中随机找数据淘汰
- no-eviction:禁止淘汰数据,也就是说内存满了后,再插入数据会失败
4.0版本后新增
7. volatile-lfu:从设置过期时间的数据集中找 最不经常使用的 淘汰
8. allkeys-lfu:全数据集中找最不经常使用的key淘汰
redis的持久化机制
redis持久化为的是解决重启后数据恢复。
持久化指的是将内存中的数据保存到硬盘中,这样可以帮助重启、或机器故障后恢复数据
两种方式 快照(RDB)和 只追加文件(AOF)
RDB
RDB:周期性的对redis中数据进行持久化,意思是每个一段时间就会生成redis内存的一份完整的快照。生成的新的快照会覆盖老的快照。
redis持久化默认采取的就是快照方式。
怎么配置RDB?
在redis.conf文件中 写入 save 900 1
代表 900秒后 如果至少有1个key发生变化,就执行快照
关于冷备
RDB适合做冷备份: 将每次的快照上传至云服务器中,可以实现冷备份
缺点:丢失数据多。
AOF也可以做冷备,但是只有一个文件,并需要每隔一段时间就copy一次
AOF
AOF实时性更好
redis默认不开启AOF,可以通过代码
appendonly yes
开启
在redis中,每执行一条更改redis数据的命令,redis就会将命令写入硬盘中的AOF文件,当AOF文件中的命令越来越多时,就会rewrite一个新的AOF文件,把旧的删除,并且根据LRU内存淘汰机制,删除不需要的key
rewrite触发机制:上一次的AOF文件大小为现在的一半,或者现在AOF文件大于64M
appendfsync always 每次数据修改都会更新AOF
appendfsync everysec 每秒更新
appednfsync no 操作系统决定何时更新
AOF优点:
- 数据丢失少
- 没有磁盘寻址的开销,写入性能高(因为写入的时候会先交给OS cache,然后再写入硬盘)
- 对误删可以紧急修复。在rewrite前,拷贝AOF文件,并将误删命令删除,就可以恢复数据了
AOF缺点:
1 AOF文件比RDB文件大
2 做数据恢复比较慢,做冷备份,定期的备份不方便
Redis4.0以后对于持久化的优化
4.0以后开始支持RDB和AOF混合持久化
好处是能结合AOF和RDB的共同优点,快速加载的痛死避免丢失过多的数据
缺点是 保存在AOF中的RDB部分是压缩的,可读性差
Redis的事务处理
Redis的事务在开启的时候,会将命令一条一条的插入到一个队列中,然后这些命令不会被真的执行,EXEC提交后,会统一执行队列中的命令
MULTI:可以用于开启事务
EXEC:用于提交事务
DISCARD:放弃事务,事务中所有的命令取消
WATCH:监控一个或多个key,如果在事务执行期间,这些key被修改,那么事务执行失败,然后需要重新获取数据(类似乐观锁)
UNWATCH:取消对key的监控
什么是缓存雪崩
就是由于缓存同一时间大面积的失效,导致大量的请求直接访问到数据库,从而导致数据库崩溃
怎么解决缓存雪崩?
- 事前:保证redis多集群的高可用性
- 事中:设置本地缓存ehcache+hystrix限流组件,并降级,避免mysql崩溃
- 事后,利用redis持久化机制,尽快恢复
什么是缓存穿透
就是大量请求不在缓存中,直接访问数据库,请求根本没有经过缓存这一层
怎么解决缓存穿透?
- 缓存无效的key:如果请求的key数据库和redis都没有,那么可以让redis缓存这个key,并设置过期时间
set key value EX 1000
这个方法只能解决key变化不大的请求,对于大量且变换的key,这种方法就不可行。
- 布隆过滤器
先将所有可能存在的key都存入布隆过滤器中,这时,当访问的请求不存在布隆过滤器中,就会过滤掉该请求;存在的话,请求会到达redis,再到达数据库。
如何解决redis并发竞争key的问题
多个系统同时操作一个key,就会导致key被并发竞争。
通过分布式锁来防止这种情况
1 .数据库
2. Meecache 的 add命令
3. redis 的setnx
4. zookeeper的临时节点
zookeeper实现方式:当给方法加锁的时候,zookeeper会在对应的方法的对应的指定节点生成一个唯一的瞬时的有序节点。随后可以判断有序节点的最小节点是否获取了锁。当锁释放的时候,需要将瞬时节点删除。
如何解决redis缓存和数据库双写一致性的问题
由于请求redis和数据库的先后顺序不一致,会导致请求在更新数据库中数据的时候,出现redis双写不一致
解决方法:如果要求缓存和数据库中数据必须完全一致,那么久需要将读请求和写请求串行化,放到一个队列中,同时执行,这样就保障了双写一致,但是这样就会导致吞吐量大大降低