高并发系统实战课
徐长龙
前微博架构师、极客时间架构师
11663 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 30 讲
结束语&结课测试 (2讲)
高并发系统实战课
15
15
1.0x
00:00/00:00
登录|注册

16|本地缓存:用本地缓存做服务会遇到哪些坑?

最终一致性
上限问题
数据同步问题
使用小的Redis分片应对压力
降级为读多写少或写多读少的服务
避免业务服务缓存的原因
内存碎片和内存利用率
jmalloc的实现原理
基础内存服务的管理方式
按页访问内存
通过offset和length访问数据
内存对齐的好处
XMM项目
数据量和读写特点的影响
特殊优化方式
GC对map的影响
无锁实现
单线程执行
分片锁
锁粒度对性能的影响
单条数据锁和map锁
定期扫描续热
LRU淘汰策略
内存空间规划
Swap Out
申请大内存并填0
缺页中断问题
虚拟内存空间
操作系统底层内存管理
业务服务层缓存
集中式缓存
总结
SLAB内存管理
内存对齐
GC和数据使用类型
程序容器锁粒度
虚拟内存和缺页中断
缓存架构
思考题
读多写多的系统
本地缓存

该思维导图由 AI 生成,仅供参考

你好,我是徐长龙。
这一章我们来学习如何应对读多写多的系统。微博 Feed、在线游戏、IM、在线课堂、直播都属于读多写多的系统,这类系统里的很多技术都属于行业天花板级别,毕竟线上稍有点问题,都极其影响用户体验。
说到读多写多不得不提缓存,因为目前只有缓存才能够提供大流量的数据服务,而常见的缓存架构,基本都会使用集中式缓存方式来对外提供服务。
但是,集中缓存在读多写多的场景中有上限,当流量达到一定程度,集中式缓存和无状态服务的大量网络损耗会越来越严重,这导致高并发读写场景下,缓存成本高昂且不稳定。
为了降低成本、节省资源,我们会在业务服务层再增加一层缓存,放弃强一致性,保持最终一致性,以此来降低核心缓存层的读写压力。

虚拟内存和缺页中断

