Redis五大高级数据结构:原理-场景-底层-横向对比

全文采用苏格拉底逐层追问模式:核心本质→独有特性→为什么适配业务→执行流程→反问自查,全程大白话,无晦涩书面语,适合面试背诵+源码复盘。

覆盖结构:Bitmap、HyperLogLog、Geo、Stream、布隆过滤器


一、Bitmap 位图

1. 核心本质是什么?

Bitmap 底层就是 Redis String 字符串,区别在于:它不以整串字符为操作单位,而是以单个二进制 bit 位作为最小操作单元。

  • 1 字节 = 8 个 bit,每个 bit 只有 01 两种取值

  • 本质:用大量二进制位,批量存储二值状态数据

2. 它具备哪些独有特性?

  1. 内存占用极低:1 个 bit 保存 1 条状态,亿级数据仅占用十几 MB

  2. 仅支持 0/1 两种值,只适合「是/否」类场景

  3. 原生支持批量统计、位运算

  4. 偏移位可自动扩容,无需提前申请内存空间

3. 为什么能解决签到、在线状态、已读标记这类场景?

这类业务有明确共性:

  • 每条数据只有两种结果:签到/未签到、在线/离线、已读/未读

  • 用户体量庞大,对内存占用要求高

  • 经常需要统计总数

Bitmap 可以完美匹配:

  • 二值数据用单个 bit 存储,完全不浪费空间

  • 海量数据场景下,内存开销大幅降低

  • 内置统计命令,可快速统计状态为 1 的总数量

4. 它是如何一步步实现业务逻辑的?

用户每日签到为例

规则定义:

  • Key:sign:日期,按天拆分数据

  • 偏移量 offset = 用户ID,一个用户独占一个 bit 位

  • bit=1 代表已签到,bit=0 代表未签到

执行流程:

  1. 用户签到

    SETBIT sign:20260529 10001 1

    定位到对应用户的 bit 位,设置为 1。重复签到offset一致,不会重复计数,天然去重。

  2. 查询单个用户是否签到

    GETBIT sign:20260529 10001

    读取对应 bit 位,0=未签到,1=已签到。

  3. 统计当日总签到人数

    BITCOUNT sign:20260529

    遍历所有 bit 位,累加值为 1 的数量,得到签到总人数。

5. 结构示意图

#mermaid-svg-Xje34A9FeZdRmpj1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Xje34A9FeZdRmpj1 .error-icon{fill:#552222;}#mermaid-svg-Xje34A9FeZdRmpj1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Xje34A9FeZdRmpj1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Xje34A9FeZdRmpj1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Xje34A9FeZdRmpj1 .marker.cross{stroke:#333333;}#mermaid-svg-Xje34A9FeZdRmpj1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Xje34A9FeZdRmpj1 p{margin:0;}#mermaid-svg-Xje34A9FeZdRmpj1 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 .cluster-label text{fill:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 .cluster-label span{color:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 .cluster-label span p{background-color:transparent;}#mermaid-svg-Xje34A9FeZdRmpj1 .label text,#mermaid-svg-Xje34A9FeZdRmpj1 span{fill:#333;color:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 .node rect,#mermaid-svg-Xje34A9FeZdRmpj1 .node circle,#mermaid-svg-Xje34A9FeZdRmpj1 .node ellipse,#mermaid-svg-Xje34A9FeZdRmpj1 .node polygon,#mermaid-svg-Xje34A9FeZdRmpj1 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Xje34A9FeZdRmpj1 .rough-node .label text,#mermaid-svg-Xje34A9FeZdRmpj1 .node .label text,#mermaid-svg-Xje34A9FeZdRmpj1 .image-shape .label,#mermaid-svg-Xje34A9FeZdRmpj1 .icon-shape .label{text-anchor:middle;}#mermaid-svg-Xje34A9FeZdRmpj1 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Xje34A9FeZdRmpj1 .rough-node .label,#mermaid-svg-Xje34A9FeZdRmpj1 .node .label,#mermaid-svg-Xje34A9FeZdRmpj1 .image-shape .label,#mermaid-svg-Xje34A9FeZdRmpj1 .icon-shape .label{text-align:center;}#mermaid-svg-Xje34A9FeZdRmpj1 .node.clickable{cursor:pointer;}#mermaid-svg-Xje34A9FeZdRmpj1 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Xje34A9FeZdRmpj1 .arrowheadPath{fill:#333333;}#mermaid-svg-Xje34A9FeZdRmpj1 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Xje34A9FeZdRmpj1 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Xje34A9FeZdRmpj1 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xje34A9FeZdRmpj1 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Xje34A9FeZdRmpj1 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xje34A9FeZdRmpj1 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Xje34A9FeZdRmpj1 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Xje34A9FeZdRmpj1 .cluster text{fill:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 .cluster span{color:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Xje34A9FeZdRmpj1 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Xje34A9FeZdRmpj1 rect.text{fill:none;stroke-width:0;}#mermaid-svg-Xje34A9FeZdRmpj1 .icon-shape,#mermaid-svg-Xje34A9FeZdRmpj1 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Xje34A9FeZdRmpj1 .icon-shape p,#mermaid-svg-Xje34A9FeZdRmpj1 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Xje34A9FeZdRmpj1 .icon-shape .label rect,#mermaid-svg-Xje34A9FeZdRmpj1 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Xje34A9FeZdRmpj1 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Xje34A9FeZdRmpj1 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Xje34A9FeZdRmpj1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 底层依托
String 字符串
字节1
字节2
bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7
bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7

