[转载]基于Aaf的数据拆分

(本文适于使用Aaf框架的开发者阅读)

1. 基本原理

  在Aaf框架中,“对象”和“存储”的关系映射有一个关键的纽带StorageAlias,即“存储别名”,同样一个类型,在不同的存储别名下,可以自由映射到任意存储“位置”。
  “位置”有两个元素决定,一个是存储上下文StoargeContext,另外一个是数据表名TableName。缺省的StorageContext在Persistence.Config中配置,缺省的数据表名就是类的名称。
   所有的映射关系,存储在两个地方,一个地方是TypeDescription的ExtendedAttributes属性中,这里存储的映射主要来源于 配置文件Persistence.Config中的配置。关系的建立是在Aaf启动阶段,由PersistenceMappingService初始化完 成,调用StorageContextMappingService中的GetStorageAliases方法取得 Persistence.Config中的存储别名的配置信息。此外,默认的存储上下文和默认的表名,也初始在此扩展特性中。

  通过Persistence.Config方式配置的映射,是“相对稳定”的映射,数量也在一个非常有限的级别上,例如我们可以将订单信息按照 交易类型的不同,存储到不同的数据库上,也可以将交易完毕的订单信息搬迁到历史库上,我们就可以定义出来形如 “EscortOrderByHistory”这样的存储别名,一看就知道是“担保交易的历史交易数据”。

  另外一个存储映射关系的地方是存储别名信息助手StorageAliasesInfoAssistant,助手的映射关系有三个来源:IStorageAliasTeller提供、手动动态添加、数据库参数配置

  通过IStorageAliasTeller方式,我们可以根据系统要求,动态 的解析一个存储别名,不仅如此,我们还可以根据AgileObject.Id自定义规则,推导出其对应的存储别名,例如我们可以将用户按照一定规则分表存 储,然后在用户的Id中包含存储别名的信息,这样,我们就可以非常方便的将海量用户拆分到若干分表当中,实现数据的分表存储。此外,与IDataRadiationClassifier搭 配起来,还可以实现“多维存储”, 就是将同样一份数据,按照不同的应用需要,散射到多个存储位置(有可能是散射到不同的数据库上,例如商品信息散射出一个专门供查询的库上;也有可能是同一 数据库的不同表上,例如订单信息根据买卖不同散射到特定的数据表上。),同时自动维护各个数据库上数据的一致性。这样就可以分身有术,让多个的数据库分担 查询压力。

  手动动态添加,是通过IStorageAliasesInfoAssistantRegisterDynamicStorageAlias方法,将已知存储位置添加到映射表中,同时返回一个GUID码作为存储别名。

  数据库参数配置,这种情形是存储映射信息是以参数的形式存储在数据库中的, 在Aaf.ParaService加载过程中,会自动生成一个Aaf.StorageAliases的配置节点,节点路径是Aaf.StorageAliases/[StorageAlias](存储别名,可配多个),节点配置格式是:
[

StorageContext1,TableName1 \n

AppId1: StorageContext2,TableName2 \n

AppId2: StorageContext3,TableName3 \n

]

  其特别的地方是在分布式环境中,根据AppID的不同将数据持久化到特定的存储上。

 

 

2. 应用实例

  在5173系统中,有许多应用,尤其以用户、发布单、订单及资金明细比较典型。下面我们一起去看看。

2.1. 用户数据分拆

  用户数据是基础数据,而且是一种会持续增长的基础数据,这种数据的膨胀会让其性能表现越来越差,因为是基础数据又不能删除或者搬移,我们处理这种数据的基本策略是将所有用户按照一定的策略散射到多个数据表中,以缓解单表的压力,下面我们分析一下实现过程:

1、 将所有用户数据按照注册日期段分组,每一个分组共用一个独立的数据表,分拆存储,分拆策略启用有一个时间基线,之前的数据依然存储在UserInfo中, 自此时间基线之后的数据便存储在形如UserInfo_X的数据表中。例如,设立时间基线为2009-5-22,每隔90天建立一个新组,某用户注册时间 为2009-12-2号,与时间基线相差194 天,除以90得2,存储在UserInfo_2中。

2、 创建一个类UserInfoSaTeller,实现IStorageAliasTeller接口

3、 将UserInfoSaTeller注册到别名信息助手,注册方法如下: PersistenceMappingService.StorageAliasesInfoAssistant.RegisterTeller(typeof(User), new UserInfoSaTeller()),这个注册在UserService的Run方法中。