想做好业务层缓存,我们需要先了解一下操作系统底层是如何管理内存的。
对照后面这段 C++ 代码,你可以暂停思考一下,这个程序如果在环境不变的条件下启动多次,变量内存地址输出是什么样的?
int testvar = 0;
int main(int argc, char const *argv[])
{
testvar += 1;
sleep(10);
printf("address: %x, value: %d\n", &testvar, testvar );
return 0;
}
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入探讨了在读多写多系统中使用本地缓存时可能遇到的各种问题和解决方案。首先,文章从虚拟内存和缺页中断的角度解释了操作系统底层是如何管理内存的,以及在高并发读写场景下如何提高缓存效率。其次,文章讨论了程序容器锁粒度的重要性,以及在业务缓存中常见的加锁方式,提出了一些优化建议。接着,文章探讨了GC和数据使用类型对缓存性能的影响,指出了在高级语言中做缓存时需要注意的问题。最后,文章提供了一些有趣的项目和组件供读者参考,以应对缓存中可能遇到的挑战。 此外,文章还介绍了内存对齐和SLAB内存管理的相关内容。内存对齐可以提高数据访问效率,减少内存碎片,而SLAB内存管理则是为了提高内存分配的效率和减少碎片。文章还强调了在业务服务缓存中需要关注的多种因素,包括锁争抢、GC原理、内存碎片、缺页等问题,以及在业务稳定后可能需要将服务做降级或考虑使用Redis分片来应对线上压力。 总的来说,本文对于在读多写多系统中使用本地缓存时需要考虑的技术细节和解决方案进行了深入浅出的介绍,对于从事相关领域的技术人员具有一定的参考价值。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《高并发系统实战课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(8)

  • 最新
  • 精选
  • 杜杜杜的杜
    置顶
    缺页中断: Java程序,向本地缓存插入大数据量时,会频繁触发缺页中断,导致不断申请内存。过程中会占用资源(包括等待申请内存时间),导致服务器响应可能缓慢,甚至系统不稳定。 是这个意思吗?

    作者回复: 你好,感觉文中举的例子让你误会了,但是不确定具体,所以我再补充过一下。 Java程序启动后会申请一大块内存,但是这个内存是虚拟的内存,只有实际使用时,linux才会分配给他真实的物理内存,访问没分配内存的虚拟内存空间时内核会产生缺页中断分配真实物理内存。而在应用过程中,如果我们把业务数据放到服务内,如果内存使用过多可能会碰到后面这几种情况。 1.高级语言的GC会定期扫描这些对象,确定哪些可以回收来节省内存,导致服务周期卡顿。 2.内存申请过多,导致系统资源不够,这时linux会偷偷把你不怎么用的内存区域落磁盘,等你用的时候产生缺页再给从磁盘放回来给程序用。 3.有的高级语言底层使用了slab方式,而不是linux管理的内存,这相比malloc方式会高效一些,因为malloc很慢并且如果申请1M以上内存,会请求多次导致卡顿。如果频繁malloc free服务,会从内核卡住,影响响应速度。同时,因为内存碎片会增多,就会导致内存 malloc 操作 分配越来越慢,最后整体表现就是服务体验很差。 综合上面的情况,我们要注意的是,使用内存缓存数据时,要计算好容量,控制好存放的对象类型和数量,尽量避免GC扫描,避免内存使用过多被linux置换。不知道是否解决了你的疑问,可以在留言区继续交流。

    2023-02-21归属地:浙江
  • piboye
    微博长年出现挂是什么原因啊?读可以本地缓存,写需要通过热key发现来异步写,是聚合的地方挂吗?

    作者回复: 核心是读写并发过高,后来做了相应的调整,当明星出事的时候紧急扩充服务器以及数据缓存层来应对

    2023-02-16归属地:广东
    2
    1
  • John
    如果用大数组管理缓存,应该需要用bitmap或链表来管理哪些地方已使用,哪些未使用。如果修改的数据小于原来的length,则原地更新,或大于,则从空闲列表或bitmap中找到合适的位置存放新的数据,并将原数据标识为删除。是不是可以认为就是自己实现一个简易的内存分配器?

    作者回复: 你好,John,这个方式很有趣,即节省空间又方便,并且如果配合slab方式更好

    2022-11-28归属地:北京
    1
  • 千锤百炼领悟之极限
    文中提到如使用 map[int]int,其中 key 是 string 通过 hash 算法转成的 int,value 保存的内容是数据所在的 offset 和长度。 这里map的value是个数组吧,因为要存offset与length,例如:map[int][2]int

    作者回复: 没错,这里可以用两个map分别存开始和长度

    2023-07-22归属地:广东
    2
  • ARM
    比如IM场景,A发给B消息,用户A输入后,直接前端显示在页面不管是否落库(用户无感知提升体验感),然后前端异步落库。然后再查询数据库 A发送给B的信息,显示出来。将A-B这两个字段设置为联合索引

    作者回复: 你好,ARM,感觉还需要补充下信息~

    2023-01-15归属地:河南
  • 徐石头
    所以不推荐在业务层内以map的形式做一级缓存?最近在优化产品Redis缓存的内存占用,考虑在redis前面增加一级缓存,有没有比较好的解决方案吗?或者关于优化Redis内存占用相关的解决方案

    作者回复: 你好,不同语言规避GC方式不同,可以先找下相关语言用的人多的组件加入大量数据,压测观测gc性能监控。redis虽然浪费一些但是比自己实现快一些,掏钱就可用,笑,等量级上来后再优化来得及,如果量很大了,本地缓存也行,就是一致性很难

    2022-12-16归属地:内蒙古
    3
  • Geek_2c6ea9
    缓存的容量怎么设置比较好,一直往里面存会不会爆掉啊。

    作者回复: 你好,很高兴你关注这里,对于临时缓存一般这里放的数据都需要提前计算好增长量,并且搭配LRU,最后要设计好数据分片规则才行。对于业务缓存最好是放不经常更新的数据。现在这类服务的业务服务器都会配上16g以上内存,相对会好一些

    2022-12-06归属地:北京
  • 第一装甲集群司令克莱斯特
    Java 的 volidate ,还是volatile?

    作者回复: 你好,克莱斯特,这两个都很有趣~我本意是提及volidate

    2022-11-30归属地:北京
收起评论
显示
设置
留言
8
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部