面试官:Redis 这些我必问

分布式缓存

缓存好处:高性能 + 高并发java

高性能(经常使用)


数据库查询耗费了800ms,其余用户对同一个数据再次查询 ,假设该数据在10分钟之内没有变化过,而且 10 分钟以内有 1000 个用户 都查询了同一数据,10 分钟以内,那 1000 个用户,每一个人查询这个数据都感受很慢 800ms
好比 :某个商品信息,在 一天以内都不会改变,可是这个商品每次查询一次都要耗费2s,一天以内被浏览 100W次
mysql 单机也就 2000qps,缓存单机轻松几万几十万qps,单机 承载并发量是 mysql 单机的几十倍。node

高并发


在中午高峰期,有 100W 个用户访问系统 A,每秒有 4000 个请求去查询数据库,数据库承载每秒 4000 个请求会宕机,加上缓存后,能够 3000 个请求走缓存 ,1000 个请求走数据库。
缓存是走内存的,内存自然能够支撑4w/s的请求,数据库(基于磁盘)通常建议并发请求不要超过 2000/smysql

缓存不良后果

  1. 缓存与数据库双写不一致
  2. 缓存雪崩
  3. 缓存穿透
  4. 缓存并发竞争

Redis 线程模型

redis 单线程 ,memcached 多线程
redis 是单线程 nio 异步线程模型react

Redis 和 Memcached 区别

  1. Redis 支持服务器端的数据操做:Redis比Memcached来讲,拥有更多的数据结构和并支持更丰富的数据操做,一般在Memcached里,你须要将数据拿到客户端来进行相似的修改再set回去。这大大增长了网络 IO 的次数和数据体积。在Redis中,这些复杂的操做一般和通常的GET/SET同样高效。因此,若是须要缓存能支持更复杂的结构和操做,那么Redis会是不错的选择
  2. 集群模式:memcached 没有原生的集群模式,须要依靠客户端来实现往集群中分片写入数据,可是 Redis 目前是原生支持 cluster模式的

Redis 单线程模型

一个线程+一个队列
redis

redis 基于 reactor 模式开发了网络事件处理器,这个处理器叫作文件事件处理器,file event handler,这个文件事件处理器是单线程的,因此redis 是单线程的模型,采用 io多路复用机制同时监听多个 socket,根据socket上的事件来选择对应的事件处理器来处理这个事件。
文件事件处理器包含:多个 socket,io多路复用程序,文件事件分派器,事件处理器(命令请求处理器、命令回复处理器、链接应答处理器)
文件事件处理器是单线程的,经过 io 多路复用机制监听多个 socket,实现高性能和线程模型简单性
被监听的 socket 准备好执行 accept,read,write,close等操做的时候,会产生对应的文件事件,调用以前关联好的时间处理器处理
多个 socket并发操做,产生不一样的文件事件,i/o多路复用会监听多个socket,将这些 socket放入一个队列中排队。事件分派器从队列中取出socket给对应事件处理器。
一个socket时间处理完后,事件分派器才能从队列中拿到下一个socket,给对应事件处理器来处理。算法

文件事件:
AE_READABLE 对应 socket变得可读(客户端对redis执行 write操做)
AE_WRITABLE 对应 socket 变得可写(客户端对 redis执行 read操做)
I/O 多路复用能够同时监听AE_REABLE和 AE_WRITABLE ,若是同时达到则优先处理 AE_REABLE 时间
文件事件处理器:
链接应答处理器 对应 客户端要链接 redis
命令请求处理器 对应 客户端写数据到 redis
命令回复处理器 对应 客户端从 redis 读数据sql

