## 算法 #### 失效算法 redis缓存失效算法应用案例: noeviction: 不删除策略,达到最大内存限制时,如果需要更多内存,直接返回错误信息。 allkeys-lru: 对所有key,悠闲删除最近最少使用的key allkeys-random: 对所有key,随机删除一部分。 volatile-lru: 只限于设置了exprire的key,悠闲删除最近最少使用的key。 volatile-random: 只限于设置了expire的key,随机删除一部分 valatile-ttl: 只限于设置了expire的key,悠闲删除剩余时候TTL短的key。 ##### 先来先淘汰 FIFO first In first Out 先来先淘汰,这种算法在每一次新数据插入时候,如果队列已满则将最早插入的数据移除。 ##### 最久未用淘汰 LRU Least Recently Used,淘汰最后一次使用时间最久远的数据。FIFO非常粗暴,不管有没有用到,直接踢掉时间 久的元素,而LRU认为,最近频繁使用过的数据,将来也很大成都会被频繁使用到,故而淘汰哪些慵懒的数据。Lin kedHashMap,数组,链表均可以实现LRU,下面仍以链表为例:新加入的数据放在头部,最近访问的,也移动到头 部,空间满时,将尾部数据删除。 ##### 最近最少使用 LFU Least Frequently Used,最近使用最少。要淘汰最近一段时间内,使用次数最少的值。可以认为比LRU多了一 重判断。LFU需要时间和次数两个纬度的参考指标,需要注意的是,两个纬度可能涉及到同一时间段内,访问次数相 同的情况下,就必须内置一个计数器和一个队列,计数器算数,队列防止相同的访问时间, #### 限流算法 限流是对系统的一种保护措施,及限制流量请求的频率(每秒处理多少个请求)。一般来说,当请求流量超过系统 的瓶颈,则丢弃多余的请求流量。保证系统的可用性。即要么不放进来,放进来久保证提供服务。 ##### 计数器 计数器采用简单的计数操作,到一段时间节点后自动清零。 ##### 漏桶算法 漏桶算法将请求缓存在桶中,服务流匀速处理。超出桶容量的部分丢弃。漏桶算法主要用于保护内部的处理业务, 保障其稳定有节奏的处理请求,但是无法根据流量的波动弹性调整响应能力。现实中,类似容纳人数有限的服务大厅 开启了固定的服务窗口 应用: nginx中的限流是漏桶算法的典型应用,配置案例如下 http { # $binary_remote_addr 表示通过remote_addr这个表示来做key,也是限制同一客户端ip地址 # zone=one:10m 表示生成一个大小为10M,名字为one的内存区域,用来存储访问的频次信息 # rate=1r/s 表示允许相同表示的客户端每秒1次访问 limit_req_zone $binary_remote_addr zone=noe:10m rate=1r/s; server { location /limited/ { # zone=one 与上面的limit_req_zone 里面的name对应 # burst=5 缓冲区,超过访问频次限制的请求可以先放到缓冲区,类似代码中的队列长度 # nodelay 如果设置,访问超过频次而且缓冲区也满了的时候就会返回503,如果没有设置 则所有请求排队,类似代码中的put还是offer limit_req zone=noe burst=5 nodelay; } } } ##### 令牌桶 令牌桶是漏桶算法的一种升级,他不但可以将流量做进一步的限制,还可以解决漏桶中无法弹性伸缩处理请求的问题。 实现类中,类似服务大厅门口防止的门禁卡发放。发放是均速的,请求较少时,令牌可以缓存起来,供流量爆发时一 次性批量获取使用。二内部服务窗口不设限。 应用: springcloud中的gateway可以使用令牌桶实现限流控制,案例如下: cloud: gatewat: routes: - id: limit_route uri: http://localhost:8080/test filters: - name: RequestRateLimiter args: # 限流的key,ipKeyResolver为spring中托管的bean,需要扩展keyResover接口 key-resolver: '#{@ipResolver}' # 令牌桶每秒填充平均速度,相当于代码中的发放频率 redis-rate-limiter.replenisRate: 1 # 令牌总容量 redis-rate-limiter.burstCapacity: 3 ##### 滑动窗口 可以理解为细分之后的计数器,计数器粗暴的限定1分钟之内的访问次数,而滑动窗口限流将1分钟拆为多个段,不但 要求整个一分钟内请求小于上限,而且要求每个片段请求数也要小于上限,相当于将原来的计数周期做了多个片段拆分 ,更为精细。 #### 调度算法 调度算法常见于操作系统中,因为系统资源有限,当有多个进程(或者多个进程发出请求)要使用这些资源时,就 必须按照一定的原则选择进程(请求)来占用资源。这就是所谓的调度 ##### 先来先服务 FCFS 按照服务提交的申请顺序,依次执行。讲究先来后到。 优缺点: 多用于cpu密集型任务场景,对io密集型不利。 时间相对均衡的业务可以排队处理。 如果业务需要依赖大量的外部因素,执行时间片长短不一,FCFS算法不利于任务整体的处理进度。 ##### 短作业优先 SJF 执行时间段的优先得到资源,即执行前申报一个我需要占据cpu的时间,根据时间长度,短的优先执行被调度。 优缺点: 适用于任务时间差较大的场景。 解决了FCFS整体处理时间长的问题,降低了平均等待时间,提高了系统的吞吐量。 未考虑作业的紧迫成都,因而不能保证紧迫性作业的及时处理。 对长作业不利,可能等待很久得不到执行。 时间基于预估和申报,主观性因素在内,无法做到100%准确。 ##### 时间片轮转 RR 时间片逐个扫描轮询,轮到谁谁执行。大家公平裁决来者有份,谁也别插队。向棋牌游戏的发牌操作,做到了时间和 机会上的平均性。 优缺点: 做到了机会的相对平均,不会因为某个任务执行时间超长而永远得不到执行。 缺乏任务主次性处理,重要的任务无法得到优先执行,必须等到时间片轮到自己。 ##### 优先级调度 HPF 进程调度每次将处理机分配给具有告优先级的就绪进程。 #### 定时算法与应用 ##### 最小堆 Timer是java中最典型的基于优先级队列+最小堆实现的定时器,内部维护一个存放定时任务的优先级队列,该优先级 队列使用了最小堆排序。当我们调用schedule方法的时候,一个新的任务被加入queue,堆重排,始终保持堆顶是 执行时间最小(即马上要执行)的。同事,内部相当于起了一个线程不断扫描队列,从队列中一次获取堆顶元素执行, 任务得到调度。 ##### 时间轮 是更为常见的定时调度算法,各种操作系统的定时任务调度,linux crontab,基础java的通讯框架netty等。 时间轮是一个头尾详解的环状数组,数组的个数即是插槽数,每个插槽中可以放置任务,以一天为例,将任务执行时间 %12,根据得到的数值,放置在时间轮上,小时指针沿着轮盘扫描,扫倒的点取出任务执行。 #### 均衡负载算法 ##### 轮询 RoundRobin 轮询即排好队,一个接一个。前面调度算法中用到的时间片轮询,就说一种典型的轮询。但是前面使用数组的下标 轮询实现。 优缺点: 实现简单,机器列表可以自由加减 无法针对节点做偏向性定制 ##### 随机 Random 从可用的服务列表随机一个提供响应 ##### 源地址哈希 Hash 对当前访问的ip做一个hash值,相同的key可以被路由到同一台机器去。 ##### 加权轮询 WRR ##### 加权随机 WR ##### 最小连接数 LC 最小连接数,统计当前机器的连接数,选最少的响应新的请求。前面的算法是站在请求的纬度去考虑。而最小连接数 是在机器的纬度去考虑。 #### 一致性hash以及应用 ##### 背景 负载均衡策略中,我们提到过源地址hash算法,让某些请求固定在对应的服务器上。这样可以解决会话信息保留 的问题。 同时,标准的hash,如果机器节点发生变更。那么请求会被重新hash,打破原始的设计初衷,使用一致性hash可以 解决。 ##### 原理 首先求出各个服务器的哈希值,并将其配置到0-(2的32平方)的圆上。 然后采用同样的方法求出存储数据的键的哈希值,也映射上。 从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。 如果最大值仍然找不到,就取第一个。 ##### 特性 单调性(Monotonicity): 单调性是指如果已经有一些请求通过哈希分配到了相应的服务器处理,又有新的服务器 加入到系统中的时候,应保证原有的请求可以被映射到原有或者新的服务器中去,而不会被映射到原来的其他服务器上。 分散性(Spread):分布式环境中,客户端请求时可能只知道其中一部分服务器,那么两个客户端看到不同的部分, 并且认为自己看到的都是完整的hash环,那么问题来了,相同的key可能被路由到不同的服务器上去。 平衡性(Balance):平衡性是只客户端hash后的请求应该能够分散到不同的服务器上。一致性的hash可以做到 尽量分散,但是不能保证每个服务器处理的请求数量完全相同。这种偏差成为hash倾斜。如果节点的分布式算法设计 不合理,那么平衡性就会收到很大的影响。 ##### 实现 ##### 验证 #### 典型业务场景应用 ##### 敏感词过滤 ##### 最优商品topk