从 Bitmap 到布隆过滤器,再到高并发缓存设计策略!

简介: bitmap和布隆过滤器海量整数中是否存在某个值--bitmap在一个程序中,经常有让我们判断一个集合中是否存在某个数的case;大多数情况下,只需要用map或是list这样简单的数据结构,如果使用的是高级语言,还能乘上快车调用几个封装好的api,加几个if else,两三行代码就可以在控制台看自己“完美”而又“健壮”的代码跑起来了。

但是,事无完美,在高并发环境下,所有的case都会极端化,如果这是一个十分庞大的集合(给这个庞大一个具体的值吧,一个亿),简单的一个hash map,不考虑链表所需的指针内存空间,一亿个int类型的整数,就需要380多M(4byte × 10 ^8),十亿的话就是4个G,不考虑性能,光算算这内存开销,即使现在满地都是128G的服务器,也不好吃下这一壶。


bitmap则使用位数代表数的大小,bit中存储的0或者1来标识该整数是否存在,具体模型如下:

image.png

“bitmap”,其中4321这四个数存在

计算一下bitmap的内存开销,如果是1亿以内的数据查找,我们只需要1亿个bit = 12MB左右的内存空间,就可以完成海量数据查找了,是不是极其诱人的一个内存缩减,以下为Java实现的bitmap代码:

public class MyBitMap {
    private byte[] bytes;
    private int initSize;
    public MyBitMap(int size) {
        if (size <= 0) {
            return;
        }
        initSize = size / (8) + 1;
        bytes = new byte[initSize];
    }
    public void set(int number) {
        //相当于对一个数字进行右移动3位,相当于除以8
        int index = number >> 3;
        //相当于 number % 8 获取到byte[index]的位置
        int position = number & 0x07;
        //进行|或运算  参加运算的两个对象只要有一个为1,其值为1。
        bytes[index] |= 1 << position;
    }
    public boolean contain(int number) {
        int index = number >> 3;
        int position = number & 0x07;
        return (bytes[index] & (1 << position)) != 0;
    }
    public static void main(String[] args) {
        MyBitMap myBitMap = new MyBitMap(32);
        myBitMap.set(30);
        myBitMap.set(13);
        myBitMap.set(24);
        System.out.println(myBitMap.contain(2));
    }
}

使用简单的byte数组和位运算,就能做到时间与空间的完美均衡,是不是美美哒,wrong!试想一下,如果我们明确这是一个一亿以内,但是数量级只有10的集合,我们使用bitmap,同样需要开销12M的数据,如果是10亿以内的数据,开销就会涨到120M,bitmap的空间开销永远是和他的数据取值范围挂钩的,只有在海量数据下,他才能够大显身手。


再说说刚刚提到的那个极端case,假设这个数据量在一千万,但是取值范围好死不死就在十个亿以内,那我们不可避免还是要面对120M的开销,有方法应对么?


布隆过滤器

如果面对笔者说的以上问题,我们结合一下常规的解决方案,譬如说hash一下,我将十亿以内的某个数据,hash成一亿内的某个值,再去bitmap中查怎么样,如下图,布隆过滤器就是这么干的:

image.png

到的值,减小hash碰撞的概率


像上面的图注所说,我们可以利用多个hash算法减小碰撞概率,但只要存在碰撞,就一定会有错误判断,我们无法百分百确定一个值是否真的存在,但是hash算法的魅力在于,我不能确定你是否存在,但是我可以确定你是否真的不存在,这也就是以上的实现为什么称之“过滤器”的原因了。


高并发缓存设计策略

why cache??


如果读者是一个计算机专业的同学,cache这个词应该是能达到让耳朵起茧的出现频次。在计算机体系中,cache是介于cpu以及内存之间,用来缓和cpu和内存处理速度差距的那么一个和事佬;在OS中,page cache又是内存和IO之间的和事佬。


cache是个和事老??听着似乎怪怪的,但是也蛮形象的啦。


前面讲了大半截的算法理论,为了防止读者犯困,直接进入下半部分主题,高并发缓存设计。


即使是在软件层,我们同样需要这么一个和事老,从最简单的服务架构开始,通常我们在服务端发起请求,然后CURD某个关系型数据库例如Mysql。但是,类似这样的架构都需要有一个磁盘作为终端持久化,即使增加索引,使用B+树的这种数据结构进行优化查询,效率还是会卡在需要频繁寻道的IO上。


这个时候,一个和事老的作用就十分明显了,我们会添加一些内存操作,来缓和IO处理速度慢带来的压力。cache is not a problem,how to use it is actually a problem。


缓存一致性问题

缓存处理的机制有以下几种:


cache aside;

read through;

write through;

write behind caching;

缓存穿透问题

所谓的缓存击穿,就是当请求发出,而无法在缓存中读到数据时,请求还是会作用到database,这样的话,缓存减压的效果就不复存在了。


设想这么一个场景,如果一个用户,使用大流量恶意频繁地去查询一条数据库中没有的记录,一直击穿缓存,势必会把database打死,如何避免缓存击穿,这就是一个问题了。


有两种方案,第一种,在缓存中添加空值,如果在database中查询无果,我们大可以把值设置为null,防止下次再次访问数据库,这样做简单便捷,但是多少有些浪费空间。


