当前位置: 首页 > 科技观察

HBase更好的实践-用好你的操作系统

时间:2023-03-12 17:38:16 科技观察

终于切换回HBase模式了。前段时间因为工作原因,了解了很多大数据生态的其他组件(Parquet、Carbondata、Hive、SparkSQL等),TPC-DS/TPC-H等),虽然只是一瞥,却也受益匪浅。它在视野和思维方式上起着极其重要的作用。至少,扩大了大数据领域的对话圈。在这里我也大胆建议小伙伴们可以在深入学习某一知识的同时,去探索周围的知识。相信会有很大的好处。说正题吧,操作系统的话题早就想跟大家分享了。一方面,拖到现在是因为我对各种理论没有深入了解,恐怕谈不上;由于很少有人关注它,这里是这个话题的开始。其实这些参数前前后后看了很多遍,只是没有理解透彻。前段时间趁着假期把这些理论又翻了一遍。有了进一步的了解,我有权在这里进行整理和整理。下图是HBase官方文档中对操作系统环境的一些配置要求:不要着急解释每个配置的具体含义。在此之前,你需要重点关注一个概念:swap,没错,这就是你或多或少听说过的,说实话,上面的参数都或多或少和swap有关。什么是交换?在Linux下,SWAP的作用类似于Windows系统下的“虚拟内存”。当物理内存不足时,利用部分硬盘空间作为SWAP分区(虚拟到内存中),解决内存容量不足的情况。SWAP是交换的意思。顾名思义,当进程向OS申请内存,发现内存不足时,OS会将内存中暂时不用的数据换出,放到SWAP分区中。此过程称为换出。当一个进程再次需要这些数据并且OS发现还有空闲的物理内存时,它会将SWAP分区中的数据交换回物理内存。这个过程称为换入。当然,交换的大小是有上限的。一旦swap用完,操作系统就会触发OOM-Killer机制,杀死消耗内存最多的进程,释放内存。为什么数据库系统不喜欢交换?显然,swap机制的初衷是为了缓解在物理内存耗尽时选择直接粗暴的OOM过程的尴尬。但坦白说,几乎所有的数据库都不太喜欢swap,不管是MySQL、Oracle、MongoDB还是HBase,为什么呢?这主要与以下两个方面有关:1.数据库系统普遍对响应延迟敏感。如果用swap代替内存,数据库服务性能势必难以接受。对于一个对响应延迟极其敏感的系统,延迟太多和服务不可用没有区别。比服务不可用更严重的是swap场景下进程永远不会死掉,也就是说系统永远不可用……再想想如果不使用swap直接oom是不是更好的选择,以至于很多high-可用性系统会直接从master切换到slave,用户基本感觉不到。2.另外,对于HBase这样的分布式系统,其实我们不担心某个节点宕机,只担心某个节点被夯。当一个节点宕机时,最多有少量请求暂时不可用,可以通过重试来恢复。但是如果一个节点阻塞了,所有的分布式请求都会阻塞,会占用服务端线程资源,导致整个集群请求阻塞,甚至拖垮集群。从这两个角度来看,所有的数据库都不喜欢swap是有道理的!swap的工作机制既然数据库不喜欢swap,那么是否有必要使用swapoff命令来关闭磁盘缓存功能呢?不,你可以想一想,关闭磁盘缓存意味着什么?实际生产环境中没有系统会这么激进。要知道世界从来不是0或1,每个人或多或少都会选择走在中间,只是有的人偏向于0,有的人只偏向于1。显然,在swap的问题上,数据库必须选择尽可能少的使用。HBase官方文档的几个要求其实就是为了贯彻这个政策:尽可能的减少swap的影响。知己知彼,方能百战百胜。要想减少swap的影响,就必须搞清楚linux内存回收是如何进行的,以免漏掉任何可能的疑惑。我们先看看swap是怎么触发的。总之,Linux会在两种情况下触发内存回收。一种是在内存分配过程中发现空闲内存不足时,立即触发内存回收;另一种是开启一个daemon进程(swapd进程),周期性的检查系统内存,当可用内存下降到一定阈值后,主动触发内存回收。第一种场景没什么好说的,我们重点说第二种场景,如下图:这里介绍一下我们关心的第一个参数:vm.min_free_kbytes,代表系统预留的空闲内存数量限制watermark[min],并影响watermark[low]和watermark[high]。简单的可以考虑:watermark[min]=min_free_kbyteswatermark[low]=watermark[min]*5/4=min_free_kbytes*5/4watermark[high]=watermark[min]*3/2=min_free_kbytes*3/2watermark[high]-watermark[low]=watermark[low]-watermark[min]=min_free_kbytes/4可见LInux的这些watermark都离不开参数min_free_kbytes。min_free_kbytes对系统的重要性不言而喻,既不能太大也不能太小。如果min_free_kbytes太小,[min,low]之间水位的缓冲会很小。一旦kswapd回收时上层申请内存过快(典型应用:数据库),空闲内存很容易下降到watermark[min],内核会直接回收(directreclaim),直接在进程上下文中回收application,然后使用回收的freepages来满足内存请求,所以application实际上会被阻塞,带来一定的响应延迟。当然,min_free_kbytes不能太大。一方面会减少应用进程的内存,浪费系统内存资源。另一方面,也会导致kswapd进程在内存回收上耗费大量时间。再看看这个过程,是不是类似于Java垃圾回收机制中CMS算法中的老年代回收触发机制,想想参数-XX:CMSInitiatingOccupancyFraction,是不是?官方文档要求min_free_kbytes不能小于1G(大内存系统设置8G),即不要轻易触发直接回收。至此,Linux的内存回收触发机制和我们关心的第一个参数vm.min_free_kbytes已经基本解释完毕。接下来,我们简单了解一下Linux内存回收回收的是什么。Linux内存回收对象主要分为两种:1、文件缓存,这个比较容易理解。为了避免每次都从硬盘读取文件数据,系统会将热点数据存放在内存中以提高性能。如果只读出文件,内存回收只需要释放这部分内存,下次再次读取文件数据时,可以直接从硬盘读取(类似HBase文件缓存)。那么如果不仅文件被读出,缓存的文件数据也被修改(脏数据),要回收内存,就要将这部分数据文件写入硬盘,然后释放(类似MySQL文件缓存)。2.匿名内存,这部分内存没有实际的载体,不像文件缓存有硬盘文件等载体,典型的堆和栈数据就是这样。这部分内存在回收时不能直接释放或写回到类似文件的介质中。这就是为什么要开发swap机制,将这类内存换出到硬盘上,需要的时候加载出来。具体来说,Linux通过什么算法来判断哪些文件缓存或者匿名内存需要被回收?我不在乎这里。有兴趣的可以参考这里。但是有一个问题需要我们思考:既然有两种类型的内存可以回收,那么当两种类型的内存都可以回收的时候,linux是如何决定回收哪一种内存的呢?或者两者都将被回收?这是我们关心的第二个参数:swappiness。该值用于定义使用交换的内核的活动性。值越高,内核会主动使用swap。值越低,交换要求越低。使用积极性。取值范围是0到100,默认是60。这个swappiness是怎么实现的呢?具体原理很复杂。简单的说,swappiness通过控制回收内存时是回收更多的匿名页面还是回收更多的文件缓存来达到这个效果。swappiness等于100,表示匿名内存和文件缓存将以相同的优先级被回收。默认值60意味着文件缓存将首先被回收。不会引起IO操作,对系统性能影响很小)。对于数据库来说,应该尽量避免swap,所以需要设置为0。这里需要注意的是,设置为0并不代表不执行swap!至此,我们讲了linux内存回收的触发机制和linux内存回收对象swap,并对参数min_free_kbytes和swappiness进行了说明。接下来我们再看一下与swap相关的另一个参数:zone_reclaim_mode。文档说把这个参数设置为0可以关闭NUMA的zonereclaim。这是怎么回事?说到NUMA,数据库又不爽了。很多DBA都被坑得很惨。那么下面是三个小问题:NUMA是什么?NUMA和swap有什么关系?zone_reclaim_mode的具体含义?NUMA(Non-UniformMemoryAccess)是相对于UMA而言的。两者都是CPU设计架构。CPU采用UMA结构设计,如下图(图片来自网络):为了缓解多核CPU在读取同一块内存时遇到的通道瓶颈问题,芯片工程师设计了一个NUMA结构,如下图所示(图片来自网络):这种架构可以很好的解决UMA的问题,即不同的CPU有专用的内存区域。为了实现CPU之间的“内存隔离”,需要软件层面的两个支持点:1.内存分配需要在请求线程的当前位置进行。分配CPU的专用内存区域。如果分配给其他CPU专用的内存区域,隔离性会受到一定影响,跨总线的内存访问性能必然会有一定程度的降低。2、另外,一旦本地内存(dedicatedmemory)不够用,先淘汰本地内存中的内存页,而不是去检查remote内存区是否有空闲内存借用。这样隔离确实比较好,但是也有一个问题:NUMA特性可能会导致CPU内存使用不均衡,有些CPU专用内存不够用,需要频繁回收,可能会造成大量掉期,系统响应延迟会很严重。摇。同时,CPU专用内存的其他部分可能处于空闲状态。这会产生一个奇怪的现象:使用free命令查看当前系统还有一些空闲的物理内存,但是系统一直在交换,导致某些应用程序的性能急剧下降。看叶锦荣老师的MySQL案例分析:《找到MySQL服务器发生SWAP罪魁祸首》。因此,对于小内存应用,NUMA带来的问题并不突出。相反,本地内存带来的性能提升是相当可观的。但是对于数据库这样的大内存用户来说,默认的NUMA策略带来的稳定性风险是难以接受的。因此,数据库强烈要求改进NUMA的默认策略。有两个方面可以改进:1.将内存分配策略从默认的affinity模式改为interleave模式,即内存页会分散分配到不同的CPUzone中。这样就可以解决内存分配不均的问题,一定程度上可以缓解上面案例中的怪问题。对于MongoDB,启动时会提示使用interleave内存分配策略:WARNING:YouarerunningonaNUMAmachine.Wesuggestlaunchingmongodlikethistoavoidperformanceproblems:numactl–interleave=allmongod[otheroptions]2.改进内存回收策略:这里终于要讲到今天第三个主角的参数zone_reclaim_mode,该参数定义了NUMA架构下不同的内存回收策略,可以取值0/1/3/4,其中0表示当本地内存不够用时,可以将内存分配到其他内存区域;1表示在本地内存不够时,本地先回收再分配;3表示本地回收尽可能先回收文件缓存对象;4表示本地回收首先使用swap回收匿名内存。可以看出,HBase推荐配置zone_reclaim_mode=0,一定程度上降低swap发生的概率。不全是swap至此,我们讨论了与swap相关的三个系统参数,并围绕Linux系统内存分配、swap、NUMA等知识点对这三个参数进行了深入解读。另外,对于数据库系统来说,有两个非常重要的参数需要特别注意:1.IO调度策略:这个话题网上有很多解释,这里不打算细说,只给出结果。通常对于sata盘的OLTP数据库,deadline算法调度策略是最好的选择。2.THP(Transparenthugepages)特性被关闭。笔者对THP的特性好奇了很久。疑点主要有两点。一是THP和HugePage是不是一回事,二是HBase为什么要求关闭THP。来回多次查阅相关文件,终于找到了一些端倪。这里有四个小点来说明一下THP的特点:(1)什么是HugePage?网上对HugePage的解释很多,大家可以搜索阅读。简单来说,计算机内存是通过表映射(内存索引表)寻址的。目前,系统内存以4KB为页,这是内存寻址的最小单位。随着内存的不断增加,内存索引表的大小也会不断增加。对于256G内存的机器,如果使用4KB的小页,光是索引表的大小就有4G左右。要知道这个索引表肯定是装在内存里的,是在CPU内存里的。如果太大,会出现大量未命中,内存寻址性能下降。HugePage就是为了解决这个问题。HugePage使用2MB的大页代替传统的小页来管理内存,这样可以将内存索引表的大小控制的很小,并且全部安装在CPU内存中,防止遗漏。(2)什么是THP(TransparentHugePages)?HugePage是大页理论,那么HugePage特性如何使用呢?目前系统提供了两种使用方式,一种叫做StaticHugePages,一种叫做TransparentHugePages。前者顾名思义是一种静态管理策略,需要用户根据系统内存大小手动配置巨页数量,这样系统启动时就会产生相应数量的巨页,以后不会更改。TransparentHugePages是一种动态管理策略,在运行时为应用程序动态分配大页面,并对这些大页面进行管理,对用户完全透明,不需要任何配置。此外,目前THP仅针对匿名内存区域。(3)为什么HBase(数据库)需要关闭THP特性?THP是一种动态管理策略,在运行时分配和管理大页面,所以会存在一定程度的分配延迟,这对于追求响应延迟的数据库系统来说是无法接受的。此外,THP还有许多其他缺点。可以参考这篇文章《why-tokudb-hates-transparent-hugepages》(4)THP关闭/开启对HBase读写性能有多大影响?为了验证THP开启和关闭对HBase性能的影响,我在测试环境做了一个简单的测试:测试集群只有一台RegionServer,测试负载为1:1的读写比。THP在某些系统中有always和never两个选项,在某些系统中有一个称为madvise的选项。您可以使用命令echonever/always>/sys/kernel/mm/transparent_hugepage/enabled来关闭/打开THP。测试结果如下图所示:如上图所示,在禁用TPH的情况下,(never)HBase的性能最好,相对稳定。在THP开启(一直)的场景下,性能比关闭THP的场景下降了30%左右,曲线抖动很大。可以看到记得在HBase上在线关闭THP。综上所述,任何数据库系统的性能都与很多因素有关,包括数据库本身的各种因素,如数据库配置、客户端使用、容量规划、表方案设计等。此外,基础系统的影响对它来说也很关键,比如操作系统,JVM等。很多时候数据库遇到一些性能问题,通过左右查看是无法定位到具体原因的。这时候就要检查一下操作系统的配置是否合理。本文从HBase官方文档需要的几个参数入手,对这些参数的具体含义进行了详细的解释。