6. 延伸思考(反问自查)

为什么不用 String/Hash 单独存储每个用户的签到状态?

千万级用户会产生海量 Key/Field,内存会膨胀上千倍,不适合海量二值状态场景。


二、HyperLogLog (HLL) 基数统计

1. 核心本质是什么?

HLL 是基于概率估计算法实现的数据结构。

  • 不存储原始数据,仅通过哈希采样、概率模型,计算不重复元素的总数量

  • 内存固定:单个 HLL 对象最大占用 12KB,和存入数据量无关

2. 它具备哪些独有特性?

  1. 内存大小恒定,存 10 条数据和存 1 亿条数据,最多只占 12KB

  2. 统计结果为估算值,标准误差约 0.81%,并非绝对精确

  3. 只能查询去重总数,无法取出存入的原始数据

  4. 支持多key合并统计,可以把多天、多页面数据合并汇总

3. 为什么能解决 UV、日活、海量去重计数这类场景?

这类业务有明确共性:

  • 核心需求:统计去重数量,同一用户多次访问只计算一次

  • 数据量极大,动辄千万、亿级

  • 多用于运营统计,允许微小误差

  • 不需要查询单条数据明细

传统方案痛点:

使用 Set 存储所有用户ID,会完整保存全部原始数据,亿级数据会占用 GB 级内存,成本极高。

HLL 匹配逻辑:

  • 只算数量、不存明细,极致节省内存

  • 微小误差在运营统计中完全可接受

  • 支持数据合并,轻松实现多日期、多页面汇总统计

4. 它是如何一步步实现业务逻辑的?

页面 UV 统计为例

规则定义:

  • Key:uv:page:页面ID:日期

  • 存入元素:用户唯一ID

执行流程:

  1. 用户访问页面

    PFADD uv:page:1:20260529 10001

    对用户ID做哈希、采样计算,更新内部统计结构,不保存原始ID。重复添加同一个用户ID自动去重。

  2. 查询当日页面独立访客数

    PFCOUNT uv:page:1:20260529

    基于内部采样数据,通过算法估算出不重复用户总数。

  3. 合并多天 UV 数据

    PFMERGE uv:page:total uv:page:1:day1 uv:page:1:day2

    合并多个 HLL 的统计数据,再通过 PFCOUNT 获取汇总结果。

5. 结构示意图