第二种方案,就是使用布隆过滤器(点题),在cache与web服务器中间加一层布隆过滤器,对访问的key做记录,如此以来,同样可以解决缓存击穿的问题。


缓存雪崩问题

缓存雪崩发生于在某个时间点,缓存同时失效,例如缓存设置了失效时间,这会联动的导致大量缓存击穿问题。


加分布式锁是一种解决方案,只有拿到锁的请求才能访问database。但是这样治标不治本,当请求量过多时,大量的线程阻塞,也会把内存撑坏的。


预热数据,分散地设置失效时间,这样可以减少缓存雪崩发生的概率。


提高缓存可用性,cache的单点一样是会是缓存雪崩的隐患,大部分缓存中间件都提供高可用架构,如redis的主从+哨兵架构。


原文链接:https://blog.csdn.net/that_is_cool/article/details/91346356


版权声明:本文为CSDN博主「that_is_cool」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

目录
相关文章
|
5月前
|
缓存 关系型数据库 MySQL
在MySQL中处理高并发和负载峰值的关键技术与策略
采用上述策略和技术时,每个环节都要进行细致的规划和测试,确保数据库系统既能满足高并发的要求,又要保持足够的灵活性来应对各种突发的流量峰值。实施时,合理评估和测试改动对系统性能的影响,避免单一措施可能引起的连锁反应。持续的系统监控和分析将对维护系统稳定性和进行未来规划提供重要信息。
315 15
|
4月前
|
数据采集 存储 算法
高并发爬虫的限流策略:aiohttp实现方案
高并发爬虫的限流策略:aiohttp实现方案
|
6月前
|
缓存 负载均衡 网络协议
电商API接口性能优化技术揭秘:缓存策略与负载均衡详解
电商API接口性能优化是提升系统稳定性和用户体验的关键。本文聚焦缓存策略与负载均衡两大核心,详解其在电商业务中的实践。缓存策略涵盖本地、分布式及CDN缓存,通过全量或部分缓存设计和一致性维护,减少后端压力;负载均衡则利用反向代理、DNS轮询等技术,结合动态调整与冗余部署,提高吞吐量与可用性。文中引用大型及跨境电商平台案例,展示优化效果,强调持续监控与迭代的重要性,为电商企业提供了切实可行的性能优化路径。
|
7月前
|
缓存 搜索推荐 CDN
HTTP缓存策略的区别和解决的问题
总的来说,HTTP缓存策略是一种权衡,需要根据具体的应用场景和需求来选择合适的策略。理解和掌握这些策略,可以帮助我们更好地优化网页性能,提高用户的浏览体验。
219 11
|
6月前
|
存储 缓存
.NET 6中Startup.cs文件注入本地缓存策略与服务生命周期管理实践:AddTransient, AddScoped, AddSingleton。
记住,选择正确的服务生命周期并妥善管理它们是至关重要的,因为它们直接影响你的应用程序的性能和行为。就像一个成功的建筑工地,工具箱如果整理得当,工具选择和使用得当,工地的整体效率将会大大提高。
261 0
|
9月前
|
数据采集 缓存 JavaScript
数据抓取的缓存策略:减少重复请求与资源消耗
本教程聚焦于提升爬虫效率与稳定性,通过结合缓存策略、代理IP技术(如爬虫代理)、Cookie和User-Agent设置,优化数据采集流程。以知乎为例,详细讲解如何抓取指定关键词的文章标题和内容。内容涵盖环境准备、代码实现、常见问题及解决方案,并提供延伸练习,帮助读者掌握高效爬虫技巧。适合具备Python基础的初学者,助你规避网站机制,顺利获取目标数据。
265 2
数据抓取的缓存策略:减少重复请求与资源消耗
|
11月前
|
存储 缓存 NoSQL
云端问道21期方案教学-应对高并发,利用云数据库 Tair(兼容 Redis®*)缓存实现极速响应
云端问道21期方案教学-应对高并发,利用云数据库 Tair(兼容 Redis®*)缓存实现极速响应
334 1
|
11月前
|
缓存 NoSQL 关系型数据库
云端问道21期实操教学-应对高并发,利用云数据库 Tair(兼容 Redis®)缓存实现极速响应
本文介绍了如何通过云端问道21期实操教学,利用云数据库 Tair(兼容 Redis®)缓存实现高并发场景下的极速响应。主要内容分为四部分:方案概览、部署准备、一键部署和完成及清理。方案概览中,展示了如何使用 Redis 提升业务性能,降低响应时间;部署准备介绍了账号注册与充值步骤;一键部署详细讲解了创建 ECS、RDS 和 Redis 实例的过程;最后,通过对比测试验证了 Redis 缓存的有效性,并指导用户清理资源以避免额外费用。
260 1
|
12月前
|
存储 消息中间件 设计模式
缓存数据一致性策略如何分类?
数据库与缓存数据一致性问题的解决方案主要分为强一致性和最终一致性。强一致性通过分布式锁或分布式事务确保每次写入后数据立即一致,适合高要求场景,但性能开销大。最终一致性允许短暂延迟,常用方案包括Cache-Aside(先更新DB再删缓存)、Read/Write-Through(读写穿透)和Write-Behind(异步写入)。延时双删策略通过两次删除缓存确保数据最终一致,适用于复杂业务场景。选择方案需根据系统复杂度和一致性要求权衡。
397 0
|
7月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?