4、 保存新用户时,内核将调用UserInfoSaTeller的GetPrimarySaveStorageAlias方法,取得存储别名,传入参数是灵便对象的Id号,因此我们需要构建一个含有分表特征信息的Id,我们现用的Id为“US09120282660351-00F7”,其中红色加粗部分为注册日期,通过注册日期,我们可以计算出一个特定的存储别名,UserInfo$2, 内核将继续调用UserInfoSaTeller的GetTableName,传入参数就是GetPrimarySaveStorageAlias计算出 来的“UserInfo$2”,很显然我们轻易可以得出实际的存储表名“UserInfo_2”。可见,我们只要按照规则构造好用户的Id,程序便可以自 动识别出来应该放到哪里存储。

5、 根据Id号取用户实例的过程与保存过程基本类似,内核调用UserInfoSaTeller的GetPrimaryLoadStorageAliases方法,取得加载时存储别名,之后的映射关系与对象创建时相同。对于用户数据,保存和获取时的存储别名是一致的。

6、 登陆时怎么办?登陆使用的用户名进行登陆,而不是用户Id。没什么太好的办法,基本思路还是遍历所有分表,好在,我们的分表时间区间都是以月为单位,十年 下来也不过一百多个表。此外,当一次遍历完成之后,我们可以将此用户对应的存储别名状态记录在某个地方,例如Cookie中,这样减少遍历的频度。

2.2. 发布单数据分拆

  发布单数据的拆分策略稍微复杂一点。根据实际应用需要,除了发布单默认的存储位置之外,发布单还有几种存储形态:根据用户分组、根据游戏、交易 完成的历史数据。根据用户分组分拆,是指把所有用户在逻辑上分成若干个用户组,每个用户组共用一个存储表;根据游戏更容易理解,即每一款游戏共用一个存储 表;交易完成的历史数据,就是那些单子不会再发生变化,这种数据,主要通过数据库Job定期搬移的。与用户数据分拆不同的是,发布单是一份数据在不同的地 方用不同的策略存储,即多维存储(游戏维度、用户分组维度)。下面我们分析一下数据创建和加载的过程:

1、 先看创建过程。因为是多维存储,BizOffer需要实现IDataRadiationClassifier接口,Aaf内核在工作时将调用其中的GetDataRadiationClasses方法,来获取每个维度的维度标识

2、 使用维度标识,调用IStorageAliasTeller的GetDimensionStorageAlias方法,翻译出维度对应的存储别名,形如:Search$0043和BizOfferBy023F-Escort,加黑的分别是“游戏标识”和“用户逻辑分组标识”。

3、 解释下用户逻辑分组的来历,我们将用户分成1000个逻辑小组,分组的方法依然与注册时间、时间基线相关,用两个时间的差(天数)对1000取模,这样就 会得到一组数字,将这组数字用四位16进制格式化,就是散射用户的逻辑分组标记,这个标记,我们也在用户Id中记录了,“US09120282660351-00F7”,即蓝色加粗部分。当一个用户创建发布单的时候,我们便知道其逻辑分组为00F7。游戏标识要简单一些,每一款游戏都会有一个自增长的表号,此表号也是用四位16进制表示。注意,用户分组目前是固定为1000个组的,而游戏分组是不断增长的。

4、 接下来就是分别对每个维度的存储别名进行存储动作,通过IStorageAliasTeller的GetContextName和GetTableName,取得实际的存储位置信息。

两个维度路径是:

存储别名–>存储上下文名称–>数据库名称–>数据表名

游戏散射:Search$0043–> RadiationOffer1–> SearchOffer–> BizOfferby0043

用户散射:BizOfferBy023F-Escort–>OfferDataRadiations–>OfferDR–>BizOfferBy023F

5、 还有一个维度是历史库的发布单数据,这个是Job搬迁创造的,数据表的命名规则是一个月三张表,按旬存储,形如:BizOfferby200607_1
。虽然创建过程不是Aaf做的,但查询数据时是Aaf做的,后面我们在做查询分析的时候要考虑这部分数据。

6、 在单条数据加载的时候,重点是确定查询策略了,就是什么数据该先从哪个维度去找,策略确定之后,就是拼存储别名来定位查询的数据库表了。我们现在的查询策 略是先查找当前库,拼EscortByCurrent的存储别名,此存储别名在Persistence.Config中配置了映射关系;接着检查“用户分 组维度”;再接着,查找历史库。

