性能优化手记下篇之【计费】
本文作者从事运营商计费系统工作 18 年,历经计费 2.8 和 BSS3.0,参与过计费 2.8、BSS3.0 等集团规范编制和入网测试,参与了多个运营商省级计费系统建设。
随着运营商 5G 商用规模的不断扩大,5G 用户比例不断提高,计费系统话单数成几何倍数快速增长。如果计费话单处理性能不能同步提升,涉及大量话单处理的业务场景都会出现严重问题。
计费应用无法及时处理高峰期的话单,导致话单大量积压,影响流量提醒及时性,引发大面积用户投诉。
当计费系统发生重大故障,系统恢复时间较长时,将会积压大量话单。此时就需要强大的计费话单处理能力来进行追单处理,否则如果临近月底话单还没跑完,将会影响出帐,后果不堪设想。
计费系统对帐时,如果想对全省一个月的话单进行新老系统对帐,那么对帐人员需要耗费大量的时间在等待跑帐。按月话单数 300 亿估算,如果话单处理能力只有 6w tps,那么需要耗费将近 6 天时间才能跑完,不具备重新跑帐的时间。
导致性能问题的业务原因很明确,所产生问题的业务后果也明确,解决问题的紧迫性很强烈,本文就笔者近期在某项目实际优化工作中的探索总结进行分享。
问题分析
性能调优,实际上就是不断地针对遇到的性能问题,寻找解决方案并获得突破,最终达成理想的优化效果。计费话单处理能力的性能问题到底在哪里呢?
我们在反复压测和调优过程中,将遇到的性能问题分为三类:部署架构问题、业务流程问题和应用逻辑问题。
01 首先,我们来分析部署架构问题
众所周知,新一代的计费系统一般都采用“平台+应用”的全新 IT 架构,在部署上应用和数据一般是分离的,优化前普遍采用的部署架构如下:
应用环节间采用消息中间件进行话单流转,采预应用按用户取模把话单分发给批价应用,批价应用按用户关联分组取模分发给实时优惠应用,实时优惠应用按用户取模分发给提醒应用。
采预应用、批价应用、提醒应用跨主机远程访问分布式 MDB 集群的客户资料。实时优惠应用每个应用容器部署一套 MDB 本地高速缓存,用于预加载客户资料,实时优惠应用访问应用容器内的本地高速缓存中的客户资料。
这样的部署架构粗看没有问题,但是仔细分析后,存在三个致命的问题:客户资料访问问题、累积量扣减锁冲突问题和 MQ 流量压力问题。
客户资料访问问题
1)对于采预应用、批价应用和提醒应用,由于都是跨主机远程访问分布式 MDB 集群的客户资料,网络交互耗时占比特别高。以批价应用为例,处理一条话单总耗时大概是 10 毫秒,其中查找客户资料耗时占了 7 毫秒(需要查找多张表,且一张表需要查找多次),查找客户资料的网络交互耗时占了 5 毫秒。
2)对于实时优惠应用,虽然预加载客户资料到本地高速缓存,但是由于每个应用容器都需要加载一份客户资料,内存占用巨大,导致只能加载有限的客户资料。
累积量扣减锁冲突问题
1)对于套餐共享累积量,一个套餐有多个用户,不同用户的话单如果在批价环节多进程并发处理,会导致不同进程同时扣减同一条累积量,导致批价扣减累积量的 mdb 锁冲突,严重影响批价的并发处理性能。
2)由于采预应用给批价应用是按用户取模分发的,所以对于共享套餐用户话单,在批价环节并发处理的概率还是比较高的,特别是话单积压场景下,这种并发冲突尤其严重。
MQ 流量压力问题
1)计费流程环节间采用消息中间件 MQ 进行话单流转,每个环节需要输出话单到 MQ,同时需要输出上传集团的信息点记录到 MQ,这两块对 MQ 的压力都是巨大的。
2)根据某省准生产环境实际压力测试数据,各应用主机占用网络带宽分别如下:采预应用主机为 12Gb/s,批价应用主机为 18Gb/s,实时优惠应用主机为 17Gb/s,提醒应用主机为 3Gb/s,各应用主机争抢网络带宽资源,导致所有应用并发时,总体性能上不去。
02 其次,我们来分析业务流程问题
新一代的计费业务流程基本上继承了计费专业的传统业务流程,主要如下:
1、当前的提醒流程,提醒应用通过合帐应用触发,并在提醒应用内部再更新一份累积量数据。
2、这种方式可以提高提醒应用的独立性,但是同时带来了一些比较明显的缺陷,主要如下:
由于提醒应用单独维护的一份累积量数据,这份数据和批价应用产生的累积量数据由于时间差的原因,可能不完全一致。同时提醒应用单独维护一份累积量数据,对提醒应用的存储资源和应用性能也造成了一定的负面影响。
由于帐户优惠并入合帐中进行实时优惠处理,导致对于大帐户优惠用户,流量提醒端到端总时长大大加长,无法达到集团 1 分钟提醒的要求。
03 最后,我们来分析应用逻辑问题
由于批价环节引入了产品累积量的应用逻辑,导致合帐应用通知提醒应用的消息量暴增,是正常话单量的 3 倍,提醒应用的性能处理压力是批价的 3 倍。
合帐应用处理限额哈希时,代码逻辑存在如下问题:申请 5000 条固定大小的哈希,按哈希大小循环遍历哈希,反复对无效数据进行内存清理,导致合帐 CPU 消耗特别高,占用大量 CPU 资源,同时跟批价竞争 CPU 资源,严重影响批价性能。
想要提升计费话单处理能力,则必须针对性的解决以上的部署架构问题、业务流程问题和应用逻辑问题。至此,性能问题的原因也逐渐清晰。
问题解决
01 部署架构问题
想要解决部署架构问题,重点是解决客户资料访问问题、累积量扣减锁冲突问题、MQ 流量压力问题。
通过 Daemonset 容器高速缓存,解决客户资料访问问题
计费应用在业务处理过程中,经常需要访问客户资料,如产品实例、销售品实例等。这些客户资料数据量非常大,放在 MDB 中供业务应用查询。
MDB 作为独立的服务集群,和业务应用集群部署在不同的主机上,跨主机的网络时延对资料的高频访问性能影响较大。但是,如果每个应用容器都部署一套 MDB 本地高速缓存,内存占用巨大,导致只能加载有限的客户资料。
如果我们在每个业务应用主机单独部署一个 Daemonset 容器,用于部署 MDB 高速缓存,业务应用支持跨容器访问同主机共享内存中的客户资料,这样既可以避免跨主机访问客户资料的网络耗时大的问题,又可以解决每个容器都部署一套高速缓存的内存占用大的问题。
通过 Daemonset 容器高速缓存,我们可以保存访问比较频繁的几张表的全省客户资料数据,业务应用跨容器通过 IPC 访问 MDB 本地高速缓存,从而提高客户资料查询效率,降低网络时延,如下图所示:
通过分布式用户分组技术,解决累积量扣减锁冲突问题
前面我们提到了,应用多进程并发处理存在数据库锁冲突,总体性能上不去。以批价为例,目前按用户维度进行分发,对于套餐共享累积量,不同的批价进程存在并发扣减同一条累积量,此时会导致数据库锁冲突,影响批价的总体处理性能。
运营商业务中的用户不是一个完全独立的个体,而是存在复杂的业务关联性,比如通过套餐关联、帐户关联、客户关联,这几种关系甚至可以多层嵌套关联。如果业务应用的并发没有基于用户的业务关联关系,很容易导致数据库锁冲突。
分布式用户分组技术,可以基于业务规则的动态分析,通过关联分组算法对用户进行关联关系管理。业务应用可以基于用户关联关系实现并行处理,且不会引发并发数据库锁冲突问题。同时,由于关联用户在一个进程处理,如果关联用户的当前环节处理完成后,可以直接进入下一个环节,不用等待其它非关联用户处理完成,整个计费流程可以实现横向扩展。
通过分布式用户分组技术将客户资料进行拆解细分之后,批价、实时优惠、提醒等应用按分布式用户分组进行话单分发,如下图所示:
批价、实时优惠、提醒等业务应用按用户关联分组进行话单事件分发,不同的进程实例处理不同的关联分组用户话单,同一个关联分组用户话单只会分发给一个应用进程实例处理,所以不同应用进程实例之间处理的话单就不再有关联关系,也就不会再有数据库操作的锁冲突,并发处理性能可以得到极大的提升。
通过 MQ 压缩技术,解决 MQ 流量压力问题
MQ 压缩技术,即对环节间交互的话单事件批量打包后,采用 zlib 库压缩数据后写入 MQ,可压缩到原始数据的 1/10~1/20,详细如下图:
这里 MQ 压缩的效果主要取决于批量话单打包的效果,如果只有少量的话单事件打包成一个消息包,则 MQ 的压缩效果就会比较差;反之,如果较多的话单事件打包成一个消息包,则 MQ 的压缩效果就会比较好。
计费操作 MQ 的网络流量主要来自两个方面,一方面是计费流程的环节间交互话单通过 MQ 进行流转,另一方面是计费流程需要吐出话单级信息点上传集团。
(一)首先,我们来看计费流程的环节间交互话单。实际上计费流程每个环节都是批量话单处理的,如果能保证从 MQ 中的一个消息队列获取一批话单事件,业务处理完成后分发给下游环节时,还是分发到同一个消息队列,则此时这批话单事件将会打包成同一个消息包,那么这个消息包包含的话单事件将会最多,MQ 压缩的效果将会达到最佳;反之,如果从一个消息队列获取的一批话单事件,被打散分发到下游环节的多个消息队列,则这批话单事件将会打包成多个消息包,那么每个消息包包含的话单事件将会比较少,MQ 的压缩效果也会比较差。
所以我们想要获得最佳的 MQ 压缩效果,需要尽可能保证上下游环节的分发规则保持一致,则可确保从消息队列获取的一批话单事件,给下游环节分发时,不会被打散分发到多个消息队列。
(二)其次,我们来看计费流程需要吐出的话单级信息点给 MQ 带来的网络流量压力。这里的话单级信息点是所有环节都需要输出的,而每个信息点本身的记录长度比较小,但是信息点记录数和话单数本身是一样多的,所以如果压缩效果不好的话,产生的网络流量将是很可怕的。
这里的话单级信息点是为了生成文件上传集团的,是由计费流程各业务应用模块分发给信息点文件生成模块的,对于分发规则本身没有特别要求,原来是按话单标识分发的。按话单标识分发均衡性没有问题,但是会导致从计费流程各应用模块的一个消息队列获取的一批话单事件,会被打散分发到信息点文件生成模块的所有消息队列,就会导致一个消息包包含的话单事件很少,MQ 的压缩效果大打折扣。
为了提高 MQ 压缩效果,我们调整了计费流程各业务应用模块给信息点文件生成模块的分发规则,改为按业务应用的应用实例标识分发,这样子从计费流程各应用模块的一个消息队列获取的一批话单事件,就会分发到信息点文件生成模块的一个消息队列中,从而保证这一批话单事件生成的信息点记录打包成一个消息包,提升 MQ 的压缩效果,极大的降低了 MQ 网络流量压力。
02 业务流程问题
首先,我们回顾下前面提到的业务流程问题:
1)由于提醒应用单独维护的一份累积量数据,这份数据和批价产生的累积量数据由于时间差的原因,可能不完全一致。同时提醒应用单独维护一份累积量数据,对提醒应用的存储资源和应用性能也造成了一定的负面影响。
2) 由于帐户优惠并入合帐中进行实时优惠处理,导致对于大帐户优惠用户,流量提醒端到端总时长大大加长,无法达到集团 1 分钟提醒的要求。
为了解决以上两个问题,我们需要打通批价应用到提醒应用的流程,减少消息中转流程,避免额外的累积量数据冗余,保证计费系统内部各模块使用的累积量数据的一致性,从而提高整个提醒流程的处理效率,降低硬件资源的消耗。
详细方案如下图:
原来由合帐应用通知提醒应用,调整为由批价应用直接通知提醒应用,减少流量提醒途经环节数,提高提醒及时性。
原来提醒应用需要自己再更新一份累积量数据,现在直接使用批价的累积量数据进行提醒判断,减少资源消耗,提升提醒应用处理性能。
02 应用逻辑问题
应用逻辑问题主要如下:
由于批价环节引入了产品累积量的应用逻辑,导致合帐应用通知提醒应用的消息量暴增,是正常话单量的 3 倍,提醒应用的性能处理压力是批价的 3 倍。
合帐处理限额哈希时,代码逻辑按固定哈希大小删除无效数据的问题,导致合帐 CPU 消耗特别高,占用大量 CPU 资源,同时跟批价竞争 CPU 资源,严重影响批价性能。
首先,针对提醒应用消息暴增问题,我们通过对通知提醒应用的话单事件按销售品实例、累积量类型和用户的维度进行合并,来降低提醒应用的实际处理话单量,从而解决提醒应用消息量暴增带来的性能压力;
其次,针对合帐 CPU 高问题,我们通过删除一些不必要的处理逻辑来降低合帐的 CPU 消耗,即增加判断逻辑,如果是无效数据,则不需要执行删除操作。
除了以上两点业务逻辑优化外,我们还针对每个业务模块进行了分析,做了很多零散的改造优化,应用逻辑优化可以归纳为以下几个策略:
应用缓存:通过缓存资料、量本等数据,减少应用远程访问 MDB 和数据库次数。
逻辑优化:通过废弃冗余逻辑,优化 SQL 语法、减少零费用话单处理等逻辑优化提升性能。
话单归并:通过对批次内同样销售品实例、用户和累积量类型的话单事件进行合并处理,减少提醒应用触发量。
性能优化结果
经过前面几个性能问题的解决,计费话单处理能力得到了一个质的飞跃,不仅突破了原来的性能天花板,而且达到了 20 万条话单/秒的高性能,这个性能远超集团对于中等省份 12 万条话单/秒的要求。
以下是在某省准生产环境实际压测的性能数据:
从以上数据可以看出,除了清单入库外,其他业务应用模块均已经达到甚至超过了 20 万条话单/秒的性能,清单入库主要由于数据库本身对于自增系列的处理性能达不到 20 万条话单/秒的要求。
随后,在其他项目的优化过程中,我们进一步对清单入库进行了优化,把自增序列改造为业务应用自己获取,不依赖于数据库的自增序列,清单入库性能从原来单进程 500 条话单/秒,提升至 2000 条话单/秒,性能提升 4 倍,总体性能远超 20 万条话单/秒。
性能问题持续永恒,性能优化永无止境。
版权声明: 本文为 InfoQ 作者【鲸品堂】的原创文章。
原文链接:【http://xie.infoq.cn/article/7e471be7af32bd8e2b63a6f59】。文章转载请联系作者。
评论