知识卡片

统一内存管理器:解决"静态划分没有一个默认值能覆盖所有场景"的问题

普通读书笔记卡

内容

Spark 1.5及更早版本使用两个独立的内存管理器:执行时内存管理器(服务Shuffle、JOIN、Sort、Aggregation这类计算过程)和存储内存管理器(服务缓存和广播变量),两者各自分到一个固定的内存份额(默认存储内存管理器占用60%的堆内存),这种静态划分方式带来了明显的问题——不同应用的负载特征差异很大,有的应用计算密集、需要更多执行内存,有的应用缓存密集、需要更多存储内存,一个固定的静态划分比例,天然不存在一个”合理的默认值可以覆盖到所有应用场景”,用户往往需要针对自己的具体应用手动调优这个比例,否则容易触发spill(内存数据被迫交换到磁盘)这类性能问题。Spark 1.6版本的解法是把这两个原本相互独立的内存管理器统一起来:当执行内存管理器使用的内存超过了自己分配到的份额,它可以临时向存储内存管理器”借用”内存空间,反过来存储内存管理器需要更多内存时也可以向执行内存管理器借用——这种借用是动态的、临时的,被借用的内存可以在任何时候被回收;同时系统还允许设置一个存储内存的最低保证量,确保这部分内存无论如何都不会被挪用,作为一道安全底线。这个案例给出了一条应对”多个用途共享同一份有限资源、但各自需求量随场景变化很大”这类问题的通用解法:与其在设计阶段就试图找到一个”放之四海而皆准”的静态划分比例(这个尝试本身往往是徒劳的,因为不同场景的真实需求差异太大),不如把静态的划分改造成动态的、可以互相临时借用的机制,让资源能够根据实际运行时的需求动态流向真正需要它的那一方,同时保留一个最低保证量作为兜底,避免某一方因为过度借出而被完全挤压——这种”动态借用+底线保护”的组合模式,对任何面临类似”固定配额切分不出一个通用最优比例”困境的资源管理场景都有参考价值。

参考来源

- 位置:《高可用架构(第1卷)》第6章《大数据与数据库》"6.9 大数据盘点之Spark篇"节,"6.9.3 Spark未来的发展趋势"(源文件:_epub-src/OEBPS/Text/Chapter6_9_4.xhtml) - 结论依据:原文说明"这种方式分配的内存都是静态的,需要手动调优以避免spill……且没有一个合理的默认值可以覆盖到所有的应用场景。在Spark1.6版本中这2个内存管理器被统一起来了:当执行内存管理器使用了超过给自己分配大小的内存时,可以临时向存储内存管理器借用内容空间,反之亦然……还可以设置存储内存的最低量来进一步控制内存借用行为",直接支撑本卡片结论。 - 原始内容:这种方式分配的内存都是静态的,需要手动调优以避免spill……且没有一个合理的默认值可以覆盖到所有的应用场景。在Spark1.6版本中这2个内存管理器被统一起来了:当执行内存管理器使用了超过给自己分配大小的内存时,可以临时向存储内存管理器借用内容空间,反之亦然。