几个查询路径如下:

当前库:EscortByCurrent–>ConsignmentByCurrent–>Consignment–>BizOffer

用户散射:BizOfferBy023F-Escort–>OfferDataRadiations–>OfferDR–>BizOfferBy023F

按发布单结束时间历史库(后台使用):Escort|200912_1–>OfferHistory–>OfferHistory–> BizOfferby200912_1

按发布单创建时间历史库(前台使用):
Escort~200912_1–> OfferHistorybyCreatedDate–> OfferHistorybyCreatedDate–> BizOfferby200912_1

7、 接下来我们梳理一下各种发布单列表是怎么出来的,当一个查询过来的时候,我们首先判断查询条件中是否含有用户信息,如果有,直接走“用户分组维度”。如果 没有用户信息,至少会包含一个游戏信息,即GameId,走游戏维度的存储。注意,列表查询没有走“默认维度”,这样默认维度就可以专心用于交易了。

8、 8、 有关发布单查询,还没有结束。就是另有一个发布单搜索对象BizOfferSearch,这个是另外生成的一份数据,专门供搜索用,与当前交易平台隔离。同样也有两个维度:

存储别名–>存储上下文名称–>数据库名称–>数据表名

游戏散射:Search$0043–> RadiationOffer1–> SearchOffer–> BizOfferSearchby0043

用户散射:BizOfferSearchBy023F-Escort–>OfferDataRadiations–>OfferDR–>BizOfferSearchBy023F

2.3. 订单数据分拆

  订单的数据分拆方法类似,根据实际应用需要,除默认存储外,也有几个维度的存储:买家分组散射、卖家分组散射、历史库数据。从技术实现方式来讲完全一个套路。。

1、 订单创建过程与发布单雷同,不再赘述。需要注意的是,买卖维度的数据表名中也是附加了用户分组标识。一笔订单,会因为买家和卖家的分组不同,而存在于不同标识的分表中。

2、 订单取得过程,先从默认库查找,找不到再去买家维度找,找不到再去卖家维度找,还找不到就到历史库中找。

维度路径是:

存储别名–>存储上下文名称–>数据库名称–>数据表名

买家散射:OrderByBuyer023F-Escort–> OrderDataRadiations–> OrderDR –> OrderByBuyer023F

卖家散射:OrderBySeller009B-Escort–> OrderDataRadiations–>OrderDR–> OrderBySeller0339

历史库:Escort|200912_1–>OrderHistory–>OrderHistory–> Orderby200912_1

3、 在前台列表查询中,买卖双方呈现自己的订单时,是分别从买卖维度即OrderDR这个库中取数据的。后台列表查询,走的是默认维度的订单库。同时,对于交易完成的数据,到历史库OrderHistory中查询。

2.4. 资金分表

  AccountDetail,除默认在外,还有用户分组维度、历史库维度。

1、 多维存储路径:

存储别名–>存储上下文名称–>数据库名称–>数据表名

用户散射:AccountDetailBy00F7–>DataRadiations–>BkDR–> AccountDetailBy00F7

历史库:200912_1–>BkHistory–> BkHistory–> AccountDetailBy200912_1

2、 特别要说明的是,每种资金类型都有自己的独立的类,他们如何共用AccountDetail这样一个存储的呢?原来所有的独立的资金类型都派生自 AccountDetail,而AccountDetail中设置的“[AgileObjectStorage(TableName = "AccountDetail", ContextName="BkUser")]”,也被继承下来了。其实更重要的是它们也继承了的IDataRadiationClassifier实 现,此外,在PaymentService的Run方法中,几乎所有类型都注册了AccountDetailSaTeller,于是实现了所有类型的存储 规则统一。

2.5. 映射列表

以下是开发环境中的映射关系列表:

对于这些类型,如果要修改字段,每一个关联维度上对应的分表都要变更,我们经常碰到的缺字段,就是某个维度上有缺失。

类型

数据库名

数据表名

存储上下文名

存储别名

UserBkUser
(默认主库)
UserInfo_1
UserInfo_2…

UserInfo_n

BkUserUserInfo$1
UserInfo$2…

UserInfo$n