流程:数据库

  1. redis 初始化时,会将链接应答处理器跟 AE_READABLE事件关联
  2. 客户端对 redis 发起链接,产生一个 AE_READABLE 事件
  3. 链接应答处理器处理客户端 AE_READABLE 事件,建立客户端对应的 socket,同时将这个 socket的 AE_READABLE 事件和命令请求处理器关联
  4. 客户端对 redis 发起读请求,会在 socket上产生一个 AE_READABLE 事件
  5. 绑定 AE_READABLE 事件的命令请求处理器会从 socket 中读取请求相关数据,执行对应操做,当执行完毕后,将 socket的 AE_WRITABLE 事件跟命令回复处理器关联
  6. 当客户端这边准备好读取响应时,会在 socket上产生一个AE_WRITABLE事件
  7. 绑定 AE_WRITABLE 事件的命令回复处理器将准备好的响应数据写入 socket,供客户端来读取
  8. 命令回复处理器写完后,删掉 socket的 AE_WRITABLE 事件和命令回复处理器的绑定关系

Redis 单线程模型效率高

一秒钟能够处理几万个请求segmentfault

  1. 非阻塞 I/O 多路复用机制(不处理事件,只轮询请求压入队列)
  2. 纯内存操做(操做只有几微秒)
  3. 单线程反而 避免了多线程频繁上下文切换的问题

Redis 数据类型

  • string

普通的 set,get kv缓存缓存

  • hash

类型 map结构,好比一个对象(没有嵌套对象)缓存到 redis里面,而后读写缓存的时候,能够直接操做hash的字段(好比把 age 改为 21,其余的不变)
key=150
value = {

"id":150,
"name":"zhangsan",
"age":20

}

  • list

有序列表 ,元素能够重复
能够经过 list 存储一些列表型数据结构,相似粉丝列表,文章评论列表。
例如:微信大 V的粉丝,能够以 list 的格式放在 redis 里去缓存
key=某大 V value=[zhangsan,lisi,wangwu]
好比 lrange 能够从某个元素开始读取多少个元素,能够基于 list 实现分页查询功能,基于 redis实现高性能分页,相似微博下来不断分页东西。
能够搞个简单的消息队列,从 list头怼进去(lpush),list尾巴出来 (brpop)

  • set

无序集合,自动去重
须要对一些数据快速全局去重,(固然也能够基于 HashSet,可是单机)
基于 set 玩差集、并集、交集的操做。好比:2 我的的粉丝列表整一个交集,看看 2 我的的共同好友是谁?
把 2 个大 V 的粉丝都放在 2 个 set中,对 2 个 set作交集(sinter)

  • sorted set

排序的 set,去重可是能够排序,写进去的时候给一个分数,自动根据分数排序

排行榜:

  1. 将每一个用户以及其对应的分数写入进去

zadd board score username

  1. zrevrange board 0 99 能够获取排名前 100 的用户
  2. zrank board username 能够看到用户在排行榜里的排名

例如:
zadd board 85 zhangsan
zadd board 72 wangwu
zadd board 96 lis
zadd board 62 zhaoliu

自动排序为:
96 lisi
85 zhangsan
72 wangwu
62 zhaoliu

获取排名前 3 的用户 : zrevrange board 0 3
96 lisi
85 zhangsan
72 wangwu

查看zhaoliu的排行 :zrank board zhaoliu 返回 4

Redis 过时策略

内存是宝贵的,磁盘是廉价的
给key设置过时时间后,redis对这批key是按期删除+惰性删除
按期删除:
redis 默认每隔 100ms随机抽取一些设置了过时时间的 key,检查其是否过时了,若是过时就删除。
注意:redis是每隔100ms随机抽取一些 key来检查和删除,而不是遍历全部的设置过时时间的key(不然CPU 负载会很高,消耗在检查过时 key 上)
惰性删除:
获取某个key的时候, redis 会检查一下,这个key若是设置了过时时间那么是否过时,若是过时了则删除。
若是按期删除漏掉了许多过时key,而后你也没及时去查,也没走惰性删除,若是大量过时的key堆积在内存里,致使 redis 内存块耗尽,则走内存淘汰机制。

