filebeat是beats的一个组件。

GMP相关

同一时刻:一个 M 只能绑定一个 P,一个 P 也只能被一个 M 使用;
生命周期上:M 和 P 不是永久一对一绑定,会因阻塞、抢占、空闲切换动态解绑、重新配对;
核心约束:M 执行 G 的前提是临时持有 P,而非终身绑定。

slice 不是线程安全的

多个 goroutine 并发读写同一个 slice,一定会出现数据竞争、结果异常、panic。
这首歌好听,推荐一下啊【ナースロボ_タイプT】あの会話の覚書【漣音】

redis 排行榜应该用哪个数据结构

ZSet【有序集合】,天然适配排序、分值、去重、分页、排名查询,是 Redis 做榜单的最优解。
元素唯一:成员(member)不可重复,天然防重复上榜
按分值排序:根据 score 自动升 / 降序排列,对应分数、热度、时间
高效排名查询:查询名次、区间分页、查某人排名时间复杂度极低
支持分数更新:直接修改 score,集合自动重排

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 添加/更新成员分数(上榜/加分)
ZADD rank 99 "user1" 88 "user2"

# 2. 降序分页查榜单(前10名,从0开始)
ZREVRANGE rank 0 9 WITHSCORES

# 3. 查询某个用户当前排名(降序名次)
ZREVRANK rank "user1"

# 4. 增减分数(点赞、积分累加)
ZINCRBY rank 10 "user1"

# 5. 获取榜单总人数
ZCARD rank

# 6. 删除下线用户
ZREM rank "user1"

LRU 详解(最近最少使用)

一、核心概念

LRU (Least Recently Used)最近最少使用缓存淘汰算法。
规则:当缓存满了,优先删掉最久没被使用的数据。


二、实现思路(经典组合)

主流方案:哈希表 + 双向链表

  1. 双向链表
    • 头部:最近使用的数据
    • 尾部:最久未使用(优先淘汰)
    • 优势:头尾增删、中间节点移动都是 $O(1)$
  2. 哈希表(HashMap)
    • key → 链表节点
    • 优势:查找节点 $O(1)$

整体时间复杂度:查、增、删、更新 均为 $O(1)$


三、操作流程

  1. 访问数据
    • 存在:移到链表头部
    • 不存在:新增节点放头部
  2. 缓存已满
    • 删除链表尾部节点 + 哈希表对应记录