BizOfferConsignment
(默认主库)
BizOfferConsignmentByCurrentEscortByCurrent
SearchOffer
(游戏散射)
BizOfferby0000

BizOfferBy07D0…
RadiationOffer1Search$0000

Search$07D0…
OfferDR
(用户散射)
BizOfferBy0000

BizOfferBy03E8
OfferDataRadiationsBizOfferBy0000-Escort

BizOffeBy03E8-Escort
OfferHistory
(历史库By交易完成时间)

BizOfferby200912_1
BizOfferby200912_2
BizOfferby200912_3
OfferHistory
Escort|200912_1
Escort|200912_2
Escort|200912_3…
OfferHistorybyCreatedDate
(历史库By创建时间)

BizOfferby200911_1
BizOfferby200911_2
BizOfferby200911_3
OfferHistorybyCreatedDate
Escort~200911_1
Escort~200911_2
Escort~200911_3…
BizOfferSearchConsignment
(默认主库)
BizOfferSearchConsignmentByCurrentEscortByCurrent
SearchOffer
(游戏散射)
BizOfferSearchby0000

BizOfferSearchBy07D0
RadiationOffer1Search$0000

Search$07D0
OfferDR
(用户散射)
BizOfferSearchBy0000

BizOfferSearchBy03E8
OfferDataRadiationsBizOfferSearchBy0000-Escort

BizOfferSearchBy03E8-Escort
OrderConsignment
(默认主库)
OrderConsignmentByCurrentEscortByCurrent
OrderDR
(买家散射)
OrderByBuyer0000

OrderByBuyer03E8
OrderDataRadiationsOrderByBuyer0000-Escort

OrderByBuyer03E8-Escort
OrderDR
(卖家散射)
OrderBySeller0000

OrderBySeller03E8
OrderDataRadiationsOrderBySeller0000-Escort

OrderBySeller03E8-Escort
OrderHistory
(历史库)

Orderby200912_1
Orderby200912_2
Orderby200912_3
OrderHistory
Escort|200912_1
Escort|200912_2
Escort|200912_3
AccountDetailBkUser
(默认主库)
AccountDetailBkUserCurrent
BkDR
(用户散射)
AccountDetailBy0000

AccountDetailBy03E8
DataRadiationsAccountDetailBy0000

AccountDetailBy03E8
BkHistory
(历史库)
..
.
AccountDetailBy200912_1
AccountDetailBy200912_2
AccountDetailBy200912_3
BkHistory
200912_1
200912_2
200912_3…
说明: Escort,是交易类型,此处只是举例,应用中可能是其它标识。

  除了上面的映射,自定义映射还有一些,顺着IstorageAliasTeller的实现类去寻找吧。

转载于:https://www.cnblogs.com/hb_cattle/articles/2029325.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/448299.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

靠边停车