内存淘汰策略:

  1. noeviction:当内存不足以容纳新写入数据时,新写入操做直接报错(没人用)
  2. allkeys-lru: 当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(最经常使用)
  3. allkeys-random: 当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,(没人用)
  4. volatile-lru:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,移除最近最少使用的key(不合适)
  5. volatile-ttl:当内存不足以容纳新写入数据时,在设置了过时时间的键空间中,有更早过时时间的 key 优先移除(不合适)

LRU 算法:

package com.mousycoder.mycode;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @version 1.0
 * @author: mousycoder
 * @date: 2019/10/31 17:55
 */
public class LRUCache<K,V> extends LinkedHashMap<K,V> {

    private final int CACHE_SIZE;

    public LRUCache( int cacheSize) {
        super((int)Math.ceil(cacheSize / 0.75) + 1 ,0.75f,true);
        this.CACHE_SIZE = cacheSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > CACHE_SIZE;
    }

    public static void main(String[] args) {
        LRUCache<Integer,Integer> lruCache = new LRUCache<>(10);
        for (int i = 0; i < 15; i++) {
            lruCache.put(i,i);
        }

        Integer integer1 = lruCache.get(0);
        for (Integer integer : lruCache.keySet()) {
            System.out.println(integer);
        }

    }
}

Redis 高并发和高可用

缓存架构(多级缓存架构、热点缓存)
redis 高并发瓶颈在单机,读写分离,通常是支撑读高并发,写请求少,也就 一秒一两千,大量请求读,一秒钟二十万次。

主从架构


一主多从,主负责写,将数据同步复制到其余 slave节点,从节点负责读,全部读的请求所有走从节点。主要是解决读高并发。、
主从架构->读写分离->支撑10W+读QPS架构

Redis Replication


master->slave 复制,是异步的
核心机制:

  1. redis 采用异步方式复制数据到 slave 节点
  2. 一个 master node是能够配置多个 slave node的
  3. slave node也能够链接其余的 slave node
  4. slave node 作复制的时候,是不会 block master node的正常工做
  5. slave node 在作复制的时候,也不会 block对本身的查询操做,它会用旧的数据集来提供服务。可是复制完成时,须要删除旧数据集,加载新的数据集,这个时候就会暂停对外服务了。
  6. slave node 主要用来进行横向扩容,作读写分离,扩容 slave node 能够提升读的吞吐量

master持久化对主从架构的意义:
若是开启了主从架构,必定要开启 master node的持久化,否则 master宕机重启数据是空的,一经复制,slave的数据也丢了

主从复制原理:

第一次启动或者断开重连状况:

  1. 当启动一个 slave node的时候,它会发送一个 PSYNC 命令给 master node
  2. master 会触发一次 full resynchronization (若是不是第一次链接,master 只会复制给 slave 部分缺乏的数据,从backlog里找)
  3. master会启动一个后台线程,开始生成一份 RDB 快照( bgsave,也能够直接在内存中建立),同时将从客户端收到的全部写命令缓存在内存中。RDB 文件生成完毕以后,master会将这个RDB发送给slave,slave会先写入本地磁盘,而后再从本地磁盘加载到内存中。而后 master会将内存中缓存的写命令发送给 slave,slave也会同步这些数据(slave若是跟 master网络故障,断开链接,会自动重连,master若是发现有多个 slave 来从新链接,仅仅只会启动一个 RDB save 操做,用一份数据服务全部 slave node)

正常状况下:
master 来一条数据,就异步给 slave

Redis高可用性

整年 99.99%的时间,都是出于可用的状态,那么就能够称为高可用性
redis 高可用架构叫故障转移,failover,也能够叫作主备切换,切换的时间不可用,可是总体高可用。
sentinal node(哨兵)

Sentinal

做用:

  1. 集群监控,负责监控 redis master 和 slave进程是否正常
  2. 消息通知,若是某个 redis 实例有故障,那么哨兵负责发送消息做为报警通知给管理员
  3. 故障转移,若是 master 挂掉,会自动转移到 slave
  4. 配置中心,若是故障转移了,通知 client 客户端新的 master地址

两节点哨兵集群