#mermaid-svg-vZCrbnzo1SNkLUMS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vZCrbnzo1SNkLUMS .error-icon{fill:#552222;}#mermaid-svg-vZCrbnzo1SNkLUMS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vZCrbnzo1SNkLUMS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vZCrbnzo1SNkLUMS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vZCrbnzo1SNkLUMS .marker.cross{stroke:#333333;}#mermaid-svg-vZCrbnzo1SNkLUMS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vZCrbnzo1SNkLUMS p{margin:0;}#mermaid-svg-vZCrbnzo1SNkLUMS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS .cluster-label text{fill:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS .cluster-label span{color:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS .cluster-label span p{background-color:transparent;}#mermaid-svg-vZCrbnzo1SNkLUMS .label text,#mermaid-svg-vZCrbnzo1SNkLUMS span{fill:#333;color:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS .node rect,#mermaid-svg-vZCrbnzo1SNkLUMS .node circle,#mermaid-svg-vZCrbnzo1SNkLUMS .node ellipse,#mermaid-svg-vZCrbnzo1SNkLUMS .node polygon,#mermaid-svg-vZCrbnzo1SNkLUMS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vZCrbnzo1SNkLUMS .rough-node .label text,#mermaid-svg-vZCrbnzo1SNkLUMS .node .label text,#mermaid-svg-vZCrbnzo1SNkLUMS .image-shape .label,#mermaid-svg-vZCrbnzo1SNkLUMS .icon-shape .label{text-anchor:middle;}#mermaid-svg-vZCrbnzo1SNkLUMS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vZCrbnzo1SNkLUMS .rough-node .label,#mermaid-svg-vZCrbnzo1SNkLUMS .node .label,#mermaid-svg-vZCrbnzo1SNkLUMS .image-shape .label,#mermaid-svg-vZCrbnzo1SNkLUMS .icon-shape .label{text-align:center;}#mermaid-svg-vZCrbnzo1SNkLUMS .node.clickable{cursor:pointer;}#mermaid-svg-vZCrbnzo1SNkLUMS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vZCrbnzo1SNkLUMS .arrowheadPath{fill:#333333;}#mermaid-svg-vZCrbnzo1SNkLUMS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vZCrbnzo1SNkLUMS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vZCrbnzo1SNkLUMS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vZCrbnzo1SNkLUMS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vZCrbnzo1SNkLUMS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vZCrbnzo1SNkLUMS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vZCrbnzo1SNkLUMS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vZCrbnzo1SNkLUMS .cluster text{fill:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS .cluster span{color:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vZCrbnzo1SNkLUMS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vZCrbnzo1SNkLUMS rect.text{fill:none;stroke-width:0;}#mermaid-svg-vZCrbnzo1SNkLUMS .icon-shape,#mermaid-svg-vZCrbnzo1SNkLUMS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vZCrbnzo1SNkLUMS .icon-shape p,#mermaid-svg-vZCrbnzo1SNkLUMS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vZCrbnzo1SNkLUMS .icon-shape .label rect,#mermaid-svg-vZCrbnzo1SNkLUMS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vZCrbnzo1SNkLUMS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vZCrbnzo1SNkLUMS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vZCrbnzo1SNkLUMS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户ID 10001
哈希运算 & 采样
用户ID 10002
HLL 内部统计结构

仅保存特征,不存原始数据
PFCOUNT 估算 → 输出去重总数

6. 延伸思考(反问自查)

哪些场景绝对不能使用 HLL?

支付、订单、库存等要求绝对精确计数的场景;需要查询数据明细的场景也禁止使用。


补充:Bitmap VS HyperLogLog 相似度 + 核心区别

1. 二者相似度

  1. 都适合大数据量统计:都为海量统计场景设计,内存极小

  2. 都天然自动去重:同一个用户重复上报数据,都不会重复计数

  3. 都不存储原始业务数据:都拿不到具体是哪些用户签到/访问,只能拿到总数

  4. 都只能做统计,不能存详细业务信息

2. 核心本质区别

  1. 精度区别 :Bitmap 100%精确;HLL 有 0.81% 固定误差,只能做运营大屏

  2. 内存上限区别 :Bitmap 内存随用户ID最大值线性上涨;HLL 永远固定12KB,亿级数据内存无变化

  3. 合并能力 :HLL支持PFMERGE多key合并汇总;Bitmap无法跨key合并统计

  4. 附加能力 :Bitmap除了计数,还能查询单个人是否签到;HLL只能看总数,查不了单个人状态

3. 选型口诀

  • 需要精准统计 + 需要查单个用户 状态 → 用Bitmap(签到)

  • 只需要大盘总数、不在乎微小误差、需要多天合并UV → 用HLL(页面访客)


三、Geo 地理位置

1. 核心本质是什么?

Geo 底层完全复用 ZSet(有序集合),是基于 ZSet 的业务封装:

  1. 通过 Geohash 算法,将二维的「经度+纬度」坐标,转换为一维字符串

  2. 把 Geohash 编码转为 ZSet 的 score,位置标识作为 ZSet 的 member

  3. 依托 ZSet 自带的排序、范围查询能力,实现地理位置检索

2. 它具备哪些独有特性?

  1. 二维坐标转为一维有序值,地理位置相近的点位,编码前缀基本一致

  2. 支持范围检索、两点距离计算、数据排序

  3. 支持单个点位新增、删除

  4. 存在Geohash边界缺陷:地理上紧紧相邻的两个点,编码前缀可能完全不一样,导致搜不到附近的人

