JRT对历史表和$get实现

由于Cache没有什么表数据大了查询和插入性能下降的问题,而关系库在数据量上千万后会性能下降,之前关注点都是Java业务脚本化和开发部署简单,还没管关系库单表大问题和级联查询复杂后慢的问题,现在开始解决这俩问题,这是第一阶段实现。

首先对单表数据太多导致性能下降的问题还是采用历史表解决,既为主业务建立几个甚至上10个历史表,历史表的主键不自增,然后ORM提供API把数据迁移到历史表。

首先给表注解加上维护历史表的地方和历史数据切割所用的字段:
在这里插入图片描述
实现两个兼容查历史数据的API给业务

/*** 根据条件+字段查询,查询结果按指定的页面把数据按List返回。由ORM根据参数决定顺带查询所有历史数据或者最近历史数据,或者不查历史数据* 开发者为主业务表建立同结果非自增主键的表来供业务数据往历史表迁移,在实体特性维护所有历史表,历史表按每个存满指定数据后换下一个历史表,* ORM按历史表依次调用查询合并结果* 以此解决单表数据太大查询和插入慢问题* 不分页** @param findHisNum  查询历史表的数量,0:不查历史表 大于0的数就是最多查从最近使用历史表往前推的指定数量的历史表* @param model       实体对象* @param param       查询条件参数,数据列名和值的键对* @param orderFields 排序字段,如RowID Desc* @param pageSize    页大小。为-1,无条件查所有数据* @param pageIndex   第几页。为-1,无条件查询所有数据* @param fields      显示字段,为空显示所有列,字段名称以英文','隔开,如:RowID,Code,Name* @param joiner      连接符,为空或不给则查询条件以且连接,给的话长度比参数少1* @param operators   操作符,为空或不给的话条件以等来判断,给的话与参数长度一致。如!=,<,>* @param <T>         限定实体类型* @return 查询实体列表List<T>*/public <T> List<T> DolerFindAll(int findHisNum, T model, List<ParamDto> param, String orderFields, int pageSize, int pageIndex, String fields, List<String> joiner, List<String> operators) throws Exception{//存储返回的对象数据List<T> retList = new ArrayList<T>();//查询起始行数int fromRow = -1;//查询结束行数int toRow = -1;//是否查询全部数据boolean findAll = false;//记录总行数int rowCount = 0;//处理显示字段if (fields != null && !fields.isEmpty()) {fields = "," + fields + ",";}//如果未传入分页数据其中一个未-1,则认为部分页而查询所有数据if (pageIndex == -1 || pageSize == -1) {findAll = true;}//计算查询起始和结束行数else {fromRow = (pageIndex - 1) * pageSize;toRow = pageIndex * pageSize;}PreparedStatement pstat = null;ResultSet rst = null;//表信息JRT.DAL.ORM.Common.TableInfo tableInfo = JRT.DAL.ORM.Common.ModelToSqlUtil.GetTypeInfo(model);List<TableInfo> findTableList=new ArrayList<>();findTableList.add(tableInfo);if(findHisNum>0){String HisTableName=tableInfo.TableInfo.HisTableName();if(!HisTableName.isEmpty()){String [] HisTableNameArr=HisTableName.split("^");boolean StartCal=false;for(int h=HisTableNameArr.length-1;h>=0;h--){String HisModelName=HisTableNameArr[h];Class cHis = GetTypeByName(HisModelName);if (cHis != null) {Object oHis = cHis.getConstructor().newInstance();String UseHisTableName=TryGetUseHisTableName(model.getClass().getSimpleName(),oHis);if(UseHisTableName!=null&&!UseHisTableName.isEmpty()){StartCal=true;}if(StartCal==true&&findHisNum>0) {findHisNum--;TableInfo tableInfoHis = JRT.DAL.ORM.Common.ModelToSqlUtil.GetTypeInfo(oHis);findTableList.add(tableInfoHis);}}}//所有的历史表都没数据那么认为第一个历史表在用if(StartCal==false){CurHisTable.put(model.getClass().getSimpleName(),"");}}}//循环查多个表的数据for(TableInfo tbInfo:findTableList) {//根据表信息将查询参数组装成Select SQLString sql = JRT.DAL.ORM.Common.ModelToSqlUtil.GetSelectSqlByTableInfo(Manager().GetIDbFactory(factoryName), tbInfo, param, operators, joiner, orderFields, false, -1);//写SQL日志JRT.Core.Util.LogUtils.WriteSqlLog("执行QueryAll返回List<T>查询SQL:" + sql);Class<?> clazzz = model.getClass();try {pstat = Manager().Connection().prepareStatement(sql);String paraSql = DBParaUtil.SetDBPara(pstat, param);rst = pstat.executeQuery();JRT.Core.Util.LogUtils.WriteSqlLog("参数:" + paraSql);while (rst.next()) {rowCount++;     //总行数加一//查询全部,或者取分页范围内的记录if (findAll || (rowCount > fromRow && rowCount <= toRow)) {T obj = (T) clazzz.getConstructor().newInstance();for (int coli = 0; coli < tbInfo.ColList.size(); coli++) {String name = tbInfo.ColList.get(coli).Name;Object value = rst.getObject(name);JRT.Core.Util.ReflectUtil.SetObjValue(obj, name, value);}retList.add(obj);}}} catch (Exception ex) {//查询异常清空数据retList.clear();throw ex;}//操作结束释放资源,但是不断连接,不然没法连续做其他数据库操作了finally {if (rst != null) {rst.close();}if (pstat != null) {pstat.close();}//如果上层调用未开启事务,则调用结束释放数据库连接if (!Manager().Hastransaction) {manager.Close();}}}return retList;}/*** 根据条件+字段查询,查询结果按指定的页面把数据按JSON返回;* 开发者为主业务表建立同结果非自增主键的表来供业务数据往历史表迁移,在实体特性维护所有历史表,历史表按每个存满指定数据后换下一个历史表* ORM按历史表依次调用查询合并结果* 以此解决单表数据太大查询和插入慢问题* 该方法不带分页* @param findHisNum  查询历史表的数量,0:不查历史表 大于0的数就是最多查从最近使用历史表往前推的指定数量的历史表* @param model       实体对象* @param param       查询条件参数,数据列名和值的键对* @param orderFields 排序字段,如RowID Desc* @param returnCount 是否输出数据总行数* @param pageSize    页大小。为-1,无条件查所有数据* @param pageIndex   第几页。为-1,无条件查询所有数据* @param fields      显示字段,为空显示所有列,字段名称以英文','隔开,如:RowID,Code,Name* @param joiner      连接符,为空或不给则查询条件以且连接,给的话长度比参数少1* @param operators   操作符,为空或不给的话条件以等来判断,给的话与参数长度一致。如!=,<,>* @param top         查询返回的行数,-1就返回所有行* @return 查询json串*/public <T> String DolerQueryAllTop(int findHisNum,T model, List<ParamDto> param, String orderFields, boolean returnCount, int pageSize, int pageIndex, String fields, List<String> joiner, List<String> operators, int top) throws Exception{//json数据组装容器StringBuilder jsonsb = new StringBuilder();//查询起始行数int fromRow = -1;//查询结束行数int toRow = -1;//是否查询全部数据boolean findAll = false;//记录总行数int rowCount = 0;//处理显示字段if (fields != null && !fields.isEmpty()) {fields = "," + fields + ",";}//如果未传入分页数据其中一个未-1,则认为部分页而查询所有数据if (pageIndex == -1 || pageSize == -1) {findAll = true;}//计算查询起始和结束行数else {fromRow = (pageIndex - 1) * pageSize;toRow = pageIndex * pageSize;}PreparedStatement pstat = null;ResultSet rst = null;JRT.DAL.ORM.Common.TableInfo tableInfo = JRT.DAL.ORM.Common.ModelToSqlUtil.GetTypeInfo(model);List<TableInfo> findTableList=new ArrayList<>();findTableList.add(tableInfo);if(findHisNum>0){String HisTableName=tableInfo.TableInfo.HisTableName();if(!HisTableName.isEmpty()){String [] HisTableNameArr=HisTableName.split("^");boolean StartCal=false;for(int h=HisTableNameArr.length-1;h>=0;h--){String HisModelName=HisTableNameArr[h];Class cHis = GetTypeByName(HisModelName);if (cHis != null) {Object oHis = cHis.getConstructor().newInstance();String UseHisTableName=TryGetUseHisTableName(model.getClass().getSimpleName(),oHis);if(UseHisTableName!=null&&!UseHisTableName.isEmpty()){StartCal=true;}if(StartCal==true&&findHisNum>0) {findHisNum--;TableInfo tableInfoHis = JRT.DAL.ORM.Common.ModelToSqlUtil.GetTypeInfo(oHis);findTableList.add(tableInfoHis);}}}//所有的历史表都没数据那么认为第一个历史表在用if(StartCal==false){CurHisTable.put(model.getClass().getSimpleName(),HisTableNameArr[0]);}}}//如果返回总行数,返回总行数写法if (returnCount) {jsonsb.append("{");jsonsb.append("\"rows\":[");}//否则采用普通数组写法else {jsonsb.append("[");}StringBuilder rowAllsb = new StringBuilder();//循环查多个表的数据for(TableInfo tbInfo:findTableList) {//根据表信息将查询参数组装成Select SQLString sql = JRT.DAL.ORM.Common.ModelToSqlUtil.GetSelectSqlByTableInfo(Manager().GetIDbFactory(factoryName), tbInfo, param, operators, joiner, orderFields, false, top);//写SQL日志JRT.Core.Util.LogUtils.WriteSqlLog("执行QueryAll返回String查询SQL:" + sql);try {pstat = Manager().Connection().prepareStatement(sql);String paraSql = DBParaUtil.SetDBPara(pstat, param);rst = pstat.executeQuery();JRT.Core.Util.LogUtils.WriteSqlLog("参数:" + paraSql);//标识是否第一行boolean isFirstRow = true;while (rst.next()) {rowCount++;     //总行数加一//查询全部,或者取分页范围内的记录if (findAll || (rowCount > fromRow && rowCount <= toRow)) {ResultSetMetaData metaData = rst.getMetaData();//获取列数int colCount = metaData.getColumnCount();//单行数据容器StringBuilder rowsb = new StringBuilder();rowsb.append("{");//标识是否第一列boolean isFirstCol = true;for (int coli = 1; coli <= colCount; coli++) {//获取列名String colName = metaData.getColumnName(coli);//获取列值Object colValue = rst.getObject(coli);if (colValue == null) colValue = "";//如果传了显示的字段,过滤不包含的字段if (fields != null && !fields.isEmpty() && fields.indexOf("," + colName + ",") < 0) {continue;}if (isFirstCol) {rowsb.append("\"" + colName + "\":");rowsb.append("\"" + colValue + "\"");isFirstCol = false;} else {//非第一列插入","rowsb.append(",");rowsb.append("\"" + colName + "\":");rowsb.append("\"" + colValue + "\"");}}rowsb.append("}");if (isFirstRow) {rowAllsb.append(rowsb.toString());isFirstRow = false;} else {rowAllsb.append(",");rowAllsb.append(rowsb.toString());}}}} catch (Exception ex) {//查询异常清空数据记录容器rowAllsb.delete(0, rowAllsb.length());throw ex;}//操作结束释放资源,但是不断连接,不然没法连续做其他数据库操作了finally {if (rst != null) {rst.close();}if (pstat != null) {pstat.close();}//如果上层调用未开启事务,则调用结束释放数据库连接if (!Manager().Hastransaction) {manager.Close();}}}//组装数据记录jsonsb.append(rowAllsb.toString());//补充数组结尾符jsonsb.append("]");if (returnCount) {jsonsb.append(",");jsonsb.append("\"total\":");jsonsb.append(rowCount);jsonsb.append("}");}return jsonsb.toString();}

对应$get的实现,首先抽取节点对象,记录每个数据的时间

package JRT.DAL.ORM.Global;/*** 一个global的管理节点*/
public class OneGlobalNode {/*** Java内部时间*/public Long Time;/*** 对象数据*/public Object Data;
}

实现缓存管理

package JRT.DAL.ORM.Global;import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.atomic.AtomicInteger;import JRT.Core.MultiPlatform.JRTConfigurtaion;
import JRT.Core.Util.LogUtils;
import JRT.DAL.ORM.Global.OneGlobalNode;/*** 实现内存模拟global的效果*/
public class GlobalManager {/*** 在内存里缓存热点数据*/private static ConcurrentHashMap<String, ConcurrentHashMap<String, OneGlobalNode>> AllHotData = new ConcurrentHashMap<>();/*** 要缓存数据的队列*/private static ConcurrentLinkedDeque TaskQuen = new ConcurrentLinkedDeque();/*** 管理缓存的定时器*/private static Timer ManageTimer = new Timer();/*** 缓存的最大对象数量*/public static Integer GlobalCacheNum = 100000;/*** 当前的缓存数量*/private static AtomicInteger CurCacheNum=new AtomicInteger(0);/*** 最后删除数据的时间*/private static Long LastDeleteTime=null;/*** 加入缓存,直接缓存,具体的后续有缓存管理器线程维护缓存,这里只管加入队列即可** @param obj* @throws Exception*/public static void InCache(Object obj) throws Exception{TaskQuen.add(JRT.Core.Util.JsonUtil.CloneObject(obj));}/*** 通过主键查询数据* @param model* @param id* @param <T>* @return* @throws Exception*/public static <T> T DolerGet(T model,Object id) throws Exception{//实体的名称String modelName = model.getClass().getName();if(AllHotData.containsKey(modelName)){//命中数据,克隆返回if(AllHotData.get(modelName).containsKey(id)){Object obj=JRT.Core.Util.JsonUtil.CloneObject(AllHotData.get(modelName).get(id));return (T)obj;}}return null;}/*** 启动缓存数据管理的线程*/public static void StartGlobalManagerTask() throws Exception{//最大缓存数量String GlobalCacheNumConf = JRTConfigurtaion.Configuration("GlobalCacheNum");if (GlobalCacheNumConf != null && !GlobalCacheNumConf.isEmpty()) {GlobalCacheNum = JRT.Core.Util.Convert.ToInt32(GlobalCacheNumConf);}//定时任务TimerTask timerTask = new TimerTask() {@Overridepublic void run() {try {//缓存队列的数据并入缓存while (TaskQuen.size() > 0) {//处理要加入缓存的队列DealOneDataQuen();}//清理多余的缓存数据,这里需要讲究算法,要求在上百万的缓存数据里快速找到时间最久远的数据if(CurCacheNum.get()>GlobalCacheNum){//每轮清理时间处于上次清理时间和当前时间前百分之5的老数据long Diff=(JRT.Core.Util.TimeParser.GetTimeInMillis()-LastDeleteTime)/20;//留下数据的最大时间long LeftMaxTime=LastDeleteTime+Diff;//遍历所有的热点数据for (String model : AllHotData.keySet()) {ConcurrentHashMap<String, OneGlobalNode> oneTableHot=AllHotData.get(model);//记录要删除的数据List<String> delList=new ArrayList<>();for (String key : oneTableHot.keySet()) {OneGlobalNode one=oneTableHot.get(key);//需要删除的数据if(one.Time<LeftMaxTime){delList.add(key);}}//移除时间久的数据for(String del:delList){oneTableHot.remove(del);}}}//清理时间久远的缓存数据} catch (Exception ex) {LogUtils.WriteExceptionLog("处理Global缓存异常", ex);}}};ManageTimer.schedule(timerTask, 0, 500);}/*** 处理队列里的一条数据并入缓存*/private static void DealOneDataQuen() {try {Object obj = TaskQuen.pop();if (obj != null) {JRT.DAL.ORM.Common.TableInfo tableInfo = JRT.DAL.ORM.Common.ModelToSqlUtil.GetTypeInfo(obj);//实体的名称String modelName = obj.getClass().getName();//得到数据的主键String id = tableInfo.ID.Value.toString();if (!AllHotData.containsKey(modelName)) {ConcurrentHashMap<String, OneGlobalNode> map = new ConcurrentHashMap<>();AllHotData.put(modelName, map);}//更新数据if (AllHotData.get(modelName).containsKey(id)) {AllHotData.get(modelName).get(id).Data = obj;AllHotData.get(modelName).get(id).Time = JRT.Core.Util.TimeParser.GetTimeInMillis();}//加入到缓存else {OneGlobalNode node = new OneGlobalNode();node.Data = obj;node.Time = JRT.Core.Util.TimeParser.GetTimeInMillis();AllHotData.get(modelName).put(id, node);//缓存数量加1CurCacheNum.addAndGet(1);//记录时间if(LastDeleteTime==null){LastDeleteTime=JRT.Core.Util.TimeParser.GetTimeInMillis();}}}} catch (Exception ex) {LogUtils.WriteExceptionLog("处理Global缓存添加异常", ex);}}
}

实现DolerGet方法

/*** 通过主键查询数据,带缓存的查询,用来解决关系库的复杂关系数据获取,顶替Cache的$g* @param model 实体* @param id 主键* @param <T>* @return* @throws Exception*/public <T> T DolerGet(T model,Object id) throws Exception{T ret=GlobalManager.DolerGet(model,id);//命中缓存直接返回if(ret!=null){return ret;}else{//调用数据库查询ret=GetById(model,id);//通知存入缓存GlobalManager.InCache(ret);}return ret;}

修改数据的API尝试把实体推入缓存队列
在这里插入图片描述
对事务时候在提交事务才推入缓存队列
在这里插入图片描述

在这里插入图片描述

网站初始化时候启动缓存管理器
在这里插入图片描述
配置缓存数量和历史表换表的大小
在这里插入图片描述

这样以一天标本两万的客户算,缓存两天的热点数据应该不会超过100万,和Global一样的给他10-20G的内存做缓存用就行了,这样理论上应该可以在SQL查询的主业务数据后其他分支数据都借助DolerGet得到数据,这里ORM增、删、改方法还没调加入缓存逻辑,增删改之后调用GlobalManager.InCache(ret);再结合TCP通知主站点分发增、删、改信息后更新缓存,缓存能命中的数据就百分百是准确的最新数据,就能达到极高的Get效率,解决关系库复杂维护的问题,思想上高度借鉴Global的$get和缓存思想。缓存的难点一直是怎么把热点数据挑出来,而通过自己实现ORM,然后增、删、改和DolerGet的数据都抛入缓存队列,就可以准确得到热点数据,因为修改的数据和DolerGet请求的数据本身就是最近活动的,然后缓存管理器再把业务放入队列的数据并入缓存,同时清理时间久远的数据,这样内存里的数据就停了的都是热点。

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

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

相关文章

【Java】7. 类型转换和类型判断

7. 类型转换 7.1 基本类型转换 顺箭头&#xff1a;隐式转换&#xff08;自动&#xff09; 逆箭头&#xff1a;强制转换&#xff08;可能造成精度丢失&#xff09; byte a 10; int b a; int c 1000; byte d (byte) c; System.out.println(d); // -24 7.2 包装类型与基…

什么是 dropblock

大家好啊&#xff0c;我是董董灿。 之前介绍过 dropout 算法&#xff0c;它在训练神经网络中&#xff0c;可以随机丢弃神经元&#xff0c;是一种防止网络过拟合的方法。 但是在卷积神经网络中&#xff0c;dropout 的表现却不是很好&#xff0c;于是研究人员又搞了一个“结构化…

第二十章总结

线程简介 Windows操作系统是多任务操作系统&#xff0c;它以进程为单位。一个进程是一个包含有自身地址的程序&#xff0c;每个独立执行的程序都称为进程。也就是说每个正在执行的程序都是一个进程。系统可以分配给每一个进程有一段有限的使用CPU的时间&#xff08;也可以称为…

建筑木模板厂家批发

在建筑施工中&#xff0c;木模板是一种常见且重要的施工材料&#xff0c;用于搭建混凝土浇筑的支撑结构。选择合适的建筑木模板厂家进行批发&#xff0c;对于施工质量和效率至关重要。本文将介绍建筑木模板厂家批发的重要性&#xff0c;并推荐贵港市能强优品木业作为专业的建筑…

【C++初阶】四、类和对象(构造函数、析构函数、拷贝构造函数、赋值运算符重载函数)

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 【C初阶】三、类和对象 &#xff08;面向过程、class类、类的访问限定符和封装、类的实例化、类对象模型、this指针&#xff09; -CSDN博客 引入&#xff1a;类的六个默认成员函数…

如何获取高质量的静态住宅IP代理?常见误区与注意事项

静态住宅IP代理在今天的网络营销领域扮演着至关重要的角色&#xff0c;静态住宅IP代理以其稳定性和高匿名性&#xff0c;为互联网业务提供了一个安全的执行环境。通过模拟真实用户的网络行为&#xff0c;这些IP代理降低了企业在网络营销活动中被识别和封禁的风险。它保护了企业…

基于SpringBoot的手机官方商城系统

基于SpringBoot的手机官方商城系统 摘要&#xff1a;随着电子商务的发展&#xff0c;网上购物已成为人们普遍的购物方式。与此同时&#xff0c;网上支付也得到了迅速的发展&#xff0c;大有赶超传统支付的趋势。在今天这个信息化程度高、生活节奏快的现代社会&#xff0c;传统…

Airtest自动化测试工具实战演练

一开始知道Airtest大概是在年初的时候&#xff0c;当时&#xff0c;看了一下官方的文档&#xff0c;大概是类似Sikuli的一个工具&#xff0c;主要用来做游戏自动化的&#xff0c;通过截图的方式用来解决游戏自动化测试的难题。最近&#xff0c;移动端测试的同事尝试用它的poco库…

Centos7安装配置nginx

快捷查看指令 ctrlf 进行搜索会直接定位到需要的知识点和命令讲解&#xff08;如有不正确的地方欢迎各位小伙伴在评论区提意见&#xff0c;小编会及时修改&#xff09; Centos7安装配置nginx Nginx介绍 Nginx (engine x) 是一个高性能的 HTTP 和 反向代理 服务&#xff0c;也…

浅谈硬件连通性测试几大优势

硬件连通性测试是确保硬件系统正常运行、提高系统可靠性和降低生产成本的关键步骤。在现代工程和制造中&#xff0c;将连通性测试纳入生产流程是一个明智的选择&#xff0c;有助于确保硬件产品的质量和性能达到最优水平。本文将介绍硬件连通性测试的主要优势有哪些! 一、提高系…

计算机网络408

一&#xff1a;计算机网络体系结构 1.计网的概念&#xff0c;组成&#xff0c;功能和分类 一&#xff1a;计算机网络的发展 (3)从功能组成视觉看&#xff1a;分为资源子网和通信子网 2.计网性能指标

C++初阶模板

介绍&#xff1a; 我们先认识以下C中的模板。模板是一种编程技术&#xff0c;允许程序员编写与数据类型无关的代码&#xff0c;它是一种泛型编程的方式&#xff0c;可以用于创建可处理多种数据类型的函数或类&#xff0c;也就是说泛型编程就是编写与类型无关的通用代码&#xf…