quorum = 1 (表明哨兵最低个数能够尝试故障转移,选举执行的哨兵)
master 宕机,只有 S2 存活,由于 quorum =1 能够尝试故障转移,可是没达到 majority =2 (最低容许执行故障转移的哨兵存活数)的标准,没法执行故障转移

三节点哨兵集群(经典)


若是 M1 宕机了,S2,S3 认为 master宕机,选举一个执行故障转移,由于 3 个哨兵的 majority = 2,因此能够执行故障转移

Redis 主从 + 哨兵

丢数据:

  1. master内存中数据异步同步到 slave master 就挂掉了,丢掉了 master 内存中的数据

  1. 脑裂,某个 master 所在机器忽然脱离了正常的网络,其余 slave机器不能链接,可是实际上 master还在运行,哨兵认为 master 宕机,选举 slave为master,此时集群里有 2 个 master, client还没来得及切换到新的master,还继续写在旧的 master上,数据丢了,此时旧的 master再次恢复,被被做为一个 slave 挂到新的 master 上,本身的数据被清空 (脑裂,大脑一分为 2,同时指挥同一我的)

解决方案:

  1. min-slaves-max-lag 10 (至少一个 slave同步的延迟不能超过 10s) 减小异步复制的数据丢失,发现slave复制数据和 ack延时过长,拒绝写入,减小同步数据损失。让client作降级写到本地磁盘里和限流,或者先暂存到消息队列,而后从新发回 master
  2. min-slaves-to-write 1 减小脑裂带来的数据丢失,最多损失 10 s数据,假设master 不能继续给 slave发送数据,而且 slave 10s没给本身的 ack消息,直接拒绝客户端写请求,同时 client作降写到本地磁盘、限流,或者先暂存到消息队列,而后从新发回 master

哨兵

sdown 主观宕机,哨兵以为一个 master 宕机(ping 超过了 is-master-down-after-milliseconds毫秒数)
odown 客观宕机,quorum数量的哨兵都以为 master宕机
哨兵互相感知经过 redis的 pub/sub系统,每隔 2 秒往同一个 channel里发消息(本身的 host,ip,runid),其余哨兵能够消费这个消息
以及同步交换master的监控信息。
哨兵确保其余slave修改master信息为新选举的master
当一个 master被认为 odown && marjority哨兵都赞成,那么某个哨兵会执行主备切换,选举一个slave成为master(考虑 1. 跟master断开链接的时长 2. slave 优先级 3.复制 offset 4. runid)
选举算法:

  1. 若是slave跟master断开链接已经超过 down-after-milliseconds * 10 + master宕机时间,则放弃
  2. 按 slave 优先级排序 ,slave-priority 越小越靠前
  3. replica offset ,哪一个slave复制越多的数据,越靠前
  4. runid 越小,越靠前

quorum 数量哨兵认为odown->选举一个哨兵切换->得到 majority哨兵的受权(quorum < majority 须要 majority个哨兵受权,quorum >= majority 须要 quorum 哨兵受权)
第一个选举出来的哨兵切换失败了,其余哨兵等待 failover-time以后,从新拿confiuration epoch作为新的version 切换,保证拿到最新配置,用于 configuration传播(经过 pu/sub消息机制,其余哨兵对比 version 新旧更新 master配置)

Redis 优化方案

高并发:主从架构
高容量:Redis集群,支持每秒几十万的读写并发
高可用:主从+哨兵

Redis 持久化

持久化的意义在于故障恢复数据备份(到其余服务器)+故障恢复(遇到灾难,机房断电,电缆被切)

  • RDB 对 Redis 中的数据执行周期性的持久化。
  • AOF 机制,每条写命令做为日志,以 append-only模式写入一个日志文件总,在 redis重启的时候,能够经过回放AOF日志中的写入指令来从新构建整个数据集

AOF 只有一个,Redis 中的数据是有必定限量的,内存大小是必定的,AOF 是存放写命令的,当大到必定的时候,AOF 作 rewrite 操做,就会基于当时 redis 内存中的数据,来从新构造一个更小的 AOF 文件,而后将旧的膨胀很大的文件给删掉,AOF 文件一直会被限制在和Redis内存中同样的数据。AOF同步间隔比 RDB 小,数据更完整

