面试官: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 多路复用能够同时监听AEREABLE和 AEWRITABLE ,若是同时达到则优先处理 AE_REABLE 时间文件事件处理器:链接应答处理器 对应 客户端要链接 redis命令请求处理器 对应 客户端写数据到 redis命令回复处理器 对应 客户端从 redis 读数据sql

流程:数据库

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

Redis 单线程模型效率高

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

  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
  2. zrevrange board 0 99 能够获取排名前 100 的用户
  3. zrank board username 能够看到用户在排行榜里的排名
    例如:
    zadd board 85 zhangsan
    zadd board 72 wangwu
    zadd board 96 lis
    zadd board 62 zhaoliu

自动排序为:96 lisi85 zhangsan72 wangwu62 zhaoliu

获取排名前 3 的用户 : zrevrange board 0 396 lisi85 zhangsan72 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 内存中的数据
  2. 脑裂,某个 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 slotredis cluster 中每一个 master 都会持有部分 slot ,当一台 master 宕机时候,会最快速度迁移 hash slot到可用的机器上(只会短暂的访问不到)走同一个 hash slot 经过 hash tag实现

Redis Cluster 核心

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

    集群元数据:包括 hashslot->node之间的映射表关系,master->slave之间的关系,故障的信息
    集群元数据集中式存储(storm),底层基于zookeeper(分布式协调中间件)集群全部元数据的维护。好处:元数据的更新和读取,时效性好,一旦变动,其余节点马上能够感知。缺点:全部元数据的更新压力所有集中在一个地方,可能会致使元数据的存储有压力。
    goosip: 好处:元数据的更新比较分散,有必定的延时,下降了压力。缺点:更新有延时,集群的一些操做会滞后。(reshared操做时configuration error)
  2. 10000 端口
    本身提供服务的端口号+ 10000 ,每隔一段时间就会往另外几个节点发送ping消息,同时其余几点接收到ping以后返回pong
  3. 交换的信息
    故障信息,节点的增长和移除, hash slot 信息
  4. gossip协议
    meet:某个节点发送 meet给新加入的节点,让新节点加入集群中,而后新节点就会开始于其余节点进行通讯
    ping:每一个节点都会频繁给其余节点发送ping,其中包含本身的状态还有本身维护的集群元数据,互相经过ping交换元数据
    ping:返回ping和meet,包含本身的状态和其余信息
    fail:某个节点判断另外一个节点fail以后,就发送 fail 给其余节点,通知其余节点,指定的节点宕机了
  5. ping消息
    ping 很频繁,且携带元数据,会加剧网络负担
    每一个节点每秒会执行 10 次 ping,每次选择 5 个最久没有通讯的其余节点
    当若是发现某个节点通讯延迟达到了 clusternodetimeout /2 ,那么当即发送 ping, 避免数据交换延迟过长,落后时间太长(2 个节点之间 10 分钟没有交换数据,整个集群处于严重的元数据不一致的状况)。
    每次ping,一个是带上本身的节点信息,还有就是带上1/10其余节点的信息,发送出去,进行数据交换
    至少包含 3 个其余节点信息,最多包含总节点-2 个其余节点的信息
  6. 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思想,须要的时候再计算,不须要的时候不计算
  2. 修改数据库成功,删除缓存失败,致使数据库是新的数据,缓存中是旧的数据
    方案:先删除缓存,再修改数据库

  3. 修改数据库还没修改完,同时又有查询请求,把旧的数据放到缓存中(高并发,每秒并发读几万,每秒只要有数据更新请求,就可能出现数据库+缓存不一致状况)
    方案:写,读路由到相同的一个内存队列(惟一标识,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 发布!

相关文章
相关标签/搜索