预算规则计算

  1. 定义

    总桶定义:表示总预算桶的模型

    分桶定义:表示用于分桶模型, 统计使用

    分桶:用于主流程中扣减的分桶,因为性能考虑,使用redis或者其他内存模型进行扣减

    分桶列表:预算桶中预算可用分桶的集合,不可用分桶的集合

  2. 理念:

    �空间换取时间

  3. 功能列表

    预算创建

    预算追加

    预算使用

    预算回滚

    预算查看

  4. 数据模型

    <pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">---总预算桶定义--
    create table budget_management_bucket(
    id bigint(20) unsigned auto_increment comment '预算id',
    gl_code varchar(20) comment '科目',
    total_quantity bigint(20) commment '总量',
    out_quantity bigint(20) comment '已发放量',
    sharding_quantity bigint(20) comment '单个分桶最多申请预算量',
    bucket_quantity int comment '初始分桶数量',
    adjust_cnt int comment '分桶预算最多可以调整次数',
    type varchar(20) comment '预算应用场景:大促,日常',
    type_rule longtext comment '子预算桶预算申请规则',
    status varchar(20) comment '状态:未生效,已生效,已失效',
    created_at datetime comment '创建时间',
    updated_at datetime comment '更新时间',
    primary_key (id)
    )
    ----子预算桶定义----
    create table sharding_budget_management_buckect(
    id bigint(20) unsigned auto_increment comment '子预算桶id',
    budget_id bigint(20) comment '预算id',
    sharding_bucket_key varchar(150) comment '分桶key',
    out_quantity bigint(20) comment '已经申请的总量',
    sharding_quantity bigint(20) comment '单个分桶最多申请预算量',
    out_cnt int comment '分桶已经申请次数',
    status varchar(20) comment '在线,离线',
    created_at datetime comment '创建时间',
    updated_at datetime comment '更新时间',
    primary_key (id)
    )

    -------内存key-value结构-----------
    分桶:
    key:分桶key
    value:当前剩余预算量

    分桶列表:
    key: 分桶列表key
    value :在线的分桶,离线的列表要即时删除</pre>

  5. 业务架构

    image
  6. 创建预算

    image
  7. 预算扣减

    image
  8. 申请预算

    image
  9. 分桶下线

    image
  10. 回滚预算

image

11.碎片回收

image

相关解释

  1. 全局锁相关解释

    扣减预算的时候不用添加全局锁,redis本来就是单线程,不需要通过锁的方式扣减

    申请预算,分桶下线因为可能涉及到并发的问题,需要添加分桶的全局锁, 不让重复的操作

  2. 预算应用场景

    分为大促模式和日常模式,大促模式一次性分配完所有的预算。日常模式涉及到预算的压缩,比如1年的可以压缩到每天分配,那么假如1年的预算是365万,那么每天的预算可以压缩到1万,预算申请的时候控制。

    日常模式中会存在子预算桶预算申请规则,规则可配置:与日期强相关(比如整点可以申请,每天可以申请,每月可以申请),与日期不相关(用完即可申请)

  3. 为什么要使用分桶

上面讲到了空间换取时间的概念,我们尽量避免热点,分桶可以降低集中请求某一点造成的压力,假如一个 redis库的tps可以达到50000,那我们使用10分桶,分别放到10个redis库中,并行处理的时候我们可以达到 500000的tps,方便以后扩展。同时我们可以不局限redis,或者使用其他类型的数据库,不局限redis,不局限noSql。如果要求不高的情况,我们可以将分桶尽量扩大,理论上关系型数据库在分桶够大的时候,也是可以满足相应的指标的。

4.碎片回收

碎片回收是指将每个分桶的预算全部回收到总的桶定义中,然后重新分配预算,类似于jvm中垃圾回收算法中的标记清除算法,所有分桶下线,回收可用预算到总预算桶。再分配可用粪桶

5.分桶创建算法

分桶数量可以指定,如果是数量类的预算,1000数量一个桶,如果金额类的预算1000000一个桶

随机算法样例,真实情况的group是分桶的key

<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">package com.constant.hash.demo;