3. 通俗易懂举例:什么是Geohash边界缺陷?

国境线/海岸线场景举例,最直观:

  • A点:在中国最东边,坐标紧贴国境线

  • B点:仅仅隔一条马路,就在境外,物理距离相隔只有 50米(现实超级近)

  • 但是:两个点刚好落在Geohash网格的两个不同格子

  • 最终现象:物理距离只有50米,但是geohash编码前缀完全不同

最终业务bug :你站在边界搜附近1公里的人,死活搜得到不到马路对面紧挨着的人。

解决方案:业务层同时查询当前格子 + 周边8个相邻格子,兜底解决边界遗漏问题。

4. 为什么能解决附近的人、附近门店、LBS 距离计算这类场景?

这类业务有明确共性:

  • 核心需求:根据当前坐标,查找指定半径范围内的其他点位

  • 需要实时计算两个坐标之间的直线距离

  • 点位支持动态新增、删除

普通结构痛点:

String、Hash、Set 无法高效实现范围+距离检索;全量遍历计算距离,执行效率极低。

Geo 匹配逻辑:

  • Geohash 完成降维,让相近位置在 ZSet 中天然相邻

  • 借助 ZSet 范围查询,快速筛选指定半径内的点位

  • 内置距离计算命令,无需手动实现算法

5. 它是如何一步步实现业务逻辑的?

查询附近门店为例

规则定义:

  • Key:geo:shop,统一管理所有门店坐标

  • member:门店ID;score:经纬度转换后的 Geohash 值

执行流程:

  1. 录入门店经纬度

    GEOADD geo:shop 116\.481028 39\.921983 shop\_001

    将经纬度转为 Geohash 编码,存入 ZSet。

  2. 查询当前坐标 1000 米内的门店

    GEORADIUS geo:shop 116\.480000 39\.920000 1000 m

    以当前坐标为中心,筛选出指定半径内的所有门店。

  3. 计算两个门店之间的距离

    GEODIST geo:shop shop\_001 shop\_002 km

    解析坐标并计算两点直线距离,单位为千米。

6. 结构示意图

#mermaid-svg-NL1sfmjq9GUf66WE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NL1sfmjq9GUf66WE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NL1sfmjq9GUf66WE .error-icon{fill:#552222;}#mermaid-svg-NL1sfmjq9GUf66WE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NL1sfmjq9GUf66WE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NL1sfmjq9GUf66WE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NL1sfmjq9GUf66WE .marker.cross{stroke:#333333;}#mermaid-svg-NL1sfmjq9GUf66WE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NL1sfmjq9GUf66WE p{margin:0;}#mermaid-svg-NL1sfmjq9GUf66WE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NL1sfmjq9GUf66WE .cluster-label text{fill:#333;}#mermaid-svg-NL1sfmjq9GUf66WE .cluster-label span{color:#333;}#mermaid-svg-NL1sfmjq9GUf66WE .cluster-label span p{background-color:transparent;}#mermaid-svg-NL1sfmjq9GUf66WE .label text,#mermaid-svg-NL1sfmjq9GUf66WE span{fill:#333;color:#333;}#mermaid-svg-NL1sfmjq9GUf66WE .node rect,#mermaid-svg-NL1sfmjq9GUf66WE .node circle,#mermaid-svg-NL1sfmjq9GUf66WE .node ellipse,#mermaid-svg-NL1sfmjq9GUf66WE .node polygon,#mermaid-svg-NL1sfmjq9GUf66WE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NL1sfmjq9GUf66WE .rough-node .label text,#mermaid-svg-NL1sfmjq9GUf66WE .node .label text,#mermaid-svg-NL1sfmjq9GUf66WE .image-shape .label,#mermaid-svg-NL1sfmjq9GUf66WE .icon-shape .label{text-anchor:middle;}#mermaid-svg-NL1sfmjq9GUf66WE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NL1sfmjq9GUf66WE .rough-node .label,#mermaid-svg-NL1sfmjq9GUf66WE .node .label,#mermaid-svg-NL1sfmjq9GUf66WE .image-shape .label,#mermaid-svg-NL1sfmjq9GUf66WE .icon-shape .label{text-align:center;}#mermaid-svg-NL1sfmjq9GUf66WE .node.clickable{cursor:pointer;}#mermaid-svg-NL1sfmjq9GUf66WE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NL1sfmjq9GUf66WE .arrowheadPath{fill:#333333;}#mermaid-svg-NL1sfmjq9GUf66WE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NL1sfmjq9GUf66WE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NL1sfmjq9GUf66WE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NL1sfmjq9GUf66WE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NL1sfmjq9GUf66WE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NL1sfmjq9GUf66WE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NL1sfmjq9GUf66WE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NL1sfmjq9GUf66WE .cluster text{fill:#333;}#mermaid-svg-NL1sfmjq9GUf66WE .cluster span{color:#333;}#mermaid-svg-NL1sfmjq9GUf66WE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NL1sfmjq9GUf66WE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NL1sfmjq9GUf66WE rect.text{fill:none;stroke-width:0;}#mermaid-svg-NL1sfmjq9GUf66WE .icon-shape,#mermaid-svg-NL1sfmjq9GUf66WE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NL1sfmjq9GUf66WE .icon-shape p,#mermaid-svg-NL1sfmjq9GUf66WE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NL1sfmjq9GUf66WE .icon-shape .label rect,#mermaid-svg-NL1sfmjq9GUf66WE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NL1sfmjq9GUf66WE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NL1sfmjq9GUf66WE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NL1sfmjq9GUf66WE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Geohash降维
经度 + 纬度

