一、为什么要用redis而不用map做缓存?
缓存分为本地缓存和分布式缓存。
map 实现的是本地缓存,生命周期随着jvm 的销毁而结束,并且在多实例的情况下,每个实例都需要各自保存一份缓存,缓存不具有一致性。
使用redis 或memcached 之类的称为分布式缓存,在多实例的情况下,各实例共用一份缓存数据,缓存具有一致性。
二、为什么要用 redis/为什么要用缓存?
- 高性能:假如用户第一次查询数据库中的某些数据,直接从硬盘读取这个过程比较慢,将用户访问的数据直接存缓存中,那么下次直接从缓存中取即可。
- 高并发:直接操作缓存能够承受的请求远远大于直接操作访问数据库,所以可以考虑把数据库的部分数据放入到缓存中。
三、Redis 单线程为什么这么快?
- 完全基于内存;
- 采用单线程,避免了不必要的上下文切换和竞态条件;
- 使用IO 多路复用模型;
- 数据结构比较简单,对数据的操作也比较简单。
四、为什么不采用多线程或多进程处理、单线程处理的缺点?
多线程处理可能会涉及到锁;
多线程处理会涉及到线程切换而消耗CPU。
耗时长的命名会导致并发下降,不止是读并发,写并发也会下降;
无法发挥多核CPU 的性能,不过可以通过单机开多个实例来解决。
五、Redis 的IO 多路复用技术
“多路” 指的是多个网络连接,“复用” 指的是复用同一个Redis 处理线程。
六、Redis 和Memcached 的区别?
- Redis 支持更丰富的数据类型:不仅支持简单的k-v 类型数据,还提供了list、set、zset、hash。memcache 支持是String。
- Redis 支持数据持久化,可以将内存的数据保存到磁盘中,重启的时候再次加载进行使用。memcached 把数据全部加载到内存中。
- 集群模式:memcached 没有原生的集群模式,只能依靠客户端来实现往集群中分片写入数据;但Redis 支持原生的cluster 模式。
- memcached 是多线程的,非阻塞IO 复用的网络模型;Redis 使用的是单线程的多路IO 复用模型。
七、Redis 设置过期时间与删除
Redis 有四种方式可以设置过期时间:
set message "hello"
- expire message 60 60s的过期时间
- pexpire message 60 60毫秒过去时间
- expiret message 60 秒时间戳
- pexpiret message 毫秒数时间戳
PTTL key 返回key剩余的过期时间(毫秒)。
TTL key返回key剩余的过期时间(秒)。
移除过期时间:
persist message
Redis 删除过期值的策略:
- 定期删除:Redis 默认每隔100ms 就随机抽取一些社设置了过期时间的key,检查是否过期,如果过期就删除。 存在问题:到过期时间并没有删除掉。
- 惰性删除:定期没有删除没有删掉的数据只有当你再次查询这个key时,才会被Redis 给删掉。
这两种方式都没能及时删除掉过期数据导致内存耗尽:Redis 内存淘汰机制
Redis 提供了6种数据淘汰策略,4.0 后又增加了2种。
八、Redis 的持久化机制
默认情况下Redis 将数据快照保存为dump.rdb 的二进制文件。
当Redis 要持久化数据时:
1.1 Redis 调用forks,同时拥有父进程和子进程
1.2 子进程将数据集写入到一个临时的RDB 文件中
1.3 当子进程完成对新RDB 文件的写入时,Redis 用新RDB 文件替换原来的RDB 文件。AOF 只追加操作的文件
2.1. AOF 持久化方式需要在配置文件中打开(appendonly yes),可以配置Redis 持久化的方式:
2.11 每次有新命令就追加AOF 文件一次
2.12 每秒中执行一次
2.13 将数据交给操作系统处理,最不安全。
AOF 文件会越来越大,为了处理这种情况,Redis 可以在不打断客户端的情况下,对AOF 文件进行重建(rebuild)。如果AOF 文件损坏,Redis 在重启时会拒绝载入这个AOF 文件,从而确保数据的一致性不被破坏。
九、RDB 和AOF 的优缺点
aof 的优点:
备份数据更稳健,丢失数据的概率更低。
可读写的日志文件,可以处理误操作。
aof 的缺点:
比起rdb 文件占用更多的磁盘空间。
恢复备份速度慢
aof 存储的写指令,rdb 存储的数据。
十、Redis 的集群
1. 一主二仆模式
主机可读可写,从机只能读
从机是从开始复制而非从切入点开始复制(无论何时宕机)。
主机shutdow 后,从机仍然是从机,不会变成主机(没有哨兵模式)。
2.薪火相传模式(主—> 从 —> 从)
一旦某台服务器宕机,后面的从机都无法备份。
从服务器仍然不能变成主服务器。
3.哨兵模式
反客为主的自动版
十一、缓存穿透、缓存击穿、缓存雪崩
-
缓存雪崩:缓存同一时间大面积失效,所有的请求都落在数据库上,造成数据库在短时间内承受大量的请求而崩掉。
解决办法:
事前:保证Redis 集群的高可用性,选择合适的内存淘汰策略;
事中:本地ehcache 缓存 + hystrix 限流&降级,避免MySQL崩掉;
事后:利用Redis 持久化机制将保持的数据尽快恢复
-
缓存穿透:大量请求的key 在缓存中根本不存在,导致请求直接打在数据库上,根本没有经过缓存一层。
一般MySQL 默认的最大连接数在 150 左右,这个可以通过 show variables like '%max_connections%'; 命令来查看。cpu,内存,磁盘,网络等无力条件都是其运行指标,这些指标都会限制其并发能力!所以,一般 3000 个并发请求就能打死大部分数据库了。
解决办法:
2.1 做好参数校验,比如id不能小于0 等;
2.2 缓存无效key,这种方式可以解决key 变化不频繁的情况,如果key 变化频繁则建议key 的过期时间设置短一点。
2.3 布隆过滤器:我们需要的就是判断 key 是否合法,把所有可能存在的请求的值都存放在布隆过滤器中,当用户请求过来,我会先判断用户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会走下面的流程。
关于布隆过滤器的介绍 缓存击穿
一般情况下我们是这样设计 key 的: 表名:列名:主键名:主键值。
十二、Redis 的事务以及相关的命令
multi......命令......exec
discard 取消事务
redis同一个事务中如果有一条命令执行失败,其后的命令仍然会被执行,没有回滚。
组队中某个命令出现了报告错误,执行时整个的队列都会被取消。
watch key
在执行multi 之前,可以先执行watch 命令,监视一个或多个key,如果在执行事务之前这些key 被其它命令所改动,则事务将被打断。watch 与 multi 之间不允许有任何的 set key1 value操作。
十三、如何解决 Redis 的并发竞争 Key 问题
并发竞争 Key 的问题也就是多个系统同时对一个 key 进行操作。
推荐一种方案:分布式锁(zookeeper 和 redis 都可以实现分布式锁)。如果不存在 Redis 的并发竞争 Key 问题,不要使用分布式锁,这样会影响性能。
十四、如何保证缓存与数据库双写时的数据一致性?
https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/redis-consistence.md