热点账户问题和常用解决方案【中】

话接上回,上篇阐述了什么是热点账户,基本财务账户如何设计,幂等健和链式设计!本篇将针对热点账户在实践中引发的问题,梳理和拆解业务流,分析问题点,提出七种常用解决方案。一、性能问题初现上线初期数据量较小,运行正常!一次大促后,账户流水的总数目接近亿级别,初现性能问题:系统整体的qps也就10+,但热点账户写入失败率偏高,并且随数据量增加失败率逐步升高;整个账户系统全靠上游有redo标识位不断重试,才能保证最终写入成功!哈哈,作为一名拥有三年工作经验的老码农,面对问题,要做的第一件事,就是静,抽根烟静静,准备开搞!二、数据流拆解拿到问题,抽根烟静一下之后,分析问题需要三步:梳理数据流,拆解过程,定位问题点。先对财务账户更新的数据流进行拆解链式锁后的基本账户操作过程,分为如下五阶段请求阶段:账户操作请求。查询阶段:查询当前账户信息。主要获取当前链,资金数据等!计算阶段:对链和操作的资金进行计算,判定资金操作合规,同时保证幂等和并发!写入阶段:事务同步写入流水和余额响应阶段:告知上游回调三、链路分析梳理数据流后,接下来分析每个阶段可能引发的问题。按照优先级,先分析业务问题区域(读取阶段,计算阶段,写入阶段),通常问题会出现在业务阶段;之后,再分析框架问题区域(请求阶段和回调阶段),该区域出现问题的情况偏小,但是一旦出现问题,就是比较有意思^^!3.1 业务问题区域分析读取阶段,计算阶段,写入阶段三个阶段是具体的业务操作,从并发和耗时两个角度来分析下可能的问题点3.2.1 耗时分析耗时分为三块查询耗时:RDS拥有亿级别数据量,查询未中primary,但命中索引,业务数据体并未完全在索引中,因此访问数据走index match;数据主键聚簇,唯一健索引查询获取数据,page极难命中cache,也不会命中磁盘电梯算法优化!结合实际情况,查询耗时在10-100ms级别写入耗时:insert 包含了自增,理论上在数据落盘是追加写,即使uniq_key去创建索引的情况下,耗时在ms级过程耗时:长连接情况下,init conn时间基本可以忽略,但是读写两次往返数据库的链路时间还是需要考虑,整体预估在1-2ms之间从整体上看,预估该阶段的耗时在10-100+ms,从实际失败率来看也基本一致!3.2.2 并发分析天级QPS:当时分析天级几十万单,天级QPS不到10,不高!瞬间QPS:每个订单拆解到资金流后,会同时操作多次热点账户,瞬间qps相对很高,理论qps就可能达到语言上限,由于上游链路限流1024,按照10级别操作拆分,理论上满池QPS在万级别。考虑实际单量,瞬间QPS=单量(10)*拆解量(10),实际的满额预估QPS可能到100+ !按照上面分析,在瞬时QPS达到10+的情况下,热点账户整体延时在10-100+ms,由于DB在写入uniq_key保证链点唯一,所以出现并发写入失败也在情理之中;并且随着数据量的提升,读取延时增加,写入失败率会继续增加。3.2 框架问题区域请求阶段做为入口,一般也分为三个小阶段webserver接收请求框架加载和路由基础校验请求阶段核心耗时一般存在于框架加载和路由,高并发场景webserver和upstream之间的调用也是一个可能出问题点!当时财务系统,采用欢总封装的go-thrift,并且其他模块并未出现请求阶段问题,所以并未对这个阶段的latency和并发做一个衡量,重点分析了业务模块!四、解决方案4.1 读取和写入阶段优化通过上面分析,目前问题的痛点是并发读取热点账户数据高延时引发写入失败,提升读性能成为了关键读性能提升有两个基本思路:读的时效快和读的次数少针对上面两个基本思路,结合财务账户情况提出了五种提升读性能的解决方案【读快】持久化last record:不从全量数据里面读,抽离子账户的最新信息,持久化到单独的表中或者内存中,降低整体数据量,提升了读性能。缺点是要保证持久化信息的准确性,引入事务写。【读快】纵向切分-时间分库分表:按照时间进行纵向切分,降低查询范围内的数据量,提升读性能。缺点是跨时间读不友好,开发量也不小【读快】纵向切分-归档:历史数据归档是实现相对简单,跨时间读也比较友好,随着数据量的提升,也是必须要做,之后会详细介绍归档方案和选型。【读快】横向切分-业务分库分表:按照账户类型或者城市分库分表,可以优化读写数据量,同时,跨表读负担也会较小。但对于热点账户或者热点城市,依然聚簇,效果不是很明显。同时,再次对热点账户进行横向分库分表也是极度不推荐,引入的极高的读写成本均。【读少】阶段快照:一定量或者一定时间内的数据,持久化一次。优势是极大的降低读写次数;缺点是需要复杂的逻辑来保证undo操作和数据一致性!五种解决方案各有千秋,作为一个初期的财务系统推荐采用持久化last record和数据归档来保证写入读性能和整体读的数据量。如果系统发展到了中期,推荐按照时间分库分表。如果发展到了双11或者春晚某些极端场景,牺牲掉部分准确性,采用阶段快照也是可以的。4.2 计算阶段优化存在计算阶段造成的最大影响也就是引起了两次数据传输,通常是不可避免的,但是如果真的是要进行提升有一种方案通用方案DB计算:通过存储计算,转嫁计算成本给DB,减少一次链路请求。但不太推荐,复杂的sql往往有坑,insert computer from select 还会造成大面积的数据隔离,很容易引起死锁。4.3 请求和回调阶段优化请求阶段一般有三种形式:同步调用,异步调用和伪同步调用!前两种调用非常常见:同步爆池的情况,一般采用限流来降压,采用漏桶,令牌桶等等策略;异步调用通常采用消息队列来削峰填谷;这里重点阐述对于支付和财务系统在请求阶段经常采用的伪同步的方式伪同步流量较早出现在innodb,leveldb等存储引擎为了利用追加写提升写入性能,采用类WAL日志来持久化数据。通常伪同步方案采用三件套:WAL日志+校验位+广播消息来完成一次完整的请求!流程图一般如下请求阶段:同步请求调用,核心要素追加写入wal日志,变更校验位,完成同步调用!此处追加写保证了快速写入,校验位来保证数据的最终写入成功。图中1,2异步阶段:通过读取wal日志的核心数据,进行复杂事务处理,如果成功进入下一阶段;如果失败,没问题,通过外部trigger来触发redo操作!如果多次redo依然失败,那么通过undo来回滚数据。回调阶段:如果成功,更改校验位,同时发布成功广播消息,关注结果和时效性的模块,可以获取最终成功的标识!如果undo回滚数据,则发布失败广播消息,告知结果失败!在伪同步的模式下指标衡量:QPS:伪同步模式,采用WAL核心要素追加写,所以写性能可以极大提升,进而满额QPS相对直接同步调用也大量提升时效性:伪同步并非完全同步,所以结果需要监听回调。对于结果强一致的请求,必须监听回调,确保一致,时效性降低;对于弱一致可以认为同步回调即成功,时效性提升。失败率:操作知识核心要素追加写入,真正的操作通过异步保证,整体成功率提升!对于资金处理过程,大量采用伪同步的请求方式来保证快速写入和最终一致性。4.4 解决方案总结总的来说,归结了七种优化方式(哈哈,上篇写的八种优化,当时总结的,现在愣是想不到还有啥了^^)。其中请求和回调的伪同步方式,是在架构层面优化,这个在多数的财务系统和财务系统的内部数据流中都会用到;而读写和计算阶段的优化,可以跟进实际业务情况进行选型处理。五、事故复盘面对各种优化方案,需要结合实际情况做出取舍,有的是长期方案,有的是快速方案,但一定需要想清楚了再开搞,过程中有一个对小拽之后影响很大的事故,引以为戒。翻车过程:当时觉的读->计算->写这个过程,两次读DB操作,下沉计算过程到DB后,通过DB计算,可以减少一次数据库请求。于是合并了一个大SQL,也就是所谓的insert ( field computer from select),觉的写了个狂赚酷炫吊炸天的SQL,一上线,库锁死了!幸好有前置的redo flag,全量redo数据恢复,要不然估计直接祭天了!对于这个复杂大SQL事故,小拽总结了三个方面莫炫技:没啥好说的,解决问题的成就感要远大于炫技!简单设计:简单的设计通常意味着可依赖;复杂的设计要尽可能的拆解,想清楚,队友都理解不了的设计,那就别上线了,可能真的需要再思考拆解下尊重线上:核心服务基本上线流程一定要遵守,测试,监控和回滚方案缺一不可六、小结本篇主要针对热点账户问题提出了七种常用的解决方案,下篇将继续引申探索下,各种解决方案在不规则高并发场景,例如双十一,微博热点事件中如何套用预知后事如何,下回再聊!【转载请注明:热点账户问题和常用解决方案【中】 | 靠谱崔小拽 】