RDB

优势:

  • RDB 会生成多个数据文件,每一个数据文件都表明了某一个时刻中 redis 的数据,这种多个数据文件的方式,很是适合作冷备,能够将这种完整的数据文件发送到一些远程的安全存储上去,RDB 作冷备,生成多个文件,每一个文件都表明某一个时刻的完整的数据快照,AOF 也能够作冷备,只有一个文件,每隔必定时间去 copy一份这个文件出来。 RDB 作冷备,由Redis控制固定时长去生成快照文件,比较方便。AOF,须要本身写脚本定时控制。
  • RDB 对 redis对外提供的读写服务,影响很是小,可让 redis 保持高性能,由于 redis 主进程只须要 fork一个子进程,让子进程执行磁盘 IO 操做来进行 RDB 持久化
  • 相对于 AOF 持久化机制来讲,直接基于 RDB 数据文件来重启和恢复 redis 进程,更加快速

缺点:

  • 若是想要在 redis故障时,尽量少丢数据,那么 RDB 没有 AOF 好,通常 RDB 数据快照,都是间隔 5 分钟,或者更长的时候生成一次,这个时候就得接受一旦 redis 进程宕机,那么会丢失最近 5 分钟数据
  • RDB 每次在 fork子进程来执行 RDB 快早数据文件生成的时候,若是数据文件特别大,可能会致使对客户端提供的服务暂停数毫秒,甚至数秒(RDB 生成间隔不要太长)

AOF 存放的指令日志,数据恢复的时候,须要回放执行全部指令日志,RDB 就是一份数据文件,直接加载到内存中。

AOF

优势:

  1. 更好保护数据不丢失,后台线程 fsync 操做,最多丢失一秒钟数据,保证 os cache中的数据写入磁盘中
  2. AOF 用 append-only 模式,没有磁盘寻址开销,写入性能很是高,文件不容易损坏。
  3. AOF 日志过大的时候,后台 rewrite log时候,老的日志文件照常写入,新的merge后的日志文件 ready的时候,再交换新老日志文件
  4. 适合灾难性恢复,某人不当心 flushall清空全部数据,只要后台 rewrite还没发生,那么能够马上拷贝 AOF 文件,将最后一条 flushall命令给删了,而后再将该 AOF 文件放回去,能够经过恢复机制,自动恢复全部数据

缺点:

  1. AOF 日志文件比 RDB 数据快照文件大
  2. 下降 Redis的写 QPS
  3. AOF 复杂,Bug多
  4. 数据恢复比较慢

最佳方案

AOF 来保证数据不丢失,RDB 作不一样时间的冷备

Redis Cluster


支持 N 个 Redis master node,每一个 master node挂载多个 slave node
多master + 读写分离 + 高可用

数据量不多,高并发 -> replication + sentinal 集群
海量数据 + 高并发 + 高可用 -> redis cluster

分布式算法

hash算法->一致性 hash 算法-> redis cluster->hash slot算法

redis cluster :自动对数据进行分片,每一个 master 上放一部分数据,提供内置的高可用支持,部分master不可用时,仍是能够继续工做
cluster bus 经过 16379进行通讯,故障检测,配置更新,故障转移受权,另一种二进制协议,主要用于节点间进行高效数据交换,占用更少的网络带宽和处理时间

hash算法

key进行hash,而后对节点数量取模,最大问题只有任意一个 master 宕机,大量数据就要根据新的节点数取模,会致使大量缓存失效。

一致性 hash 算法


key进行hash,对应圆环上一个点,顺时针寻找距离最近的一个点。保证任何一个 master 宕机,只受 master 宕机那台影响,其余节点不受影响,此时会瞬间去查数据库。
缓存热点问题:
可能集中在某个 hash区间内的值特别多,那么会致使大量的数据都涌入同一个 master 内,形成 master的热点问题,性能出现瓶颈。
解决方法:
给每一个 master 都作了均匀分布的虚拟节点,这样每一个区间内大量数据都会均匀的分布到不一样节点内,而不是顺时针所有涌入到同一个节点中。

