1. 在项目中是如何使用缓存的?为何要用?不用行不行?用了可能会有哪些不良后果?redis
结合项目业务,主要两个目的:高性能和高并发。缓存走内存,自然支持高并发。算法
不良后果:缓存
2. Redis的线程模型是什么?网络
Redis内部使用文件事件处理器(file event handler),这个处理器是单线程,全部Redis才叫单线程模型。多线程
采用IO 多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。架构
文件处理器包含4个部分:并发
socket并发不一样操做,对应不一样文件事件处理器,IO多路复用监听多个socket,将产生事件的socket放入队列排队,less
文件事件分发器每次从队列中取出一个socket,根据socket事件类型交给对应的事件处理器进行处理。dom
I/O多路复用:异步
在同一个线程里面, 经过拨开关的方式,来同时传输多个I/O流。
Redis 通讯过程:
3. 为何Redis单线程模型也能效率这么高?
4. Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
数据类型:
string:最基本。普通Set get,简单K-V缓存
set name zhangsan
hash:类map结构。能够把结构化数据(对象)缓存。把简单对象缓存,后续操做能够只修改对象中某个字段。
key=user value={ "id": 100, "name":"zhangsan", "age",20 }
hset user id 100
hset user name zhangsan
hset user age 20
list:有序列表。微博大V粉丝以list格式放Redis缓存。
key=某大V value=[zhangsan, li, wangwu]
list的lrange命令:从某个元素开始读取多少个元素,能够基于list实现简单分页。
lrange mylist 0 -1
0开始位置,-1结束位置,结束位置为-1时,表示列表的最后一个位置,即查看全部。
lpush, lpop //栈(FILO)
set: 无序,自动去重。
JVM的hashset能够去重,可是多台机器的呢?这个Redis的set适用于分布式全集去重。能够基于set作交集,并集,差集。查看大V共同好友等等。
sadd myset 1 // 添加元素1 smembers myset // 查看所有元素 sismember myset 2 // 判断是否包含某个元素 srem myset 1 // 删除元素1 srem myset 1 3 // 删除某些元素 scrad myset // 查看元素个数 spop myset // 随机删除一个元素 smove testset myset abc // 将testset的元素abc移到myset sinter testset myset // 求两个set交集 sunion testset myset // 求两个元素并集 sdiff testset myset //求差集 在testset中而不宅myset的元素
sorted set: 排序的set,去重还能够排序。例如写入元素带分数,自动根据分数排序。
zadd board 85 zhangsan zadd board 72 lisi zadd board 96 wangwu zadd board 63 zhaoliu zrevrange board 0 3 // 获取排名前三 rev 改降序 zrank board zhaoliu
5. Redis的过时策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现?(往Redis写入数据怎么会没了?)
缓存基于内存,内存有限,写入超过内存容量,确定有数据失效。要么设置过时时间,要么被redis干掉。
设置过时时间:
若是设置一批key只能存活1个小时,1个消息后,redis是怎么对这批数据进行删除的?
redis过时策略是:按期删除 + 惰性删除。
按期删除:随机抽取一些过时key来检查和删除。
为何?若是不少key,10W个key设了过时时间,每隔几百毫秒,去检查,会形成高CPU负载。因此实际上redis时随机抽取key来删除。
惰性删除:并非key到时间就被删除,而是过时后查询这个key时,redis查询下这key过时了,删除。不会返回值。
数据明明过时了,怎么还占用着内存?
可是实际上这仍是有问题的,若是按期删除漏掉了不少过时 key,而后你也没及时去查,也就没走惰性删除,此时会怎么样?
若是大量过时 key 堆积在内存里,致使 redis 内存块耗尽了,咋整?
解决方案是:走内存淘汰机制。
内存淘汰机制:
Redis内存淘汰机制以下:
手写一个 LRU(Least Recent Used) 算法:
利用JDK实现一个Java版LRU.
class LRUCache<K, V> extends LinkedHashMap<K, V> { private final int CACHE_SIZE; // 传递进来最多能缓存多少数据 public LRUCache(int cacheSize) { // 设置一个hashmap的初始大小 // true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部 super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。 return size() > CACHE_SIZE; } }
用到的LinkedHashMap的构造函数:
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }
参数说明:
initialCapacity: 初始容量大小,使用无参构造方法时,此值默认是16
loadFactor: 负载因子,使用无参构造方法时,此值默认是 0.75f
accessOrder: false: 基于插入顺序 true: 基于访问顺序
重点看看accessOrder的做用,使用无参构造方法时,此值默认是false。
accessOrder = true: 基于访问的顺序,get一个元素后,这个元素被加到最后(使用了LRU 最近最少被使用的调度算法)
6. 如何保证 redis 的高并发和高可用?redis 的主从复制原理能介绍一下么?redis 的哨兵原理能介绍一下么?
Redis实现高并发:
主从架构,一主多从,读写分离,主节点负责写,将数据同步到其余从节点,从节点负责读。
好处:轻松水平扩容,支撑高并发。
Redis Replication 的核心机制:
Master节点异步复制到Slave节点。
注意点:1. Master节点必须使用持久化。
2. Master的各类备份方案。如从备份中挑一份RDB去恢复Master,才能确保Master启动时,是有数据的。
Redis 主从复制的核心原理:
Slave第一次连Master, 发送PSYNC给Master, 触发full resynchronization全量复制。
Master生产快照RDB文件,同时在内存缓存最新数据(从客户端接收最新写命令),发送RDB给Slave。
Slave先把RDB写磁盘,再加载到内存。而后Master再把内存中缓存的写命令发到Slave,Slave再同步这些写命令。
若是Slave与Master因网络缘由断开后,再链接时,Master只会发缺乏的部分数据到Slave。
主从复制的断点续传:
master内存中维护一个backlog,master和slave都有一个replica offset,还有一个master run id.
master run id:是一个节点的惟一id, Host + IP有可能能变动。
若是链接断开,再连上后slave就从上次replica offset开始复制,若是没有找到,就全量复制。
无磁盘化复制:
master在内存中建立RDB,不写磁盘,发给Slave。
repl-diskless-sync yes
# 等待 5s 后再开始复制,由于要等更多 slave 从新链接过来
repl-diskless-sync-delay 5
复制的流程:
全量复制:
若是在复制期间,内存缓冲区持续消耗超过 64MB,或者一次性超过 256MB,那么中止复制,复制失败。
client-output-buffer-limit slave 256MB 64MB 60
heartbeat:
master默认每隔10秒发送一次heartbeat,slave每隔1秒发送一个 heartbeat。
Redis如何作到高可用:
redis的高可用架构,叫作failover故障转移,也能够叫作主备切换。
master在故障时,自动检测,而且将某个slave自动切换为master的过程,叫作主备切换。
Redis高可用,作主从架构,加上哨兵机制,实现主备切换。
Redis 哨兵集群实现高可用:
sentinel(哨兵)主要功能:
哨兵至少须要 3 个实例,来保证本身的健壮性。
经典的 3 节点哨兵集群:
配置 quorum=2,若是 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 能够一致认为 master 宕机了,而后选举出一个来执行故障转移,同时3个哨兵的majority是2,因此还剩下的2个哨兵运行着,就能够容许执行故障转移。
redis 哨兵主备切换的数据丢失问题:
两种状况和致使数据丢失:
1. 异步复制致使的数据丢失
有部分数据尚未来得及复制到slave,master就挂了,这部分数据就丢失。
2. 脑裂致使的数据丢失
脑裂,指某个master忽然脱离正常网络,没法与其余slave链接,但master还在运行,
此时哨兵认为master挂了,开启选举,将其余slave选为master。这个时候集群内有2个mster, 就叫脑裂。
此时,客户端会向旧master写数据,当旧master恢复成一个新的slave挂到新master时,新master并无这段时间数据,就丢失了。
数据丢失问题的解决方案:
min-slaves-to-write 1
min-slaves-max-lag 10
要求至少有 1 个 slave(min-slaves-to-write),数据复制和同步的延迟不能超过 10 秒,若是超过了,master再也不接收请求。
有了 min-slaves-max-lag 这个配置,减小异步复制数据的丢失量。
若是不能继续给指定数量slave发送数据,且slave超过10秒没有给本身发ack,则拒绝客户端写请求,最多丢10秒数据。
sdown 和 odown 转换机制:
sdown 是主观宕机,就一个哨兵若是本身以为一个 master 宕机了,那么就是主观宕机。
odown 是客观宕机,若是 quorum 数量的哨兵都以为一个 master 宕机了,那么就是客观宕机。
sdown: 若是一个哨兵 ping 一个 master,超过了 is-master-down-after-milliseconds 指定的毫秒数以后,就主观认为 master 宕机了
哨兵集群的自动发现机制:
哨兵以前经过Redis的pub/sub实现互相发现。每隔两秒,每一个哨兵会往本身监控的master+slave对应的__sentinel__:hello这个channel里发送消息,
消息包括本身的host,ip, run id还有对master的监控配置。每一个哨兵都监听本身监控的channel, 消费其余哨兵的消息,感知其余哨兵的存在。
slave 配置的自动纠正:
当一个slave成为master时,哨兵确保其他slave链接到新的master上。
slave -> master选举算法:
slave选举考虑因素:
跟master断开链接的时长,slave优先级,复制数据的offset,run id
若是一个 slave 跟 master 断开链接的时间已经超过了 down-after-milliseconds 的 10 倍,外加 master 宕机的时长,那么 slave 就被认为不适合选举为 master。
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
选举排序:
1. slave priority越低,优先级越高。默认配置中slave-priority=100
2. slave priority相同时,看replica offset,哪一个复制的数据多,offset靠后,优先级高
3. 以上相同时,选run id小的slave
quorum和majority:
哨兵主备切换时,须要quorum数量的哨兵认为sdown(主观down),才能转换为odown。
这个时候选一个哨兵来作切换,这个哨兵还须要获得majority哨兵的受权,才能正式执行切换。
configuration epoch(version):
哨兵会对一套redis的 master + slave 进行监控,有相应的监控配置。
执行切换的哨兵,会重新master(slave->master)获得一个configuration epoch,是一个version号,每次切换的version号必须惟一。
若是选举出的哨兵进行切换失败,则其余哨兵等待failover-timeout时间后,接替作切换,从新获取一个新的configuration epoch做为新的version。
configuration传播:
哨兵完成切换后,在本身本地更新生成最新master配置,而后经过hello channel同步给其余哨兵。
新的master配置是跟着新的version号,其余哨兵都是根据版本号的大小来更新本身的master配置。
Part1总结:
redis高并发:主从架构,一主多从,通常项目足够,一主写入数据,单机几W的QPS,多从用来查询数据,多个实例能够提供10W的QPS。
好比Redis主只有8G内存,其实最多只能容纳8G的数据量。若是要容量的数据量更大,就须要redis集群,用集群后能够提供每秒几十万的读写并发。
redis高可用:若是主从架构,加上哨兵集群就能够实现。一个实例宕机,自动进行主备切换。