热点帐户问题和经常使用解决方案【中】

话接上回,上篇阐述了什么是热点帐户,基本财务帐户如何设计,幂等健和链式设计!本篇将针对热点帐户在实践中引起的问题,梳理和拆解业务流,分析问题点,提出七种经常使用解决方案。

1、性能问题初现

上线初期数据量较小,运行正常!
一次大促后,帐户流水的总数目接近亿级别,初现性能问题:系统总体的qps也就10+,但热点帐户写入失败率偏高,而且随数据量增长失败率逐步升高;整个帐户系统全靠上游有redo标识位不断重试,才能保证最终写入成功!web

哈哈,做为一名拥有三年工做经验的老码农,面对问题,要作的第一件事,就是,抽根烟静静,准备开搞!算法

2、数据流拆解

拿到问题,抽根烟静一下以后,分析问题须要三步:梳理数据流,拆解过程,定位问题点。先对财务帐户更新的数据流进行拆解sql

finance_hot_account_3

链式锁后的基本帐户操做过程,分为以下五阶段数据库

  • 请求阶段:帐户操做请求。
  • 查询阶段:查询当前帐户信息。主要获取当前链,资金数据等!
  • 计算阶段:对链和操做的资金进行计算,断定资金操做合规,同时保证幂等和并发!
  • 写入阶段:事务同步写入流水和余额
  • 响应阶段:告知上游回调

3、链路分析

梳理数据流后,接下来分析每一个阶段可能引起的问题。按照优先级,先分析业务问题区域(读取阶段,计算阶段,写入阶段),一般问题会出如今业务阶段;以后,再分析框架问题区域(请求阶段和回调阶段),该区域出现问题的状况偏小,可是一旦出现问题,就是比较有意思^_^!架构

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、解决方案

4.1 读取和写入阶段优化

经过上面分析,目前问题的痛点是并发读取热点帐户数据高延时引起写入失败,提高读性能成为了关键

读性能提高有两个基本思路: 读的时效快和读的次数少

针对上面两个基本思路,结合财务帐户状况提出了五种提高读性能的解决方案

  • 【读快】持久化last record:不从全量数据里面读,抽离子帐户的最新信息,持久化到单独的表中或者内存中,下降总体数据量,提高了读性能。缺点是要保证持久化信息的准确性,引入事务写。
  • 【读快】纵向切分-时间分库分表:按照时间进行纵向切分,下降查询范围内的数据量,提高读性能。缺点是跨时间读不友好,开发量也不小
  • 【读快】纵向切分-归档:历史数据归档是实现相对简单,跨时间读也比较友好,随着数据量的提高,也是必需要作,以后会详细介绍归档方案和选型。
  • 【读快】横向切分-业务分库分表:按照帐户类型或者城市分库分表,能够优化读写数据量,同时,跨表读负担也会较小。但对于热点帐户或者热点城市,依然聚簇,效果不是很明显。同时,再次对热点帐户进行横向分库分表也是极度不推荐,引入的极高的读写成本均。
  • 【读少】阶段快照:必定量或者必定时间内的数据,持久化一次。优点是极大的下降读写次数;缺点是须要复杂的逻辑来保证undo操做和数据一致性!

五种解决方案各有千秋,做为一个初期的财务系统推荐采用持久化last record和数据归档来保证写入读性能和总体读的数据量。若是系统发展到了中期,推荐按照时间分库分表。若是发展到了双11或者春晚某些极端场景,牺牲掉部分准确性,采用阶段快照也是能够的。

4.2 计算阶段优化

存在计算阶段形成的最大影响也就是引发了两次数据传输,一般是不可避免的,可是若是真的是要进行提高有一种方案通用方案

  • DB计算经过存储计算,转嫁计算成本给DB,减小一次链路请求。但不太推荐,复杂的sql每每有坑,insert computer from select 还会形成大面积的数据隔离,很容易引发死锁。

4.3 请求和回调阶段优化

请求阶段通常有三种形式:同步调用,异步调用和伪同步调用
前两种调用很是常见:同步爆池的状况,通常采用限流来降压,采用漏桶,令牌桶等等策略;异步调用一般采用消息队列来削峰填谷;这里重点阐述对于支付和财务系统在请求阶段常常采用的伪同步的方式

伪同步流量较早出如今innodb,leveldb等存储引擎为了利用追加写提高写入性能,采用类WAL日志来持久化数据。一般伪同步方案采用三件套:WAL日志+校验位+广播消息来完成一次完整的请求!流程图通常以下

finance_hot_account_4

  • 请求阶段:同步请求调用,核心要素追加写入wal日志,变动校验位,完成同步调用!此处追加写保证了快速写入,校验位来保证数据的最终写入成功。图中1,2
  • 异步阶段:经过读取wal日志的核心数据,进行复琐事务处理,若是成功进入下一阶段;若是失败,没问题,经过外部trigger来触发redo操做!若是屡次redo依然失败,那么经过undo来回滚数据
  • 回调阶段:若是成功,更改校验位,同时发布成功广播消息,关注结果和时效性的模块,能够获取最终成功的标识!若是undo回滚数据,则发布失败广播消息,告知结果失败!

在伪同步的模式下指标衡量:

  • QPS:伪同步模式,采用WAL核心要素追加写,因此写性能能够极大提高,进而满额QPS相对直接同步调用也大量提高
  • 时效性:伪同步并不是彻底同步,因此结果须要监听回调。对于结果强一致的请求,必须监听回调,确保一致,时效性下降;对于弱一致能够认为同步回调即成功,时效性提高。
  • 失败率:操做知识核心要素追加写入,真正的操做经过异步保证,总体成功率提高!

对于资金处理过程,大量采用伪同步的请求方式来保证快速写入和最终一致性

4.4 解决方案总结

总的来讲,归结了七种优化方式(哈哈,上篇写的八种优化,当时总结的,如今愣是想不到还有啥了^_^)。其中请求和回调的伪同步方式,是在架构层面优化,这个在多数的财务系统和财务系统的内部数据流中都会用到;而读写和计算阶段的优化,能够跟进实际业务状况进行选型处理。

5、事故复盘

面对各类优化方案,须要结合实际状况作出取舍,有的是长期方案,有的是快速方案,但必定须要想清楚了再开搞,过程当中有一个对小拽以后影响很大的事故,引觉得戒。

翻车过程:当时觉的读->计算->写这个过程,两次读DB操做,下沉计算过程到DB后,经过DB计算,能够减小一次数据库请求。因而合并了一个大SQL,也就是所谓的insert ( field computer from select),觉的写了个狂赚酷炫吊炸天的SQL,一上线,库锁死了!幸亏有前置的redo flag,全量redo数据恢复,要否则估计直接祭天了!

对于这个复杂大SQL事故,小拽总结了三个方面

莫炫技:没啥好说的,解决问题的成就感要远大于炫技!
简单设计:简单的设计一般意味着可依赖;复杂的设计要尽量的拆解,想清楚,队友都理解不了的设计,那就别上线了,可能真的须要再思考拆解下
尊重线上:核心服务基本上线流程必定要遵照,测试,监控和回滚方案缺一不可

6、小结

本篇主要针对热点帐户问题提出了七种经常使用的解决方案,下篇将继续引伸探索下,各类解决方案在不规则高并发场景,例如双十一,微博热点事件中如何套用

预知后事如何,下回再聊!

【转载请注明:热点帐户问题和经常使用解决方案【中】 | 靠谱崔小拽

相关文章
相关标签/搜索