优化方案
限额限次短期优化方案:
基础方案
(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
虚拟上万个结点,然后将其真实的对应到实际结点中。
服务挂掉了怎么办
需要好好设计。
更多学习
更多实时资讯,前沿技术,生活趣事。尽在【老马啸西风】
交流社群:[交流群信息](https://mp.weixin.qq.com/s/rkSvXxiiLGjl3S-ZOZCr0Q)