April 1, 2019 · 1 min · jiezi

热点账户问题和常用解决方案【上】

热点账户问题由来已久,一直是账户系统设计中的一个难点和瓶颈!小拽将通过上中下三篇文章,分别介绍下热点账户的产生,解决方案和延伸应用!本篇主要介绍下什么是热点账户?通用财务账户系统如何设计?以及其中的幂等健和链式设计等一、热点账户问题1.1 什么是热点账户热点账户:顾名思义,热点账户就是会被高频操作的账户!相较于普通的账户,热点账户数量不多,但操作频率极高!热点账户从产生来源可分两大类:富二代型:从产生之初就是热点账户,非常稳定。例如财务中公司的账户,每一笔资金操作都要经过公司出金账户,自然而然操作就会灰常频繁,此类账户还包括:大V账户,大KA账户等等,此类账户所引起的问题是本文重点要解决的暴发户型:本身是普通账户,由于热点问题变为热点帐户。例如微博出轨女猪脚账户,诺贝尔奖获得者等等,由于热点事件造成的短时间内访问暴增!此类热点账户防不胜防,超出本文的攻击范围,暂不讨论。1.2 热点账户问题热点账户一旦产生便伴随着高并发,流量分布不均匀,高一致性等等问题。在实际场景中是热点账户必然存在,常常成为用户系统的瓶颈!同时,热点账户问题也是高并发问题的延展,由于热点的不规则性,如何在高并发情况下,削峰填谷,弹性抗压也是很有挑战性的一个方向!1.3 热点账户通用解决方案的价值热点账户除了是账户体系的一个通用问题,在高并发,流量分布不均匀,异常峰值等其他问题上,也有一定的通用性。例如微博热点问题,支付宝双11弹性变更,高频抢购问题等等。期望通过学习热点账户的八种解决方案,能够举一反三,应用于不同场景!二、如何设计一个财务账户在解决热点账户问题之前,先来看下如何设计一个简单的财务账户,来保障资金记账的安全!2.1 业务场景分析从业务上看,财务账户需要准确记录用户的资金变动过程和结果!因此设计一个简单财务账户至少要能包括两个部分:账户余额和账户流水便于理解,来张传统的账本,看下什么是流水,什么是余额账户流水:账户流水也就是通俗意义上的帐或者账单!针对某个账户,每一笔资金的变更都需要记录下来,并且保障准确,不可更改!同时如图所示,流水中需要包含单据产生的原因,来源,变更额等等账户余额:账户余额记录用户某个场景账户的当前资金额度!在复杂的业务场景中往往需要拆分出不同的子账户和账户模型。例如,未结算子账户,可提现子账户,冻结子账户,授信账户等等。从业务场景上一个账户系统核心需要准确记录余额和流水,同时,必须保障记录的准确,完备,不可变更!2.2 技术层面拆解2.2.1 基本表方案通过业务场景初步分析,基本的账户系统,需要三张基本表账户基本信息:账户信息表子账户余额信息:账户余额表账户流水信息:账户流水表三张表基本关系账户信息表 1:N 账户余额表账户余额表 1:N 账户流水表## 具体账户和用户的关联可以参考三户模型 2.2.2 表字段设计从技术层面看,设计具体表细节关键要解决以下几个问题防重:幂等健设计防改:链式设计防错:销账设计先上结果,简单的,能够满足上述需求的设计可以参考innodb mvcc,核心表字段如下2.2.3 表字段解读2.2.3.1 幂等健设计通过三个属性资金凭证号+版本号+rollback三个字段作为uniq key来保证幂等!资金凭证号:来自业务方,业务方发起资金操作的唯一财务凭证,必须可追溯上游凭证和对账!版本号:每次获取DB最新流水n后,版本号n+1插入,保障在并发情况下,每个子账户只有唯一一个版本号:n+1条记录能够插入成功!rollback:回滚标识,保证每条记录能且只能销账一次!对于幂等建设计此处有三条小技巧上游产生:每一个幂等健如果可能的话,尽可能的上游产生,这样可以最大限度的避免自产生幂等健的重复问题。如果确实不能上游产生,例如订单ID,提现单ID,那么也尽可能的分阶段产生,例如提现时,先生成提现单ID,真正提现操作的时候,一定是带着提现单ID和信息来的,防止重复造成资损!业务关联:幂等健的产生可以用ice生成,但是,最好能够和业务关联,因为通过业务强关联的幂等健可以无限回溯来容灾!比如,a用户的b订单进行c操作,uniq_key = a_b_c的话,也就是在任何情况下,无论多少次回溯,重试也只会有一个唯一的a_b_c,而ice生成则可能造成自回溯的时候插入多条!写库保证:这条原则是高一致高并发的基本原则!因为读取a,校验a,然后插入,必然会存在读写之间a变了,或者主从延时a已经变了,读了历史a。因此,幂等一定要通过写库保证或者最底层保证2.2.3.2 链式设计链式设计是保证操作精准不可篡改的非常有效手段!通过资金的before info,after info,版本号三个要素来保证一条资金记录一旦插入成功,前后置信息固化!链式设计的情况下单条修改是不可能的,多条修改需要在保证条目不变的情况下重组资金,但是,整体资金不可变解决多条修改的一般方案:分布式存储,选举来判定最终正确的链,来确认是否某条链发生了过程修改,这种设计有一个很时髦的名字:区块链!而每条流水的核心信息加密后也有了一个更加时髦的名字:比特币!2.2.3.3 销账设计销账设计在账户系统中是一直存在的,现实财务系统可以红销蓝抵,线上财务系统加了链式之后,基本上就只能采用蓝抵通过增加rollback字段,并且严格限制0|1,保证一条账务流水只能被抵销一次!具体三张表详细字段,需要脱敏,就不贴了,参考上面,其中索引,字段大小,联合索引等设计根据自身业务场景兼容即可!小结:欲知后事如何,且听下回分解本部分简单介绍了什么是热点账户和账户的基本设计,涵盖幂等健设计,链式设计等等!下一篇重点分析下热点账户在链式设计下的问题,产生原因和八种基本解决方案【转载请注明:热点账户问题和常用解决方案【上】 | 公众号:靠谱崔小拽 |】

February 13, 2019 · 1 min · jiezi