分布式系列之缓存设计中常见的问题

linlinismine linlinismine     2022-12-16     755

关键词:

 

    缓存这个东西相信大家工作中都接触得比较多,相应的在不同场景下也会遇到各种各样的问题。下面我列举几种可能会遇到的问题并提供一些解决建议。

 

1、如何把海量数据存放在缓存中并提供快速查询

     现实中我们的缓存通常都是以string,map,array,list,set,tree等具体的类型或者集合存放内存中,它们的共同点都在于把元素具体内容放到内存里面。这种在元素数量小的时候是没问题。但一旦数据量过大,消耗的内存也会呈现线性增长,最终达到瓶颈,并且查询效率也可能随着元素数量增长而下降。比如list与array,没有数字下标的情况下只能是0(n)遍历,有人也许会说到map的效率不是很高吗,查询效率可以达到O(1)。但这只是理想情况而已,hash冲突大的情况下map的查询也会退化,并且map也并没有解决内存消耗的问题。难道就没有办法解决这个问题吗?当然有!答案就是Bit-map和布隆过滤器!

     

      什么是Bit-map?

       所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素或者元素经过转换(比如hash)得到的值。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。原理如下图:

  技术分享图片

 

      那1M的Bit-map可以表示多少数据呢,1兆字节(mb)=8388608比特(bit),也就是指我们可以用1M的内存表示800W+的数据。。。。

      举个情景比如运营方给了一批100W用户ID,如果这批用户在指定时间内购买了某个商品就给用户派一张优惠劵,假设这100W的用户的userId都是64位的长整型数。那如何把这100W的用户存起来节省空间然后访问的性能也不差?我的建议是放到redis缓存里面利用redis的setbit,getbit的命令存储起来和访问,这样要比你存db要节省更多的空间并且查询速度也快~。如果这100W的用户ID分布范围比较随机,我建议是本地排好序,然后分成几段用不同Bit-map表示~,这样就不会造成过多不必要的空间浪费。别外本地排序也要以用Bit-map实现哦。

     

    什么又是布隆过滤器?

   布隆过滤器英语:Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

  基本概念和原理:

  如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为O(n),O(logn),O(n/k)

    布隆过滤器的原理是,当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。

 所以说布隆过滤器底层还是依赖Bit-map的存储原理,因为是通过散列函数来进行映射就会有冲突的可能性,当元素a,b的hash出来index是一样的时候就无法判断到底Bit-map里面存的是a还是b。所以一般布隆过滤器是不允许删除元素的,因为真不知道删除的是哪个元素。。。。

  布隆过滤器hash冲突性与散列函数的设计和Bit-map的大小有关,如果Bit-map太小那必然很多元素都会落到同一个下标,并且后面数量越大冲突也就越大。不过不用太担心毕竟1M的Bit-map就可以保存800W+数据~。

   当你存储元素量不是很大的话,可以优先考虑散列表(map),数量大时布隆过滤器会不错的选择~。

  

2、高并发时缓存如何更新

    在更新缓存时我们通常会加锁更新,为了减少锁住的资源,通常用分段锁的设计,只锁需要更新的资源。但在高并发的情景下大多数发请求都集中在一个key的时候也就是hot-key的情形下,对缓存进行更新。如果采用加锁的形式,就只能有一个线程去更新,其它的线程就只能同步阻塞,瞬间就有会大量线程hold住,甚至有可能把线程打满,这个时候系统的性能就会大打折扣。所以在高并发hot-key的情景下,加锁更新很影响性能。如果把读取和更新的操作隔离开来会怎样,如下图

技术分享图片

 

   这种方案就是起额外的线程定时去定时去更新cache,这样读取线程就不会存在锁争的问题。这种通常cache不会设置超时时间,比如guava cache的refreshAfterWrite策略的cache就是永远不会超时的,只是每次读取的时候判断是否到了刷新周期,如果到了选取其中一个线程去更新,其它线程仍然返回旧值。所以这异步更新cache也不失为一个方法。只是要注意的控制好异步更新频率,频率太小那cache的实时性就会受影响。

 

 3、Redis OR Memcache?

      每当有人问我这个东西是用redis还是memcache存起来时,我会建议他从下面几个方面去考虑:

       1、缓存的更新设置是怎样的?每次都get,set全部数据吗还是部分。

       2、除了get,set还有其它操作吗?比如排序,获取前面5个元素?

       3、预计缓存的qps有多少?

       4、缓存需要持久化吗

       5、单个缓存有多大

       对于redis、memcache来说,我觉得如果是一般业务首先考虑的不应该是两者的性能问题,而是这两者提供的数据结构哪个更适切合你当前的业务需求还有将来业务的发展。在这一点方面redis无疑是占据了绝大优势,因为redis提供了String、Hash、List、Set和Sorted Set五种数据结构,而memcache只有key-value。所以一般我是优先考虑redis的。另外在单个缓存大小方面memcache的value存储,最大为1M,如果存储的value很大,只能使用redis。刚提到为什么不优先考虑性能问题呢?因为这两者的性能都不算差,并且后面都是可以横向扩展的,甚至还可以通过其它方式比如增加多层cache如local cache去提升。其实更多还是考虑业务的维护与迭代。如果你是纯k-v操作,并且数据量非常大,并发量非常大的业务,这个时候我建议你memcache会更适合你~,如果是其它redis可能会更适合你~。

 

     



大型网站架构系列:缓存在分布式系统中的应用

本文是《缓存在分布式系统中的应用》第三篇文章。上次主要给大家分享了,缓存在分布式系统中的应用,主要从不同的场景,介绍了CDN,反向代理,分布式缓存,本地缓存的常规架构和基本原理。因为时间关于,原计划分享《... 查看详情

