优化方案
限额限次短期优化方案:
基础方案
(1)添加 merId 过滤条件,便于命中联合索引。 (索引)
(2)每一层统计,次数和金额并行执行(从概率上来说,拦截是低概率,压力还是会到 mongodb ) fixed(2) 固定线程数2(并行)
注意日志的 logId
(3)添加表为对应业务的分表。(分表,效果一般)
(4)降低存储量。专门为限额建立表(降低单条数据量)
全部并发执行
性能提升最显著,需要调整代码结构。
看下代码重构的复杂度。
Redis (下一步)
减少对于 redis 的访问
现象
比较长的访问时间(长耗时 50ms,3次也很长)
方案
使用本地缓存,定时去 oracle 加载。可限定大小,50m 以下。
从配置概率,依次加载用户,二级商户,全局。(因为量少) 部分加载商户配置。
可额外加载一个商户配置的 bloomfilter
初步直接一个 map + 全业务配置。 最简单。
是否引入 oracle
是:避免网络消耗+问题定位的复杂性。
如何避免全量
根据日志表更新时间。构建类似心跳的机制。
按类别:商户 、用户、二级商户、全局。
按业务:企账通,跨境,跨境。。。
分成比较细,便于细化管理,类似于 map 再分 segment。 可以降低配置间冲突。
代价是可能维护的代码较多。
单个类别
第一次查询:内部有个标识 volatile=full 一次加载全量。
记录下上次查询的最后时间。(心跳)
结合日志表的变更日志。进行晚于最后时间的进行更新。(考虑到网咯波动,时间差,时间往前推 10min)定期15分钟更新一次。
配置可调整。(配置中心)
后续增量操作
第一次查询:
内部有个标识 volatile=full 一次加载完成。
默认为 full。
如果服务重启,那么也是全量加载。
除却第一次,后面全部使用 increase 模式。
替换平滑
本地缓存没初始化完成,则访问 redis。
根据增量结果,构建好新的 map 之后,然后进行对象的替换。
状态标识
每次机器重启,就会导致所有配置丢失。
应该添加一个标识,标识当前本地缓存是否可用。
volatile isReady=false
等待第一次全量加载完成之后,isReady=true。
如果不能存储全量,也考虑类似如下的标志:
-
全量(所有配置都在本地缓存,不用访问 redis)
-
无(必须访问 redis)
-
部分(部分在本地,具体什么样的需要访问 redis,如何判断,需要细化讨论)
缓存穿透
本地缓存保证,避免这个问题,全量时不存在就是不存在。
缓存雪崩+缓存预热:暂时不考虑
因为 redis 本身就可以承担这个职责。
日志定位
时间
是否全量
共计加载数量
类别
加载耗时
性能统计
暂时不做处理。
限额限次短期优化方案:
基础方案
(1)添加 merId 过滤条件,便于命中联合索引。 (索引)
(2)每一层统计,次数和金额并行执行(从概率上来说,拦截是低概率,压力还是会到 mongodb )
fixed(2) 固定线程数2(并行)
注意日志的 logId
(3)添加表为对应业务的分表。(分表,效果一般)
(4)降低存储量。专门为限额建立表(降低单条数据量)
全部并发执行
性能提升最显著,需要调整代码结构。 看下代码重构的复杂度。
限额限次配置合并
对于一个交易来说,配置永远只会命中一种。
优先级依次为:
-
用户配置(优于商户下用户)
-
二级商户
-
商户(包含3.2商户下用户)
-
全局
除了1和3.2可能只过一个。其他可以认为全得过(我得理解)
能否配置层首先根据交易,找到最低的限额限次,然后并发执行?(合并只是逻辑复杂性)
其他实现细节
基于缓存注解
类似于 spring @Cache 或者类似,保证原来访问 redis 代码的封闭性。
架构的简单性
结合现有的配置数量(共计 20w 不到的配置)
直接全量加载到内存。
按照业务区分(便于一个交易的相关配置获取+合并)
内存爆炸怎么办
预防批导配置太多。
设置 map 的数量(大小)限制,如超过 50w 条 直接打 error 日志。(透镜报警)
阈值:可以看下内存的使用情况定义,不能太大。 昨天看永久带最大2g,full gc 0.6s 感觉还行。
拒绝策略
最简单的,超过阈值,拒绝添加。
至于淘汰策略,是拒绝,还是 LRU或者其他。
问题就是:拒绝了哪些?因为这部分就需要去访问redis。
此时,拆分的比较细优势就出来了。
比如只有商户有淘汰,其他配置直接拒绝并报警。
或者不用考虑这么多,估计有这个用户量,现有架构也得跟着变化。只要能变得动就行。
中间变量的累计
针对范围
单业务,除掉 100 个热点商户,其他的交易量不大。
仅仅针对热点商户。
准备工作
统计各业务 最近一个月的交易量,按照业务划分。
优化方案
比如交易量前 100 且超过 1w (可以不加。的信息。
注意设计好失效的时间。Key 中添加日期区分。
失效时间为 24小时。
借助 redis 进行累计。
并发问题
分布式锁
可以不考虑。
压力
一般一条商户的配置为 1-5条,所以一个业务共计 500 左右。
核心思想:
-
相对稳定的配置读本地缓存,降低 redis 压力
-
计算耗时的热点商户,访问 redis,降低 mongodb 压力。
何为热点商户
前提:假设每个商户的业务增长是平滑的,昨天很高,今天会依旧很高。实时计算代价太大,暂时不考虑。
建立一张热点商户表,每天定时执行。(一天延迟)
计算策略:
-
昨日的峰值。
-
三十天的峰值平均(建议)
如果量太大,可以考虑降低天数,比如7天。
热点商户的新增
场景:引入一个大交易量的新商户。
方案:可以页面新增。但是禁止删除/修改。
新增后,每次 mongodb 累计后,设置进去。
优先级:也可以不设置,让新增的全部压力到 redis。(放在后期。)
本地缓存优化
可以放在 redis ,然后启动后直接加载 redis 。加载到本地后,以本地为准。
如何保证数据一致性?
变化频率太快,不方便本地+redis。
可以后期再做优化。
系统重启?
分布式服务?
将导致数据的一致性非常麻烦。
如何移除对于公共资源的依赖
为何数据库需要移除
如果依赖的数据库需要重启等,则会导致必须停止服务,
如果是处于这种考虑,那么应该不依赖数据库。
但是后来的服务,可以写成以 ip 进行飘,就不存在类似的问题。
怎么不依赖 redis 进行累加
Redis 就是为了解决分布式服务中多台机器的状态数据问题。
也可以使用另外一种方案。
根据某个主键进行 hash mod n,将固定的交易固定 hash 到指定的机器。
服务发布怎么办?
可以在配置中心配置一个开关,所有的发布的时候开启配置。
然后走原来的方式。(比如直接过 mongo)
配置好之后,在开启。
服务不均匀怎么办
可以使用一致性 hash
虚拟上万个结点,然后将其真实的对应到实际结点中。
服务挂掉了怎么办
需要好好设计。