什么是靠边停车 靠边停车是大路考中一个指标很明确的考核项目,要求学员驾驶车辆使之靠边停下。 操作方法 1、停车前,要通过内、外后视镜观察后方和右侧交通情况,开右转向灯。 2、适量踩下制动踏板。 3、向右转动方向盘(第一把轮…

RuntimeException 和 Exception 区别、异常的子父级关系

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 1.java 将所有的错误封装为一个对象,其根本父类为Throwable, Throwable 有两个子类:Error 和 Exception。 2.Err…

通过路口

操作方法 1、让车减速 2、观察路口的情况 3、通过路口 注意事项 1、不要抢黄灯 2、不要开英雄车,即红灯亮起时通过路口的最后一辆车 3、控制车速,控制在50km/h以下 4、看到左右车都减速时,也马上减速 5、要左转…

C语言笔记(关键字)

gdb调试 gcc 源程序 -g;加gdb调试信息gdb可执行程序;(gdb调试)l(ist):查看源码,按一下从main开始10行以此往后l n:查看n处上下10行的源码run:运行程序b&…

自定义 Git - Git 钩子 (自动部署)

Git 钩子 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 和其它版本控制系统一样,Git 能在特定的重要动作发生时触发自定义脚本。 有两组这样的钩子:客户端的和…

变更车道

操作方法 1、观察与判断观察车辆后方、侧方和准备变更的车道上的交通流情况; 2、确认安全后,打开转向指示灯示意,并再次通过后视镜观察两侧道路上有无车辆超越,确认准备驶入的车道是否允许留有安全距离; 3…

C语言笔记(符号)

注释符号 几个似非而是的注释问题 例子: (A) int / * ... * /i; (B) char * s "abcdefgh //hijklmn"; (C) //Is it a \valid comment? (D) in/ * ... * /t i; 我们知道C语言里可以有两种注释方式:“/* */” 和 “ // ”。那么上面几条…

直线行驶

考核要求 保证跟车安全速度和安全距离,了解车辆行驶速度、注意观察路面状况,采取相应措施。不能有左右摆动、方向不稳的现象。 考试口诀 一.寻找中心 二.双眼锁定本车能通行的中心 三.心理想着走中间 四.双眼从本车前面最突出点的.中心…

java 命令: jmap 命令使用 ( 查看内存使用、设置 )

jdk安装后会自带一些小工具,jmap命令(Java Memory Map)是其中之一。主要用于打印指定Java进程(或核心文件、远程调试服务器)的共享对象内存映射或堆内存细节。 jmap命令可以获得运行中的jvm的堆的快照,从而可以离线分析堆,以检查内存泄漏&am…

第一节 接口概述 [转贴]

接口(interface)用来定义一种程序的协定。实现接口的类或者结构要与接口的定义严格一致。有了这个协定,就可以抛开编程语言的限制(理论上)。接口可以从多个基接口继承,而类或结构可以实现多个接口。接口可以…

获取本机用户名、MAC地址、IP地址、硬盘ID、CPU序列号、系统名称、物理内存

我们在利用C#开发桌面程序(Winform)程序的时候,经常需要获取一些跟系统相关的信息,例如用户名、MAC地址、IP地址、硬盘ID、CPU序列号、系统名称、物理内存等。 首先需要引入命名空间: using System.Management; //…

只用一套解决方案,就可解决80%的交通物流行业信息难题

行业背景 新中国成立70多年来,中国交通运输总体上已经形成了多节点、全覆盖的综合运输网络,“五纵五横”综合运输大通道基本贯通,一大批综合客运、货运枢纽站场(物流园区)投入运营,取得了一系列瞩目成果&am…

起步

什么是开车起步 起步,即发动汽车,使汽车开始走动。 操作方法 1、“踩”离合器 2、“挂”一档 3、“打”左转向灯(提醒后车驾驶员注意安全避让) 4、“鸣”号(引起车辆周围的行人或机动车等注意&…

Linux 使用 jstat 命令查看 jvm 的 GC 情况

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 Options,选项,我们一般使用 -gcutil 查看gc情况 vmid,VM的进程号,即当前运行的java进程号…

WKWebview加载本地图片时出现路径问题

出现问题情况是:将uiwebview换成wkwebview之后,之前将webview上那些图片放回本地下载后缓存的图片没办法在webview上找到,最后排查出原因是,wkwebview需要将图片和网页文件放在同个路径下。 具体实现方法如下(获取图片缓存的路径&…

上车准备

上车准备一 1、环视车辆、确保安全。 2、调整驾驶座。身体坐正,双手向前伸直,以手腕刚好能放在方向盘上为准。左脚搁在离合器上,右脚搁在刹车上,小腿和大腿成90度。左脚可以轻松把离合器踩到底。身体离方向盘20—25厘米。 …

C++复习

register关键字请求“编译器”将局部变量存储于寄存器中 C语言中无法取得register变量地址 在C中依然支持register关键字 1、C编译器有自己的优化方式,不使用register也可能做优化 2、C中可以取得register变量的地址 C编译器发现程序中需要取register变量的地址…

Docker 方式安装 Nginx 、阿里云服务器上装 Ngnix

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到教程。 非 Docker 方式安装,直接 Linux 安装见另一文:Linux 上 安装 nginx 、阿里云服务器上安装 nginx 1. 直接从镜像仓…

C#实现A*算法

理解A*寻路算法具体过程 这两天研究了下 A* 寻路算法, 主要学习了这篇文章, 但这篇翻译得不是很好, 我花了很久才看明白文章中的各种指代. 特写此篇博客用来总结, 并写了寻路算法的代码, 觉得有用的同学可以看看. 另外因为图片制作起来比较麻烦, 所以我用的是原文里的图片. 当…

路考口诀

路考口诀一 一踩(踩离合)、二挂(挂一档)、三看(看倒车镜)、四转(转向灯)、五按(按喇叭)、六手刹、七走 路考口诀二 01.路考之道很轻松,牢…