二维坐标
一维hash编码
转为 ZSet Score
门店ID
ZSet Member
ZSet 有序集合
范围查询 → 获取附近点位

7. 延伸思考(反问自查)

为什么不单独开发新结构,而是复用 ZSet?

ZSet 已成熟支持排序、范围查询,复用现有结构可以降低开发复杂度,同时保证性能稳定。


四、Stream 流

1. 核心本质是什么?

Redis 5.0 及以上版本新增结构,专为消息队列、数据流设计,属于增强型有序日志结构。

  • 每条消息拥有全局唯一、自增的 ID,格式:时间戳\-序列号

  • 消息支持持久化,消费完成后不会自动删除

  • 原生支持消费组、消息 ACK、断点续读、异常重试

2. 它具备哪些独有特性?

  1. 消息持久化,服务宕机后数据不丢失

  2. 支持消费组,多消费者分摊任务,适配集群消费

  3. ACK 确认机制,手动标记消费完成,避免消费者宕机丢失消息

  4. 支持消息回溯、重复消费

  5. 和 List 队列不同,消息不会被取出后直接删除

3. List队列有什么痛点?Stream是如何一一解决的?

List消息队列痛点 Stream解决方案
消息弹出立刻删除,消费者宕机消息直接丢失 消息持久化留存,消费不删除,ACK之后才清除待消费标记
无消费组,无法集群多消费者负载均衡 原生消费组,天然支持分布式负载均衡
消费失败无法重试,不能回溯历史消息 无ACK可以一直重读,支持任意位置消息回溯
只能一头进一头出,消息顺序简单,无全局唯一ID 自带时间戳全局ID,支持多维度消息管理

4. 延伸对比:Redis Stream VS 专业MQ(RocketMQ/Kafka)

很多面试官追问:既然Stream补齐了List所有短板,为什么还要用RocketMQ?这里直白分层对比,不长篇大论:

✅ Stream优势

  • 无需额外部署中间件,项目本来就有Redis,零运维成本

  • 轻量易用,代码简单,适合小型异步解耦

❌ Stream硬伤(不如专业MQ)

  1. 消息堆积能力弱:Redis内存数据库,消息大量堆积会打爆内存;Kafka/RocketMQ磁盘存储,无惧海量堆积

  2. 没有消息重试队列、死信队列、消息延时队列:业务异常重试、延时订单完全没法原生支持

  3. 没有消息轨迹、消息重试次数、消息回溯时间粒度粗糙

  4. 分区/分片能力弱,单机上限明显,无法支撑百万级TPS大流量

📌 最终选型标准

  • 内部小系统、低TPS、简单异步解耦、不想部署MQ → Stream

  • 交易订单、高并发流量、需要死信/延时/重试、消息轨迹 → 必须RocketMQ/Kafka

5. 它是如何一步步实现业务逻辑的?

订单异步通知队列为例

规则定义:

  • Key:stream:order:notify,消息队列名称