四、Go 极简实现(可直接运行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package main

import (
"container/list"
"fmt"
)

type LRUCache struct {
cap int
data map[int]*list.Element
ll *list.List
}

// 链表存储的键值对
type entry struct {
key, val int
}

func NewLRU(cap int) *LRUCache {
return &LRUCache{
cap: cap,
data: make(map[int]*list.Element),
ll: list.New(),
}
}

// Get 获取值
func (l *LRUCache) Get(key int) int {
ele, ok := l.data[key]
if !ok {
return -1
}
// 移到队首(最近使用)
l.ll.MoveToFront(ele)
return ele.Value.(*entry).val
}

// Put 插入/更新
func (l *LRUCache) Put(key, val int) {
// 已存在:更新值 + 移队首
if ele, ok := l.data[key]; ok {
ele.Value.(*entry).val = val
l.ll.MoveToFront(ele)
return
}

// 不存在:新建节点
newEle := l.ll.PushFront(&entry{key, val})
l.data[key] = newEle

// 超过容量,淘汰尾部
if l.ll.Len() > l.cap {
delEle := l.ll.Back()
l.ll.Remove(delEle)
delete(l.data, delEle.Value.(*entry).key)
}
}

func main() {
lru := NewLRU(2)
lru.Put(1, 1)
lru.Put(2, 2)
fmt.Println(lru.Get(1)) // 1
lru.Put(3, 3) // 淘汰 2
fmt.Println(lru.Get(2)) // -1
}

五、常见变种 & 对比

  1. LRU-K:访问 K 次才进入热点队列,抵御突发冷热数据
  2. LFU:按访问频次淘汰(区别:LRU 看时间,LFU 看次数)
  3. Redis 近似 LRU:不严格遍历链表,随机采样淘汰,性能更高

总结

1. 哈希表(map)

只负责:快速查找
特点:无序,不记录访问顺序
能力:给一个 key,
O(1)
找到对应的链表节点

2. 双向链表

只负责:维护访问时序(有序)
特点:严格有序
链表头部:最近刚使用
链表尾部:最久没使用(要被淘汰)

只有 len(s) == cap(s) 时,再执行 append 才会触发扩容。

len < cap:直接在原底层数组末尾写,不扩容。
len == cap:再 append → 必须扩容(新数组 + 拷贝)。
空切片 s := []int{}:len=0, cap=0,第一次 append 就扩容(分配 1 个元素空间)。

预防缓存击穿

【查询不存在的数据,请求直接越过缓存,全部打到数据库。】
空值缓存:查询结果为空,也把 null/空对象 写入缓存,设置短过期时间。
布隆过滤器:前置拦截不存在的 key,请求根本不进缓存 / DB。
接口层参数校验:非法 ID、非法参数直接拦截。

预防缓存穿透

【热点 Key 突然失效,大量并发请求瞬间全部打到数据库。】
互斥锁(分布式锁):同一 key 只放一个请求去查 DB、更新缓存,其他请求等待。
永不过期:热点 Key 逻辑过期(不删缓存,后台异步更新)。
缓存过期时间加随机值:打散过期时间,避免集体失效。

预防缓存雪崩

【大量缓存 key 集体失效 / 缓存服务宕机,所有请求直接涌向 DB。】
过期时间加随机偏移,打散失效时间;
缓存集群高可用:主从、哨兵、集群,避免单点故障;
服务层限流、降级、熔断:兜底保护 DB;
多级缓存:本地缓存 + 分布式缓存,层层防护;
Redis 持久化:宕机后快速恢复数据。

SingleFlight(请求合并/单飞模式)

一、是什么

SingleFlight 是 Go 官方扩展库(golang.org/x/sync/singleflight)提供的并发原语,核心作用:同一 key 的并发请求只执行 1 次,其余等待并共享结果,本质是「请求合并」而非「互斥锁」。

二、核心原理(极简版)

  1. 结构体Group 内含 mu(互斥锁)+ m(map:key→正在执行的call)。
  2. 执行流程
    • N 个 goroutine 同时调用 sg.Do(key, func)
    • 第 1 个抢到锁的:发现 key 不存在 → 执行 func(查DB/调API)。
    • 其他 N-1 个:发现 key 已存在 → 阻塞等待,不执行 func。
    • 执行完的 goroutine:把结果/错误写入,唤醒所有等待者,共享同一份结果。
  3. 返回值(val interface{}, err error, shared bool)shared=true 表示结果是共享的(非本 goroutine 执行)。

三、核心作用(面试必背)

1. 防缓存击穿(最常用)

  • 场景:热点 key 过期 → 大量请求同时打 DB。
  • SingleFlight:同一 key 只放行 1 个请求查 DB,其他等待 → DB 压力从 N 降为 1

2. 减少重复调用

  • 第三方 API(按次计费)、复杂计算、配置拉取、元数据查询等,相同输入→相同输出的高成本操作。

3. 削峰控并发

  • 瞬间高并发接口,合并请求,保护下游服务。

四、Go 代码示例(直接能用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
"fmt"
"sync"
"time"

"golang.org/x/sync/singleflight"
)

// 模拟:查DB(耗时1秒)
func queryDB(key string) (string, error) {
time.Sleep(1 * time.Second) // 模拟慢查询
fmt.Println("真实查询DB:", key)
return "data:" + key, nil
}

func main() {
var sg singleflight.Group
var wg sync.WaitGroup

// 模拟10个并发请求同一个key
for i := 0; i < 10; i++ {
wg.Add(1)
go func(idx int) {
defer wg.Done()
// 关键:用同一个key,合并请求
val, err, shared := sg.Do("hot_key", func() (interface{}, error) {
return queryDB("hot_key") // 只执行1次
})
if err != nil {
fmt.Printf("请求%d失败:%v\n", idx, err)
return
}
fmt.Printf("请求%d成功:%v,是否共享:%t\n", idx, val, shared)
}(i)
}
wg.Wait()
}

输出:只会打印 1 次「真实查询DB」,其余 9 个请求直接共享结果。

五、关键特点(vs 互斥锁)

  • 互斥锁:一次一个线程进临界区,串行执行,所有请求都要排队。
  • SingleFlight只执行1次,其他等待共享,并行等待、结果共享,性能更高。

六、避坑点(面试高频)

  1. 不是缓存:不存结果,只合并「缓存未命中→查DB」这一瞬间的请求,必须配合 Redis 等缓存用
  2. 阻塞风险:如果执行函数卡死/慢,所有等待者都会阻塞 → 加超时控制(Do+context)
  3. 错误扩散:执行函数失败,所有等待者都拿到错误 → 做好降级/重试
  4. key 设计:必须精准(如订单ID、用户ID),不能带随机参数/时间戳,否则合并失效。
  5. 全局复用singleflight.Group 要全局单例,否则不同实例无法合并请求。

七、总结(一句话)

SingleFlight:同一 key 并发请求合并为 1 次执行,防缓存击穿、降下游压力

Flink 核心速记

一、基础定位

Apache Flink分布式实时流处理引擎,主打低延迟、高吞吐、Exactly-Once,同时支持批处理(批是流的特例)。
口号:流处理优先,批流一体

二、核心特点

  1. 批流统一
    底层统一流式模型,批数据 = 有界流,实时数据 = 无界流,一套代码跑批/流。
  2. 状态计算
    内置状态管理(内存+持久化),支持累计、窗口、聚合等计算,是实时计算核心。
  3. 精准语义
    支持 Exactly-Once(精确一次)、At-Least-Once、At-Most-Once。
  4. 低延迟
    基于事件驱动,而非微批,毫秒级延迟。
  5. 高可用 & 容错
    依靠 Checkpoint(检查点)+ Savepoint(保存点) 做故障恢复、版本升级。

三、核心架构(四大组件)

  • JobManager:主节点,调度任务、协调 Checkpoint、故障恢复(集群大脑)
  • TaskManager:工作节点,执行具体 Task,管理内存/网络/状态
  • Client:客户端,提交任务、生成执行图
  • ResourceManager:资源管理器(对接 Yarn/K8s/Standalone)

运行链路:Client 提交任务 → JobManager 解析生成执行图 → 分发到 TaskManager 执行。

四、核心编程模型(两层 API)

1. 底层 API:DataStream(流) / DataSet(批,逐步废弃)

面向数据流,细粒度控制,做复杂计算、底层开发。
核心概念:

  • Stream:无界/有界数据流
  • Operator:算子(map、filter、flatMap、keyBy、reduce、sink)
  • KeyBy:分组(类似 SQL group by),数据按 key 分到同一下游子任务

2. 高层 API(主流业务使用)

  • Table API & Flink SQL:类 SQL 语法,开发效率高,企业最常用,支持窗口、聚合、关联。

五、高频核心概念(必背)

1. 窗口 Window

流是无限的,靠窗口切分成有限数据集再计算:

  • 滚动窗口 Tumbling:固定时长/大小,不重叠、不漏数据
  • 滑动窗口 Sliding:窗口长度 > 滑动步长,数据重叠
  • 会话窗口 Session:按空闲间隔划分,一段时间无数据则窗口关闭

2. 时间语义(3 种)

  1. EventTime 事件时间:数据本身携带的时间戳(业务最常用,保证数据时序正确)
  2. ProcessingTime 处理时间:算子所在机器的系统时间(延迟最低,易受机器影响)
  3. IngestionTime 摄入时间:数据进入 Flink 的时间

配合 Watermark(水位线) 解决 EventTime 乱序、迟到数据 问题。

3. Watermark 水位线

  • 作用:标记小于该时间的数据已全部到达,触发窗口计算
  • 本质:一种特殊数据流,单调递增
  • 迟到数据处理:设置允许迟到时间、侧输出流丢弃脏数据

4. 状态 State

  • 分类:托管状态(Flink 自动管理,推荐)、原生状态
  • 常用结构:ValueState、ListState、MapState、ReducingState
  • 持久化:搭配 Checkpoint 落地到 HDFS/Redis 等

5. Checkpoint & Savepoint

  • Checkpoint:自动周期性快照,故障自动恢复,保证 Exactly-Once
  • Savepoint:手动触发快照,用于版本升级、代码修改、任务启停,人工运维使用

六、数据传输 & 并行度

  • 并行度 Parallelism:决定任务并发能力,每个算子可单独设置并行度
  • 数据分区策略:keyBy(哈希)、rebalance(轮询)、rescale、broadcast 等

七、典型应用场景

  1. 实时大屏、实时指标统计
  2. 实时风控、日志分析、用户行为分析
  3. 实时数仓(分层计算:ODS → DWD → DWS → ADS)
  4. 实时 ETL、数据同步

八、和 Spark Streaming 核心区别(面试对比)

  1. 模型
    • Flink:纯流模型,事件驱动,毫秒级延迟
    • Spark Streaming:微批模型,按批次处理,延迟秒级
  2. 时间 & 乱序
    • Flink:原生支持 EventTime + Watermark,乱序处理完善
    • Spark 弱支持,乱序处理复杂
  3. 状态
    • Flink 内置强状态管理;Spark 依赖外部存储
  4. 批流
    • Flink 流优先,批流一体;Spark 批优先,流是补充

极简背诵口诀

Flink = 分布式实时流引擎 + 批流一体 + 事件时间+水位线 + 状态+Checkpoint + 低延迟 Exactly-Once