记一次服务优化的历程

开发一个服务是简单的,让服务在不可靠和有限资源环境下稳定的运行需要不断的设计优化.

场景描述:起源于一流量统计的功能,需要实时或者准实时的计算流数据中每分钟的特定事件类型的数量并存储到关系型数据库中.流数据的处理经由SparkStreaming程序进行处理.

第一版:也是最初的版本,采用Spark Streaming的窗口特性每隔1分钟执行一次批量的计数操作并将结果更新到关系型数据库中,在数据量小的情况下能正常工作,但由于流数据可能存在延迟以及系统内部的缓存,每一分钟的数据被分散到几分钟甚至几小时范围内,曾一度出现因写数据库延迟而造成的服务积压,延迟持续增加.

    总结:[按批统计+同步Oracle行级累加]

第二版:仍然使用SparkStreaming应用程序每分钟执行一次统计操作,但是每次将最新的流量数据缓存到Redis中,每次统计完成后先对Redis中缓存的数据进行累加操作,统计数据有变化的同步到Oracle中。较方案一写入性能有改进,数据量不大的场景下能稳定运行,在数据量较大的场景下由于Spark RDD 算子执行分布式技术的延迟+操作Redis延迟+写入数据库的延迟急剧增加,由分布式计算消耗的CPU、内存资源也急剧增加,且由于SparkStreaming程序的滑动窗口会缓存窗口大小的数据对内存消耗也在增加,此时需要一种更加轻量级的统计模式.

    总结:[按批统计+统计结果缓存+Oracle行级累加]

第三版:不再依赖Rdd的分组计数等操作,借助Redis内存数据库的高性能写入和原子递增等特性,直接在Redis中完成计数操作[+1操作],Redis中统计值的清理设置指定的过期时间启动清理,开发统计值同步服务按指定周期将Redis中最新的统计信息刷新到Oracle表,因Redis中统计的是总记录数,同步服务每次都执行更新操作,记录不存在的执行插入操作.优点是简化流量统计的方式,CPU、内存的占用都有减少,同事使用Redis隔离了在分布式环境下直接操作Oracle数据库,提高了计算服务的可靠性和稳定性。同步服务只负责将统计信息刷回Oracle表.该方案下统计计算能支持大量数据的实时统计,受限于Redis的读写性能.

    总结:[分布式环境下使用Redis进行计数+以指定频率向Oracle表同步最新统计值]

第四版:由于第三版设计上的缺陷,交付后由于redis中大量的统计信息要被刷新到Oracle中,导致写入过程出现严重的延迟和积压,最终导致同步服务宕机,通过在同步服务加一层本地缓存来处理,在本地缓存一份上一次Redis中流量数据的镜像,每次获取到新的统计信息先和本地缓存进行比较统计只有变换的部分向Oracle表中执行更新操作,不存在的执行插入操作.改进后将每次操作Oracle表的并发数控制在一定范围,配合同步周期使服务稳定运行.

    总结:[Redis分布式计数+本地缓存+同步变换部分到Oracle表]

数据流:

记一次服务优化的历程