执行流程:

  1. 生产者发送订单消息

    XADD stream:order:notify \* userId 1001 orderId 2026052901 money 99

    \* 表示由 Redis 自动生成消息ID,消息写入队列并持久化。

  2. 创建消费组

    XGROUP CREATE stream:order:notify group1 0

    创建消费组,从队列第一条消息开始消费。

  3. 消费者阻塞拉取消息

    XREADGROUP GROUP group1 consumer1 COUNT 1 BLOCK 0 STREAMS stream:order:notify \>

    阻塞等待新消息,拉取待处理数据。

  4. 消费完成,手动确认

    XACK stream:order:notify group1 1789000000000\-0

    告知队列当前消息已正常处理,清除待消费标记。

6. 架构示意图

#mermaid-svg-1OlZMNNTwt5zfaga{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1OlZMNNTwt5zfaga .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1OlZMNNTwt5zfaga .error-icon{fill:#552222;}#mermaid-svg-1OlZMNNTwt5zfaga .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1OlZMNNTwt5zfaga .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1OlZMNNTwt5zfaga .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1OlZMNNTwt5zfaga .marker.cross{stroke:#333333;}#mermaid-svg-1OlZMNNTwt5zfaga svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1OlZMNNTwt5zfaga p{margin:0;}#mermaid-svg-1OlZMNNTwt5zfaga .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1OlZMNNTwt5zfaga .cluster-label text{fill:#333;}#mermaid-svg-1OlZMNNTwt5zfaga .cluster-label span{color:#333;}#mermaid-svg-1OlZMNNTwt5zfaga .cluster-label span p{background-color:transparent;}#mermaid-svg-1OlZMNNTwt5zfaga .label text,#mermaid-svg-1OlZMNNTwt5zfaga span{fill:#333;color:#333;}#mermaid-svg-1OlZMNNTwt5zfaga .node rect,#mermaid-svg-1OlZMNNTwt5zfaga .node circle,#mermaid-svg-1OlZMNNTwt5zfaga .node ellipse,#mermaid-svg-1OlZMNNTwt5zfaga .node polygon,#mermaid-svg-1OlZMNNTwt5zfaga .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1OlZMNNTwt5zfaga .rough-node .label text,#mermaid-svg-1OlZMNNTwt5zfaga .node .label text,#mermaid-svg-1OlZMNNTwt5zfaga .image-shape .label,#mermaid-svg-1OlZMNNTwt5zfaga .icon-shape .label{text-anchor:middle;}#mermaid-svg-1OlZMNNTwt5zfaga .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1OlZMNNTwt5zfaga .rough-node .label,#mermaid-svg-1OlZMNNTwt5zfaga .node .label,#mermaid-svg-1OlZMNNTwt5zfaga .image-shape .label,#mermaid-svg-1OlZMNNTwt5zfaga .icon-shape .label{text-align:center;}#mermaid-svg-1OlZMNNTwt5zfaga .node.clickable{cursor:pointer;}#mermaid-svg-1OlZMNNTwt5zfaga .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1OlZMNNTwt5zfaga .arrowheadPath{fill:#333333;}#mermaid-svg-1OlZMNNTwt5zfaga .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1OlZMNNTwt5zfaga .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1OlZMNNTwt5zfaga .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1OlZMNNTwt5zfaga .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1OlZMNNTwt5zfaga .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1OlZMNNTwt5zfaga .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1OlZMNNTwt5zfaga .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1OlZMNNTwt5zfaga .cluster text{fill:#333;}#mermaid-svg-1OlZMNNTwt5zfaga .cluster span{color:#333;}#mermaid-svg-1OlZMNNTwt5zfaga div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1OlZMNNTwt5zfaga .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1OlZMNNTwt5zfaga rect.text{fill:none;stroke-width:0;}#mermaid-svg-1OlZMNNTwt5zfaga .icon-shape,#mermaid-svg-1OlZMNNTwt5zfaga .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1OlZMNNTwt5zfaga .icon-shape p,#mermaid-svg-1OlZMNNTwt5zfaga .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1OlZMNNTwt5zfaga .icon-shape .label rect,#mermaid-svg-1OlZMNNTwt5zfaga .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1OlZMNNTwt5zfaga .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1OlZMNNTwt5zfaga .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1OlZMNNTwt5zfaga :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生产者
Stream 消息队列

消息持久化留存,消费不删除
消费组 group1
消费者 1
消费者 2
执行业务逻辑
XACK确认消费完成,消息才真正标记已处理

7. 延伸思考(反问自查)

Stream 和 Kafka、RocketMQ 如何选型?