import java.util.*;

/**

  • 一致性hash算法达到负载均衡

  • 在预算分桶计算可以实现获取平均化的获取每个分桶中的数据避免出现热点的问题,可以动态增加减少节点

  • 添加虚拟节点

  • @author <a href="mailto:zhaoxiaotao@terminus.io">tony</a>

  • com.constant.hash.demo

  • 2018/12/14 10:39

  • hash-demo
    */
    public class ConsistentHashingWithVirtualNode {
    //真实节点
    private static String[] groups = {"192.168.0.0:111", "192.168.0.1:111", "192.168.0.2:111", "192.168.0.3:111", "192.168.0.4:111"};

    //真实节点集合
    private static List<String> realGroups = new LinkedList<>();

    //虚拟节点
    private static SortedMap<Integer, String> virtualNodes = new TreeMap<>();
    //每个真实节点关联的虚拟节点个数
    private static final int VIRTUAL_NODE_NUM = 1000;

    static {
    realGroups.addAll(Arrays.asList(groups));

     //将虚拟节点添加到环上
     for (String realGroup : realGroups) {
         for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
             String virtualNodeName = getVirtualNodeName(realGroup, i);
             int hash = HashUtil.getHash(virtualNodeName);
             //System.out.println("[" + virtualNodeName + "] lauched @" + hash);
             virtualNodes.put(hash, virtualNodeName);
    
         }
     }
    

    }

    //虚拟节点名称
    private static String getVirtualNodeName(String realName, int num) {
    return realName + "&&VN" + String.valueOf(num);
    }
    //真实节点名称
    private static String getRealNodeName(String virtualName) {
    return virtualName.split("&&")[0];
    }

    //获取节点
    private static String getServer(String widgetKey) {
    int hash = HashUtil.getHash(widgetKey);
    SortedMap<Integer, String> subMap = virtualNodes.tailMap(hash);
    String virtualNodeName;
    if (subMap == null || subMap.isEmpty()) {
    virtualNodeName = virtualNodes.get(virtualNodes.firstKey());
    } else {
    virtualNodeName = subMap.get(subMap.firstKey());
    }
    return getRealNodeName(virtualNodeName);
    }

    public static void main(String[] args) {
    Map<String, Integer> resMap = new HashMap<>();
    for (int i = 0; i < 1000; i++) {
    Integer widgetId = (int) (Math.random() * 100000);
    String group = getServer(widgetId.toString());
    if (resMap.containsKey(group)) {
    resMap.put(group, resMap.get(group) + 1);
    } else {
    resMap.put(group, 1);
    }
    }

     resMap.forEach((k, v) -> {
         System.out.println("group " + k + ":" + v + "(" + v / 10.0D + "%)");
     });
    

    }
    }</pre>

<pre class="cm-s-default" style="color: rgb(89, 89, 89); margin: 0px; padding: 0px; background: none 0% 0% / auto repeat scroll padding-box border-box rgba(0, 0, 0, 0);">package com.constant.hash.demo;

/**

  • fnv1_32_hash算法
  • @author <a href="mailto:zhaoxiaotao@terminus.io">tony</a>
  • com.constant.hash.demo
  • 2018/12/14 10:31
  • hash-demo
    */
    public class HashUtil {
    public static int getHash(String str) {
    final int p = 16777619;
    int hash = (int) 2166136261L;
    for (int i = 0; i < str.length(); i++) {
    hash = (hash ^ str.charAt(i)) * p;
    }
    hash += hash << 13;
    hash ^= hash >> 7;
    hash += hash << 3;
    hash ^= hash >> 17;
    hash += hash << 5;
    if (hash < 0) {
    hash = Math.abs(hash);
    }
    return hash;
    }
    }</pre>

碎片收集算法

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,123评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,031评论 2 384
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,723评论 0 345
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,357评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,412评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,760评论 1 289
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,904评论 3 405
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,672评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,118评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,456评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,599评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,264评论 4 328
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,857评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,731评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,956评论 1 264
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,286评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,465评论 2 348

推荐阅读更多精彩内容