Hash Slot算法

redis cluster 有固定 16384 个 hash slot,对每一个key计算 CRC16 值,而后对16384取模,能够获取 key对应的 hash slot
redis cluster 中每一个 master 都会持有部分 slot ,当一台 master 宕机时候,会最快速度迁移 hash slot到可用的机器上(只会短暂的访问不到)
走同一个 hash slot 经过 hash tag实现

Redis Cluster 核心

  1. 基础通讯

经过 gossip 协议通讯(小道留言,全部节点都持有一份元数据,不一样的节点若是出现了元数据的变动,就不断将元数据发送给其余节点,让其余节点也进行元数据的变动)

集群元数据:包括 hashslot->node之间的映射表关系,master->slave之间的关系,故障的信息
集群元数据集中式存储(storm),底层基于zookeeper(分布式协调中间件)集群全部元数据的维护。好处:元数据的更新和读取,时效性好,一旦变动,其余节点马上能够感知。缺点:全部元数据的更新压力所有集中在一个地方,可能会致使元数据的存储有压力。
goosip: 好处:元数据的更新比较分散,有必定的延时,下降了压力。缺点:更新有延时,集群的一些操做会滞后。(reshared操做时configuration error)

  1. 10000 端口

本身提供服务的端口号+ 10000 ,每隔一段时间就会往另外几个节点发送ping消息,同时其余几点接收到ping以后返回pong

  1. 交换的信息

故障信息,节点的增长和移除, hash slot 信息

  1. gossip协议

meet:某个节点发送 meet给新加入的节点,让新节点加入集群中,而后新节点就会开始于其余节点进行通讯
ping:每一个节点都会频繁给其余节点发送ping,其中包含本身的状态还有本身维护的集群元数据,互相经过ping交换元数据
ping:返回ping和meet,包含本身的状态和其余信息
fail:某个节点判断另外一个节点fail以后,就发送 fail 给其余节点,通知其余节点,指定的节点宕机了

  1. ping消息

ping 很频繁,且携带元数据,会加剧网络负担
每一个节点每秒会执行 10 次 ping,每次选择 5 个最久没有通讯的其余节点
当若是发现某个节点通讯延迟达到了 cluster_node_timeout /2 ,那么当即发送 ping, 避免数据交换延迟过长,落后时间太长(2 个节点之间 10 分钟没有交换数据,整个集群处于严重的元数据不一致的状况)。
每次ping,一个是带上本身的节点信息,还有就是带上1/10其余节点的信息,发送出去,进行数据交换
至少包含 3 个其余节点信息,最多包含总节点-2 个其余节点的信息

  1. JRedis原理
  • 请求重定向

客户端发送到任意一个redis实例发送命令,每一个redis实例接受到命令后,都会计算key对应的hash slot,若是在本地就本地处理,不然返回moved给客户端,让客户端进行重定向 (redis-cli -c)

  • hash slot

经过tag指定key对应的slot,同一个 tag 下的 key,都会在一个 hash slot中,好比 set key1:{100} 和 set key2:{100}

  • smart jedis

本地维护一份hashslot->node的映射表。
JedisCluster 初始化的时候,随机选择一个 node,初始化 hashslot->node 映射表,同时为每一个节点建立一个JedisPool链接池,每次基于JedisCluster执行操做,首先JedisCluster都会在本地计算key的hashslot,而后再本地映射表中找到对应的节点,若是发现对应的节点返回moved,那么利用该节点的元数据,更新 hashslot->node映射表(重试超过 5 次报错)

  • hashslot迁移和ask重定向

hash slot正在迁移,那么会返回ask 重定向给jedis,jedis 接受到ask重定向以后,,会重定向到目标节点去执行

  • 高可用性和主备切换原理

