- 1、本文档共10页,可阅读全部内容。
- 2、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
- 3、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 4、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 5、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 6、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 7、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 8、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
队列二级取模方案的数学陷阱
及其优化的理论和实践
山西项目组 张大朋 2010/8/1
摘要
由于电信业务的特点,电信类软件系统所面临的任务压力都比较大。为了能够快速处理大量任务,通常会采用多进程+多线程的方式来进行并发处理。这时候对各进程和各线程的任务分配是必不可少的工作,采用取模的方法对原始任务进行分解处理是简单可行的一种方案。利用这种方法将原始任务序列进行1次取模任务分配时,我们可以很容易确定分配结果是均匀公平的。由于人类固有的思维惯性,大部分人会把这种均匀性分配的认识推广到2次取模分配上,然而事实并非如此,这里存在一个巨大的陷阱,而这个陷阱和分配的进程数与线程数存在着确定的数学关系。本文在通过对这些数量关系研究的基础上,给出了一些推论,并给出了自己的推导证明,希望这些推论可以让我们在日后timer优化及相似事情的处理上避开一些逻辑陷阱。
事件背景
在日常的维护工作中,发现电信业务存在一个典型的特点:每个月的月初和月末的几天里,是业务受理高峰,这比平时要高出很多。系统中流动的业务数据量也在这几天内急速飙升至最高值,并经常刷新纪录。因此这些时段也最能考验我们的软件系统的受压能力,而大部分情况下,我们系统中负责业务处理的timer都会瘫痪掉,造成系统大量压单,我们最繁忙的工作就是不断重起这些timer应用程序。本月月末,依然未能幸免,状态机timer压单严重。我们保持5个进程不变,把每个进程的线程数由原来的7个提高到10个,期望通过提高并发处理能力来缓解业务压力。但是第2天,我们并未看到预期的效果,而状态机timer压单量已经飙升至15000,成为历史最高点。这时候,项目经理在对timer滚动输出的日志中敏锐的发现到系统中存在好多线程在空跑,进一步检查数据库心跳,发现果然很多线程在执行空的循环,在巨大的任务压力面前居然还有线程不干活,真是可恶。项目经理立刻意识到是昨天调整线程数造成的,当把这个问题提出来后,我们感觉到线程数10这个数字存在问题,凭直觉建议改用素数,于是我们把线程数从10调成13,结果发现所有线程都在工作了,在对数据的监控中,我们也感觉到了状态机处理速度在加快。
初步分析
为了后续说明的方便,这里对状态机timer的任务分配机制进行简单的介绍。状态机Timer的主要任务是对业务定单对应流程实例的状态的转换,以驱动流程流转。在一个定单的流程实例生成时,流程实例的主键proc_inst_id由数据库sequence生成,作为流程实例的唯一标识。同时会将该主键对状态机timer的进程数取模,取模结果记入流程实例的subarea_no字段,作为将来状态机timer进程任务分配的依据。进程从0开始编号,n个进程,编号依次为0、1、2、… n-1,0号进程只处理subarea_no为0的流程实例,依此类推。每个进程分配到相应的任务数据后,会根据配置文件中的线程数参数,并发出m个线程来分摊处理这些任务数据,每个线程拥有一个线程号作为自身标示,线程号从0开始,一直到m-1,线程的任务分配是在每个线程提取数据时完成的,线程在提取数据时,同样拿proc_inst_id对线程数取模,每个线程只处理余数等于自身线程号的那批数据。
现在我们来分析一下前面说的5个进程、10个线程的组合为什么会导致很多线程为空,是偶然还是必然?
前面说过,状态机处理的目标是一堆流程实例记录,而流程实例可以由主键proc_inst_id唯一标示,我们不妨将这一堆任务抽象为一堆由proc_inst_id组成的数字序列,序列中的每个数字标识其对应的任务。现在我们给每个进程分配任务,不妨假设有z个数据、n个进程、每个进程对应m个线程,现在n=5,m=10。由于proc_inst_id是由数据库sequence生成,因此它是一个步长为1的等差数列,考虑到各种因素的干扰,导致最后状态机每次提取到的数据并不严格是一个等差数列,但是从宏观上看,不影响我们这里等差数列的假设,因为大部分情况下是。为了演示的方便,我们把这个数列向前平移,变为一个初值为1、步长为1的等差数列,显然这个动作也不会影响我们的取模分配。那么我们开始给进程分配任务,将这个等差数列对n=5取模,因为步长为1,所以每个proc_inst_id取模结果必然顺次落到0、1、2、3、4上,这样每个proc_inst_id就归属于对应的0、1、2、3、4号进程的任务队列里,为了演示方便,我们取z=100来看看进程任务分配的结果:
[0]: 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100
[1]: 1 6 11 16 21 26 31 36 41 46 51 56 61 66 71 76 81 86 91 96
[2]: 2 7 12 17 22
文档评论(0)