淘宝搜索分析系统Pora2的应用之HBase高并发读写性能优化

来源:互联网 发布:李小璐的淘宝店铺 编辑:程序博客网 时间:2024/06/04 18:19
问题导读:

1、如何理解淘宝的搜索分析系统Pora?
2、redis超时的原因有哪些?






前言
淘宝搜索的个性化离线实时分析系统Pora已升级至Pora2,Pora2是在基于Yarn的流式计算框架IStream基础上开发的,同时为保证数据和消息的实时处理系统中较多地使用了HBase,是一个典型的高并发读写HBase的分布式应用。

系统在发布之初遇到了比较严重的性能问题,表现为处理速度跟不上实时日志,并且整个Hadoop/HBase集群压力大,连带其它应用受影响。经过排查发现问题主要都出现在了对HBase的使用上,现将遇到的几个典型的使用HBase的问题总结如下,希望能为其它类似应用正确使用HBase提供参考。


HBase的Periodic Flusher
从系统的各种统计指标分析,系统主要慢在了读写HBase的环节,观察HBase日志发现每个RegionServer都在频繁地flush和compact。分析后发现当前hbase版本里有一个Periodic Flusher的机制,memstore中的数据如果持续一段时间没有flush的话hbase会自动触发flush,这个时间间隔默认是1小时。了解了一下这是hbase 0.94.8以后引入的新feature,初衷是防止有些memstore长时间不flush,在没有开启wal且遇到region server挂掉时导致数据丢失。

由于我们的 hbase每个region server有将近100个region,几乎每分钟都有region因为达到一小时的时间间隔触发flush,而多数情况下每次flush的文件都很小,flush次数多了之后又会引起compaction,如此频繁的flush和compaction使得region server处理速度明显变慢。在我们将这个配置调整为10小时后,可以从下图中看到hbase flush queue size和fs pread latency都有明显变小。
 

 


注:个人觉得hbase的这个新feature应用场景很有限,实在不应该作为一个默认开启的配置,建议可以通过配置直接禁用。​


不间断的频繁scan对region server造成较大压力
​​Pora2采用了一个基于HBase实现的消息队列Hqueue,下游使用方通过读这个消息队列,第一时间获取最新的消息进行处理。

读消息的过程相当于一次Scan,在一开始的Pora2版本中我们未对读Hqueue的频率进行控制,导致某些读HQueue的worker不停地发起新的Scan,即便Hqueue数据已经读完,仍会立刻重新创建Scan,而这个Scan会因为读不到数据很快结束之后又重新创建。这样不停地新建Scanner对Region Server端构成了不小的且是不必要的压力。

发现这个问题后我们修改了这部分程序代码,在读完数据后sleep几秒后再重新scan。

​在修改完hbase的配置并且加上读Hqueue的scan频率控制后,Pora2情况明显好转,但处理累积数据时的速度还是不够快。


​超大并发下的hbase问题
从统计指标上分析,系统还是慢在访问hbase上,访问hbase的日志中不时出现各种TimeOutException也可以说明这一点。跟踪日志中某些频繁报超时的regionserver发现,有些region server日志持续报以下异常:

所在机器连接数很多,但load很低,而一旦pora2停了,这些现象就会很快消失。

从这一现象分析判断是Pora2对HBase的并发连接数太多了,使得region server的handler不够用,server端还没来得及处理请求,client端已经到了超时时间而断开。

为此我们大幅度减少了访问hbase的进程数以减少对hbase的并发连接,为了不降低处理能力,在进程内部使用更多的处理线程。由于线程间是共享对hbase的连接的,所以增加线程数不会增加对hbase的连接数。

通过这一调整,很大程度缓解了hbase region server的压力。


避免HBase访问热点
​在作了较多优化改进后发现仍有几个worker比较慢,跟踪那几个慢的worker日志发现读HBase经常超时,找到超时的region …


redis超时问题分析
Redis在分布式应用中占据着越来越重要的地位,短短的几万行代码,实现了一个高性能的数据存储服务。最近dump中心的cm8集群出现过几次redis超时的情况,但是查看redis机器的相关内存都没有发现内存不够,或者内存发生交换的情况,查看redis源码之后,发现在某些情况下redis会出现超时的状况,相关细节如下。

1. 网络。Redis的处理与网络息息相关,如果网络出现闪断则容易发生redis超时的状况。如果出现这种状况首先应查看redis机器网络带宽信息,判断是否有闪断情况发生。
2. 内存。redis所有的数据都放在内存里,当物理内存不够时,linux os会使用swap内存,导致内存交换发生,这时如果有redis调用命令就会产生redis超时。这里可以通过调整/proc/sys/vm/swappiness参数,来设置物理内存使用超过多少就会进行swap。


另外还有一些特殊情况也会导致swap发生。当我们使用rdb做为redis集群持久化时可能会发生物理内存不够的情况(aof持久化只是保持支持不断的追加redis集群变化操作,不太容易引起swap)。当使用rdb持久化时,如程序1所示主进程会fork一个子进程去dump redis中所有的数据,主进程依然为客户端服务。此时主进程和子进程共享同一块内存区域, linux内核采用写时复制来保证数据的安全性。在这种模式下如果客户端发来写请求,内核将该页赋值到一个新的页面上并标记为写,在将写请求写入该页面。因此,在rdb持久化时,如果有其他请求,那么redis会使用更多的内存,更容易发生swap,因此在可以快速恢复的场景下尽量少使用rdb持久化可以将rdb dump的条件设的苛刻一点,当然也可以选择aof,但是aof也有他自身的缺点。另外也可以使用2.6以后的主从结构,将读写分离,这样不会出现server进程上又读又写的情景发生 3. Redis单进程处理命令。Redis支持udp和tcp两种连接,redis客户端向redis服务器发送包含redis命令的信息,redis服务器收到信息后解析命令后执行相应的操作,redis处理命令是串行的具体流程如下。首先服务端建立连接如程序2所示,在创建socket,bind,listen后返回文件描述符:


对于redis这种服务来说,它需要处理成千上万个连接(最高达到655350),需要使用多路复用来处理多个连接。这里redis提供了epoll,select, kqueue来实现,这里在默认使用epoll(ae.c)。拿到listen函数返回的文件描述符fd后,redis将fd和其处理acceptTcpHandler函数加入到事件驱动的链表中.实际上在加入事件队列中,程序4事件驱动程序将套接字相关的fd文件描述符加入到epoll的监听事件中。


在初始话完所有事件驱动后,如程序5所示主进程根据numevents = aeApiPoll(eventLoop, tvp)获得io就绪的文件描述符和其对应的处理程序,并对fd进行处理。大致流程是accept()->createclient()->readQueryFromClient()。其中readQueryFromClient()读取信息中的redis命令-> processInputBuffer()->call()最后完成命令。

从上述代码可以看出redis利用ae事件驱动结合epoll多路复用实现了串行式的命令处理。所以一些慢命令例如sort,hgetall,union,mget都会使得单命令处理时间较长,容易引起后续命令time out.所以我们第一需要从业务上尽量避免使用慢命令,如将hash格式改为kv自行解析,第二增加redis实例个数,每个redis服务器调用尽量少的慢命令。…
0 0
原创粉丝点击