缓存系列:缓存一致性问题的解决思路

大家好,我是李哥。上次我们讨论了在分布式系统下的缓存架构体系,从浏览器缓存到客户端缓存,再到CDN缓存,再到反向代理缓存,再到本地缓存,再到分布式缓存。整个链路中有非常多的缓存。在整个缓存链路,存在各种各... 查看详情

重学springboot系列之ehcache缓存,缓存问题(代码片段)

...Boot系列之EhCache缓存,缓存问题,session共享与redis分布式锁EhCache缓存整合SpringCache与Ehcache缓存的使用方法缓存使用中的坑缓存雪崩穿透等解决方案缓存使用的若干问题缓存穿透缓存击穿缓存雪崩redis缓存配置自定义缓存到... 查看详情

大型网站架构系列:缓存在分布式系统中的应用

缓存是分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。本文是缓存在分布式应用第一篇文章,介绍缓存的原理,缓存的分类,缓存的设计,CDN缓存(原理,架... 查看详情

[分布式服务]海量互联网服务设计之降级设计

...聚集化体验降级简化流程分级体验降级服务要点上周在[分布式服务]海量互联网服务设计的有损价值观这篇文章中提到,与金融行业服务要求的强一致性不同,海量互联网服务要求的是能够扛住更高的qps,服务降级研究的问题是在... 查看详情

分布式缓存系列之guavacache(代码片段)

   guava是google的一个开源java框架,其github地址是https://github.com/google/guava。guava工程包含了若干被Google的Java项目广泛依赖的核心库,例如:集合[collections]、缓存[caching]、原生类型支持[primitivessupport]、并发库[concurrencylib... 查看详情

软件设计之缓存使用

...存的使用-总结》  it.zuocheng.net 本文主要讨论分布式环境下,缓存怎样在软件设计作用、原理、实现方式及注意问题。缓存的作用减小原始数据訪问压力提高资源利用率缓存的原理局部性原理缓存的实现方式查询算法... 查看详情

大型网站架构系列:缓存在分布式系统中的应用

缓存是分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。本文是缓存在分布式应用第二篇文章,介绍分布式缓存,Memcache,Redis,本地缓存(硬盘缓存,内存缓... 查看详情

分布式架构系列:缓存

一、缓存概述缓存是分布式系统中的重要组件,主要解决高并发,大数据场景下,热点数据访问的性能问题。提供高性能的数据快速访问。1.1缓存的原理(1) 将数据写入/读取速度更快的存储(设备);(2) 将数据缓存... 查看详情

day648.缓存设计问题-java业务开发常见错误(代码片段)

...,阿昌来也!今天学习分享记录的是关于缓存设计的一系列问题。通常我们会使用更快的介质(比如内存)作为缓存,来解决较慢介质(比如磁盘)读取数据慢的问题,缓存是用空间换时间,来解决... 查看详情

day648.缓存设计问题-java业务开发常见错误(代码片段)

...,阿昌来也!今天学习分享记录的是关于缓存设计的一系列问题。通常我们会使用更快的介质(比如内存)作为缓存,来解决较慢介质(比如磁盘)读取数据慢的问题,缓存是用空间换时间,来解决... 查看详情

分布式系统缓存系列之guavacache

https://baobao.baidu.com/article/a82f691856b7d6dcb42dd52b5ba19806.htmlhttps://baobao.baidu.com/article/f6567059f32d0b6239371102b290ee61.htmlhttps://baobao.baidu.com/article/9f4fafe30fe54d5cc5436141c3b 查看详情

系统架构设计中缓存的重要性

  在分布式网络系统中,缓存更是无处不在:(1)对静态页面的缓存;(2)服务端对某些请求数据的缓存(包括本地缓存和分布式缓存);(3)客户端对服务器端数据的缓存,例如我们的头像等信息;使用缓存带来的问题:... 查看详情

分布式技术专题「分布式缓存专题」针对于缓存淘汰算法之lru和lfu及fifo原理分析

前提概要无论是浏览器缓存(如果是chrome浏览器,可以通过chrome:://cache查看),还是服务端的缓存(通过memcached或者redis等内存数据库)。缓存不仅可以加速用户的访问,同时也可以降低服务器的负载和压力。那么了解常见的缓存淘汰... 查看详情

replication(上):常见的复制模型&分布式系统的挑战

总第530篇2022年第047篇分布式系统设计是一项十分复杂且具有挑战性的事情。其中,数据复制与一致性更是其中十分重要的一环。数据复制领域概念庞杂、理论性强,如果对应的算法没有理论验证大概率会出错。如果在设计过程... 查看详情

面对集中式缓存实现上的挑战,redis交出的是何种答卷?聊聊redis在分布式方面的能力设计(代码片段)

对于一个集中式缓存的分布式能力构建,必须要额外提供一些机制,来保障数据在各个节点上的安全与一致性。本文以Redis为代表,看下集Redis面对上述问题交出的是怎样一份答卷。大家好,又见面了。本文是笔者作为掘金技术... 查看详情

58沈剑架构系列缓存架构设计细节二三事

本文主要讨论这么几个问题:(1)“缓存与数据库”需求缘起(2)“淘汰缓存”还是“更新缓存”(3)缓存和数据库的操作时序(4)缓存和数据库架构简析 一、需求缘起场景介绍缓存是一种提高系统读... 查看详情

http系列之:http缓存

简介为了提高网站的访问速度和效率,我们需要设计各种各样的缓存,通过缓存可以避免不必要的额外数据传输和请求,从而提升网站的请求速度。对于HTTP协议来说,本身就自带有HTTP缓存。今天我们就深入探讨一下HTTP中的缓存... 查看详情