中小流量、简单异步任务选用 Stream;高并发交易、海量消息堆积、需要延时/死信队列,必须使用专业消息中间件。


五、布隆过滤器 Bloom Filter

1. 核心本质是什么?

布隆过滤器是概率型存在判断结构 ,Redis 无原生命令,业界标准实现方案:Bitmap + 多个哈希函数

  1. 开辟一段连续的 bit 内存空间

  2. 使用多个不同哈希函数,将一个元素映射到多个不同 bit 位

  3. 根据 bit 位状态,判断元素是否存在

2. 它具备哪些独有特性?

  1. 判断规则:判定不存在 ,则一定不存在;判定存在,有可能误判(假阳性)

  2. 内存占用极小,海量数据场景下,开销远低于 Set、Hash

  3. 普通布隆过滤器无法删除元素,删除操作会干扰其他元素的判断

  4. 增大 bit 空间、增加哈希函数数量,可以降低误判概率

3. 为什么能解决缓存穿透、黑名单、URL 去重这类场景?

这类业务有明确共性:

  • 核心需求:快速拦截绝对不存在的数据

  • 数据体量庞大,不适合存储全部数据明细

  • 允许极低概率的误判

传统方案痛点:

大量请求查询不存在的数据,会直接穿透到数据库,压垮 DB;使用 Set 存储全量数据,内存开销巨大。

布隆过滤器匹配逻辑:

  • 先做前置拦截,判定不存在则直接返回,不查询缓存与数据库

  • 判定存在才走正常查询流程

  • 极小的内存开销,即可拦截绝大部分无效请求

4. 它是如何一步步实现业务逻辑的?

以**防缓存穿透(用户查询)**为例

规则定义:

  • 底层依托 Bitmap,Key:bloom:user

  • 使用 3 个独立哈希函数,一个元素对应 3 个 bit 位

执行流程:

  1. 预热阶段:将数据库已存在的用户写入过滤器
plaintext 复制代码
   // 对用户ID计算3个哈希偏移量
   h1 = hash1\(userId\)
   h2 = hash2\(userId\)
   h3 = hash3\(userId\)
plaintext 复制代码
   //将对应 3 个 bit 位全部置 1。
   SETBIT bloom:user h1 1
   SETBIT bloom:user h2 1
   SETBIT bloom:user h3 1`
  1. 线上查询:判断用户是否存在
plaintext 复制代码
   b1 = GETBIT bloom:user h1
   b2 = GETBIT bloom:user h2
   b3 = GETBIT bloom:user h3
   
   if b1 && b2 && b3:
       // 大概率存在 → 先查Redis缓存,缓存未命中再查DB
   else:
      // 一定不存在 → 直接返回,不查询缓存与DB`

5. 原理示意图

#mermaid-svg-94GzJ5yBhm6TNi9e{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-94GzJ5yBhm6TNi9e .error-icon{fill:#552222;}#mermaid-svg-94GzJ5yBhm6TNi9e .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-94GzJ5yBhm6TNi9e .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-94GzJ5yBhm6TNi9e .marker{fill:#333333;stroke:#333333;}#mermaid-svg-94GzJ5yBhm6TNi9e .marker.cross{stroke:#333333;}#mermaid-svg-94GzJ5yBhm6TNi9e svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-94GzJ5yBhm6TNi9e p{margin:0;}#mermaid-svg-94GzJ5yBhm6TNi9e .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e .cluster-label text{fill:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e .cluster-label span{color:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e .cluster-label span p{background-color:transparent;}#mermaid-svg-94GzJ5yBhm6TNi9e .label text,#mermaid-svg-94GzJ5yBhm6TNi9e span{fill:#333;color:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e .node rect,#mermaid-svg-94GzJ5yBhm6TNi9e .node circle,#mermaid-svg-94GzJ5yBhm6TNi9e .node ellipse,#mermaid-svg-94GzJ5yBhm6TNi9e .node polygon,#mermaid-svg-94GzJ5yBhm6TNi9e .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-94GzJ5yBhm6TNi9e .rough-node .label text,#mermaid-svg-94GzJ5yBhm6TNi9e .node .label text,#mermaid-svg-94GzJ5yBhm6TNi9e .image-shape .label,#mermaid-svg-94GzJ5yBhm6TNi9e .icon-shape .label{text-anchor:middle;}#mermaid-svg-94GzJ5yBhm6TNi9e .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-94GzJ5yBhm6TNi9e .rough-node .label,#mermaid-svg-94GzJ5yBhm6TNi9e .node .label,#mermaid-svg-94GzJ5yBhm6TNi9e .image-shape .label,#mermaid-svg-94GzJ5yBhm6TNi9e .icon-shape .label{text-align:center;}#mermaid-svg-94GzJ5yBhm6TNi9e .node.clickable{cursor:pointer;}#mermaid-svg-94GzJ5yBhm6TNi9e .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-94GzJ5yBhm6TNi9e .arrowheadPath{fill:#333333;}#mermaid-svg-94GzJ5yBhm6TNi9e .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-94GzJ5yBhm6TNi9e .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-94GzJ5yBhm6TNi9e .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-94GzJ5yBhm6TNi9e .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-94GzJ5yBhm6TNi9e .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-94GzJ5yBhm6TNi9e .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-94GzJ5yBhm6TNi9e .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-94GzJ5yBhm6TNi9e .cluster text{fill:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e .cluster span{color:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-94GzJ5yBhm6TNi9e .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-94GzJ5yBhm6TNi9e rect.text{fill:none;stroke-width:0;}#mermaid-svg-94GzJ5yBhm6TNi9e .icon-shape,#mermaid-svg-94GzJ5yBhm6TNi9e .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-94GzJ5yBhm6TNi9e .icon-shape p,#mermaid-svg-94GzJ5yBhm6TNi9e .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-94GzJ5yBhm6TNi9e .icon-shape .label rect,#mermaid-svg-94GzJ5yBhm6TNi9e .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-94GzJ5yBhm6TNi9e .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-94GzJ5yBhm6TNi9e .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-94GzJ5yBhm6TNi9e :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