判断节点宕机:
若是一个节点认为另一个节点宕机了, 就是pfail,主观宕机
若是多个节点都认为另一个节点宕机了,那么就是fail,客观宕机(跟哨兵原理同样)
在cluster-node-timeout内,某个节点一直没有返回 pong,那么就被认为是 pfail
若是一个节点认为某个节点pfail了,那么会在gossip消息中,ping给其余节点,若是超过半数的节点认为pfail了,那么就会变成fail。
从节点过滤:
对宕机的 mster node ,从其全部的 slave node中,选择一个切换成 master node
检查每一个 slave node与master node断开链接的时间,若是超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没资格切换成 master(和哨兵一致)
从节点选举:
每一个从节点,根据本身对 master 复制数据的 offset,设置一个选举时间,offset越大(复制数据越多)的从节点,选举时间越靠前,全部的 master node 开始投票,给要进行选举的 slave进行投票,若是大部分 master node(N/2 +1) 都投票给某个从节点,那么选举经过,从节点执行主备切换,从节点切换成主节点
总结:和哨兵很像,直接集成了 replication 和 sentinal

缓存雪崩

方案:
事前:保证 redis 集群高可用性 (主从+哨兵或 redis cluster),避免全盘崩溃
事中:本地 ehcache 缓存 + hystrix 限流(保护数据库) & 降级,避免 MySQL被打死
过后: redis持久化,快速恢复缓存数据,继续分流高并发请求

限制组件每秒就 2000 个请求经过限流组件进入数据库,剩余的 3000 个请求走降级,返回一些默认 的值,或者友情提示
好处 :

  1. 数据库绝对不会死,确保了每秒只会过去 2000 个请求
  2. 只要数据库不死,对于用户来讲 2/5的请求能够被处理
  3. 系统没死,用户多点几回可能就刷出来了

缓存穿透


4000 个请求黑客攻击请求数据库里没有的数据
解决方案:把黑客查数据库中不存在的数据的值,写到缓存中,好比: set -999 UNKNOWN

缓存与数据库双写一致性

  1. cache aside pattern


读的时候,先读缓存,缓存没有,就读数据库,而后取出数据后放入缓存,同时返回响应
更新的时候,删除缓存,更新数据库
为何不更新缓存:
更新缓存代价过高(更新 20 次,只读 1 次),lazy思想,须要的时候再计算,不须要的时候不计算

  1. 修改数据库成功,删除缓存失败,致使数据库是新的数据,缓存中是旧的数据

方案:先删除缓存,再修改数据库

  1. 修改数据库还没修改完,同时又有查询请求,把旧的数据放到缓存中(高并发,每秒并发读几万,每秒只要有数据更新请求,就可能出现数据库+缓存不一致状况)

方案:写,读路由到相同的一个内存队列(惟一标识,hash,取模)里,更新和读操做进行串行化(后台线程异步执行队列串行化操做),(队列里只放一个更新查询操做便可,多余的过滤掉,内存队列里没有该数据更新操做,直接返回 )有该数据更新操做则轮询取缓存值,超时取不到缓存值,直接取一次数据库的旧值

TP 99 意思是99%的请求能够在200ms内返回
注意点:多个商品的更新操做都积压在一个队列里面(太多操做积压只能增长机器),致使读请求发生大量的超时,致使大量的读请求走数据库
一秒 500 写操做,每200ms,100 个写操做,20 个内存队列,每一个队列积压 5 个写操做,通常在20ms完成

Redis 并发竞争问题


方案:分布式锁 + 时间戳比较

Redis 集群部署架构

10台机器,5 主 5 从,每一个节点QPS 5W ,一共 25W QPS(Redis cluster 32G + 8 核 ,Redis 进程不超过 10G)总内存 50g,每条数据10kb,10W 条数据1g,200W 条数据 20G,占用总内存不到50%,目前高峰期 3500 QPS

本文由博客一文多发平台 OpenWrite 发布!
相关文章
相关标签/搜索