优化方案

限额限次短期优化方案:

基础方案

(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。

如果不能存储全量,也考虑类似如下的标志:

  1. 全量(所有配置都在本地缓存,不用访问 redis)

  2. 无(必须访问 redis)

  3. 部分(部分在本地,具体什么样的需要访问 redis,如何判断,需要细化讨论)

缓存穿透

本地缓存保证,避免这个问题,全量时不存在就是不存在。

缓存雪崩+缓存预热:暂时不考虑

因为 redis 本身就可以承担这个职责。

日志定位

时间

是否全量

共计加载数量

类别

加载耗时

性能统计

暂时不做处理。

限额限次短期优化方案:

基础方案

(1)添加 merId 过滤条件,便于命中联合索引。 (索引)

(2)每一层统计,次数和金额并行执行(从概率上来说,拦截是低概率,压力还是会到 mongodb )

fixed(2) 固定线程数2(并行)

注意日志的 logId

(3)添加表为对应业务的分表。(分表,效果一般)

(4)降低存储量。专门为限额建立表(降低单条数据量)

全部并发执行

性能提升最显著,需要调整代码结构。 看下代码重构的复杂度。

限额限次配置合并

对于一个交易来说,配置永远只会命中一种。

优先级依次为:

  1. 用户配置(优于商户下用户)

  2. 二级商户

  3. 商户(包含3.2商户下用户)

  4. 全局

除了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 左右。

核心思想:

  1. 相对稳定的配置读本地缓存,降低 redis 压力

  2. 计算耗时的热点商户,访问 redis,降低 mongodb 压力。

何为热点商户

前提:假设每个商户的业务增长是平滑的,昨天很高,今天会依旧很高。实时计算代价太大,暂时不考虑。

建立一张热点商户表,每天定时执行。(一天延迟)

计算策略:

  1. 昨日的峰值。

  2. 三十天的峰值平均(建议)

如果量太大,可以考虑降低天数,比如7天。

热点商户的新增

场景:引入一个大交易量的新商户。

方案:可以页面新增。但是禁止删除/修改。

新增后,每次 mongodb 累计后,设置进去。

优先级:也可以不设置,让新增的全部压力到 redis。(放在后期。)

本地缓存优化

可以放在 redis ,然后启动后直接加载 redis 。加载到本地后,以本地为准。

如何保证数据一致性?

变化频率太快,不方便本地+redis。

可以后期再做优化。

系统重启?

分布式服务?

将导致数据的一致性非常麻烦。

如何移除对于公共资源的依赖

为何数据库需要移除

如果依赖的数据库需要重启等,则会导致必须停止服务,

如果是处于这种考虑,那么应该不依赖数据库。

但是后来的服务,可以写成以 ip 进行飘,就不存在类似的问题。

怎么不依赖 redis 进行累加

Redis 就是为了解决分布式服务中多台机器的状态数据问题。

也可以使用另外一种方案。

根据某个主键进行 hash mod n,将固定的交易固定 hash 到指定的机器。

服务发布怎么办?

可以在配置中心配置一个开关,所有的发布的时候开启配置。

然后走原来的方式。(比如直接过 mongo)

配置好之后,在开启。

服务不均匀怎么办

可以使用一致性 hash

虚拟上万个结点,然后将其真实的对应到实际结点中。

服务挂掉了怎么办

需要好好设计。