元素ID
哈希函数1 → 偏移位1
哈希函数2 → 偏移位2
哈希函数3 → 偏移位3
Bitmap 位图
所有bit位都为1?
可能存在
一定不存在

6. 延伸思考(反问自查)

如何解决普通布隆过滤器不能删除元素的问题?

改用计数布隆过滤器,将单个 bit 位改为计数器,支持数值递减,实现安全删除。


六、全文汇总对比表

数据结构 底层依赖 核心能力 设计取舍 典型业务场景
Bitmap String bit 级二值状态存储、精准批量统计 内存随ID增大上涨,无法跨key合并 用户签到、在线状态、权限标记
HyperLogLog 独立结构 海量数据去重大盘统计 牺牲微小精度,永久固定12KB内存,支持多key合并 页面UV、日活运营大盘
Geo ZSet 地理位置检索、距离计算 存在Geohash网格边界bug 附近门店、附近的人、外卖LBS
Stream 独立结构 可靠轻量级消息队列 内存存储,消息堆积能力差,无死信/延时队列 内部系统简单异步解耦
布隆过滤器 Bitmap 快速拦截不存在请求,防缓存穿透 存在假阳性误判,普通版本不能删除 缓存穿透防护、黑名单过滤

七、Redis所有高级结构统一设计思想(面试升华回答)

  1. 不重复造轮子:能复用String/ZSet就复用底层结构,降低内核复杂度

  2. 业务定向取舍:根据场景主动牺牲精度、功能,换取极致内存和性能

  3. 只做单一专精:每个高级结构只解决一类垂直业务问题,不做大而全

相关推荐
QiLinkOS1 小时前
【从实验室到商业战场:发明专利如何重塑科技与企业的共生生态】
大数据·c语言·数据结构·c++·人工智能·单片机·算法
如此这般英俊2 小时前
手撕Claude Code—第一章 agent-loop
数据结构·人工智能·语言模型·自然语言处理
过期动态3 小时前
【LeetCode 热题 100】接雨水
java·数据结构·算法·leetcode·职场和发展
青山师3 小时前
动态规划算法深度解析:从状态转移方程到工业级优化
数据结构·算法·面试·动态规划·代理模式·java面试
YL200404265 小时前
【Redis实战篇】秒杀实现方案(以优惠券秒杀为例)
数据库·redis
Severus_black6 小时前
【初阶数据结构与算法】八大排序之非比较排序(计数排序),一次性讲清!
数据结构·算法·排序算法
better_liang6 小时前
每日Java面试场景题知识点之-如何设计分布式锁
java·redis·zookeeper·面试·分布式锁
QiLinkOS6 小时前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
自传.7 小时前
Redis 高频考试面试知识点1
redis·aof·rdb·redis面试