由于本来未打算写博客记录调试点点滴滴,所有前面的几个版本都没有做数据记录与运行时状态截图,只能大致的说下系统的改进流程。
1.2中,我将tcc相关的存储由mysql迁移至redis,同时将可靠消息系统的存储也由mysql迁移至了redis,同时引入了mycat做数据库的集成管理,方便今后的数据库扩容。
期间遇到了很多问题,例如随着qps的升高,account表与point表所在的数据库cpu使用量极高,同时单个支付需要花费的时间不断上升,经确认是应为系统中只有五个账户,导致所有的支付都在这些账户上展开,形成了极高的行锁锁竞争,大量请求处于锁等待状态。由于原系统的设计理念就是处理低频账户,所以,我将账户数量增加到了1000,并随机对账户进行订单支付。此时,核心支付的qps上升至80左右。此时的数据库配置为,account,point,order都运行与独立的服务器上。系统请求量达到80以后,数据库设计的瓶颈变为order表,order库所在的数据库出现cpu消耗上升至1000%以上的情况,原因是大量的读写请求同时操作rp_trade_payment_order与rp_trade_payment_record表,解决方案为做数据库主从。在对order库所在的表做完读写分离后,核心支付qps达到120左右,系统的瓶颈仍为order库,在接下来的1.3中将对order库做水平拆分。
在接下来的版本中,每个版本我都会提供详细的系统运行拓扑与运行结果分析。
1.3.1版本,改进了bank队列的消费模式,不会再出现先前版本的bank消息被消费,但大量任务堆积在app-queue组件中的情况,bank队列消息的堆积情况将清楚的显示在activemq的主页面。
以下是order库未做水平拆分时的测试结果:
拓扑结构如下:
其中10.8.59.176和10.8.59.200为HP gen8服务器(12cpu)
10.8.59.208与10.8.59.168位HP gen9服务器(16cpu)
其余都为openstack虚拟出的虚拟机(4cpu,4GB),
应用与数据库部署情况为
10.8.59.176部署point库
10.8.59.200部署account库
10.8.59.208与10.8.59.168部署order库,互为主从(mycat配置中208在上,208为写库,168为读库)
10.8.59.238部署base库
10.8.59.240部署accounting库
10.8.59.239部署message库
10.8.59.241部署zookeeper,
10.8.59.234部署mq,
10.8.59.242与10.8.59.243部署task,
10.8.59.231与10.8.59.244部署service,
10.8.59.248与10.8.59.233部署app(前台war包),
10.8.59.246部署redis,
10.8.59.230部署mycat
以下是一些运行时图片
运行后的数据链接:链接:https://pan.baidu.com/s/1AS4b1LVoSwm9fvgkwq6AaQ 密码:fhug
可以分析数据可以看出qps最高时达140,最低时为90左右,波动较大,而分析图中的cpu消耗可以看出point库与account库中锁竞争非常激烈。接着我在service-trade的log中找了两条订单的处理情况:
[INFO ][20180422 17:43:28,294][RpTradePaymentManagerServiceImpl:205] 接收到支付结果{result_code=SUCCESS, time_end=20180422054235, messageId=f66e468162c34ad9b139f5d57cccb870, out_trade_no=822018042217422610026, payWayCode=TEST_PAY_HTTP_CLIENT, transaction_id=20180422054235}
[INFO ][20180422 17:43:28,294][RpTradePaymentManagerServiceImpl:209] ------[接收到要处理的订单822018042217422610026]--------[开始处理时间2018-04-22 17:43:28 294]------
[INFO ][20180422 17:43:28,312][RpTradePaymentManagerServiceImpl:281] ==>开始处理支付成功的订单结果822018042217422610026
[INFO ][20180422 17:43:28,326][RpTradePaymentManagerServiceImpl:284] ==>保存消息数据822018042217422610026
[INFO ][20180422 17:43:28,333][RpTradePaymentManagerBizImpl:61] ------completeSuccessOrder[订单822018042217422610026完成支付TRYING阶段开始时间2018-04-22 17:43:28 333]------
[INFO ][20180422 17:43:28,351][RpTradePaymentManagerBizImpl:70] ==>Record update完成[订单822018042217422610026]
[INFO ][20180422 17:43:28,396][RpTradePaymentManagerBizImpl:79] ==>修改订单账户金额[订单822018042217422610026]
[INFO ][20180422 17:43:28,498][RpTradePaymentManagerBizImpl:81] ==>修改订单账户金额完成[订单822018042217422610026]
[INFO ][20180422 17:43:28,498][RpTradePaymentManagerBizImpl:84] ==>修改账户积分开始[订单822018042217422610026]
[INFO ][20180422 17:43:28,577][RpTradePaymentManagerBizImpl:87] ------completeSuccessOrder[订单822018042217422610026完成支付TRYING阶段结束时间2018-04-22 17:43:28 577]------
[INFO ][20180422 17:43:28,582][RpTradePaymentManagerBizImpl:93] ------confirmCompleteSuccessOrder[订单822018042217422610026完成支付CONFIRMING阶段开始时间2018-04-22 17:43:28 582]------
[INFO ][20180422 17:43:28,624][RpTradePaymentManagerBizImpl:107] ------confirmCompleteSuccessOrder[订单822018042217422610026完成支付CONFIRMING阶段结束时间2018-04-22 17:43:28 624]------
[INFO ][20180422 17:43:28,812][RpTradePaymentManagerServiceImpl:296] ==>发消息前:822018042217422610026
[INFO ][20180422 17:43:28,361][RpTradePaymentManagerServiceImpl:205] 接收到支付结果{result_code=SUCCESS, time_end=20180422054235, messageId=6ffa1cfb021449bb82c5b22e2827dc2c, out_trade_no=782018042217422610027, payWayCode=TEST_PAY_HTTP_CLIENT, transaction_id=20180422054235}
[INFO ][20180422 17:43:28,361][RpTradePaymentManagerServiceImpl:209] ------[接收到要处理的订单782018042217422610027]--------[开始处理时间2018-04-22 17:43:28 361]------
[INFO ][20180422 17:43:28,383][RpTradePaymentManagerServiceImpl:281] ==>开始处理支付成功的订单结果782018042217422610027
[INFO ][20180422 17:43:28,399][RpTradePaymentManagerServiceImpl:284] ==>保存消息数据782018042217422610027
[INFO ][20180422 17:43:28,406][RpTradePaymentManagerBizImpl:61] ------completeSuccessOrder[订单782018042217422610027完成支付TRYING阶段开始时间2018-04-22 17:43:28 406]------
[INFO ][20180422 17:43:28,431][RpTradePaymentManagerBizImpl:70] ==>Record update完成[订单782018042217422610027]
[INFO ][20180422 17:43:28,469][RpTradePaymentManagerBizImpl:79] ==>修改订单账户金额[订单782018042217422610027]
[INFO ][20180422 17:43:28,561][RpTradePaymentManagerBizImpl:81] ==>修改订单账户金额完成[订单782018042217422610027]
[INFO ][20180422 17:43:28,561][RpTradePaymentManagerBizImpl:84] ==>修改账户积分开始[订单782018042217422610027]
[INFO ][20180422 17:43:28,620][RpTradePaymentManagerBizImpl:87] ------completeSuccessOrder[订单782018042217422610027完成支付TRYING阶段结束时间2018-04-22 17:43:28 620]------
[INFO ][20180422 17:43:28,625][RpTradePaymentManagerBizImpl:93] ------confirmCompleteSuccessOrder[订单782018042217422610027完成支付CONFIRMING阶段开始时间2018-04-22 17:43:28 625]------
[INFO ][20180422 17:43:28,680][RpTradePaymentManagerBizImpl:107] ------confirmCompleteSuccessOrder[订单782018042217422610027完成支付CONFIRMING阶段结束时间2018-04-22 17:43:28 680]------
[INFO ][20180422 17:43:29,176][RpTradePaymentManagerServiceImpl:296] ==>发消息前:782018042217422610027
可以看出tcc阶段调用point与account进行trying或者comfirm处理时花费了非常长的时间,因此需要解决行锁竞争的问题,一个是将账户数继续扩大,避免同时操作一个账户导致锁竞争,一种是锁控制,任一时刻同一个账户只能被一个线程访问,同一账户的后到请求需等上一个该账户的支付请求完成后才能开始。
我在这里将使用第二种方案(这个无论如何是要做的,早点做了吧),用redis实现分布式锁,在app-queue模块中运行bank task时就根据账户号去获取锁,可较好的分担数据库端的压力(也可以在数据库端实现同样功能的锁,但是这个比较难实现,需要修改数据库源码),1.3.2将实现该功能。