sharding-jdbc metadata load优化(4.1.1版本)

news2024/11/16 21:34:51

背景

系统启动时,会注意sharding-jdbc提示加载metadata

于是想看看里面做了什么事情

问题追踪

debug后可以观察走到了该类

org.apache.shardingsphere.shardingjdbc.jdbc.core.context.ShardingRuntimeContext#loadSchemaMetaData

先看这个shardingRuntimeContext相关的类关系,对于多数据源的场景,会定义一个加载元数据的方法供子类实现,主要关注loadSchemaMetaData方法

重点关注ShardingRuntimeContext里的逻辑

对于分库的数据库加载,可以看到有两个问题

1.对于分库的表元数据加载,是单线程执行的,即使把max.connections.size.per.query调大,也不会有效率提升

2.某些规则的元数据与分片表的元数据是一致的,存在重复加载情况

问题解决

分表配置中,某些规则的acutal_data_nodes是一样的,根据该字段分组,对于分表规则相同的配置,可以加载一份即可

在本项目中复写对应的类增强下对应的load方法,新增了groupBy加载的配置支持

//根据配置选择原生加载还是groupBy加载
public SchemaMetaData load(final DatabaseType databaseType) throws SQLException {
        SchemaMetaData result = useGroupMetaLoad?loadShardingSchemaMetaDataGroupByActualDataNodes(databaseType):loadShardingSchemaMetaData(databaseType);
        result.merge(loadDefaultSchemaMetaData(databaseType));
        return result;
    }

//原有逻辑
    private SchemaMetaData loadShardingSchemaMetaData(final DatabaseType databaseType) throws SQLException {
        LOGGER.info("Loading {} logic tables' meta data.", shardingRule.getTableRules().size());
        Map<String, TableMetaData> tableMetaDataMap = new HashMap<>(shardingRule.getTableRules().size(), 1);
        for (TableRule each : shardingRule.getTableRules()) {
            tableMetaDataMap.put(each.getLogicTable(), load(each.getLogicTable(), databaseType));
        }
        return new SchemaMetaData(tableMetaDataMap);
    }
//新逻辑
    private SchemaMetaData loadShardingSchemaMetaDataGroupByActualDataNodes(final DatabaseType databaseType) throws SQLException {
        LOGGER.info("Loading {} logic tables' meta data.", shardingRule.getTableRules().size());
//根据actualDataNodes分组,key为要查询的逻辑表名,value为同个acutalDataNodes的表名
        Map<String, Set<String>> sameNodeMap = groupByActualDataNodes(shardingRule.getShardingDataSourceNames().getShardingRuleConfig().getTableRuleConfigs());
        int maxQuery=Math.min(CORES*2,maxConnectionsSizePerQuery);
        List<List<String>> tableGroups = Lists.partition(new ArrayList<>(sameNodeMap.keySet()), Math.max(sameNodeMap.size() / maxQuery, 1));
        Map<String, TableMetaData> tableMetaDataMap = new HashMap<>(shardingRule.getTableRules().size(), 1);
        if (tableGroups.size() == 1 || isCheckingMetaData) {
            for (String sameLogicTable : tableGroups.get(0)) {
                TableMetaData load = load(sameLogicTable, databaseType);
                Set<String> value = sameNodeMap.get(sameLogicTable);
                for (String s : value) {
                    tableMetaDataMap.put(s, load);
                }
            }
        } else {
            //async,模仿已有写法
            ExecutorService executorService = Executors.newFixedThreadPool(Math.min(tableGroups.size(), maxQuery));
            try {
                Collection<Future<Map<String, TableMetaData>>> futures = new LinkedList<>();
                for (List<String> each : tableGroups) {
                    futures.add(executorService.submit(() -> {
                        Map<String, TableMetaData> tableMetaData = new HashMap<>();
                        for (String s : each) {
                            tableMetaData.put(s, load(s, databaseType));
                        }
                        return tableMetaData;
                    }));
                }
                for (Future<Map<String, TableMetaData>> each : futures) {
                    try {
                        Map<String, TableMetaData> m = each.get();
                        for (String s : m.keySet()) {
                            Set<String> sameTable = sameNodeMap.get(s);
                            for (String string : sameTable) {
                                tableMetaDataMap.put(string, m.get(s));
                            }
                        }
                    } catch (final InterruptedException | ExecutionException ex) {
                        if (ex.getCause() instanceof SQLException) {
                            throw (SQLException) ex.getCause();
                        }
                        Thread.currentThread().interrupt();
                    }
                }
            } finally {
                executorService.shutdown();
            }
        }
        LOGGER.info("Actual {} logic tables' meta data loaded.", sameNodeMap.size());
        return new SchemaMetaData(tableMetaDataMap);
    }

private Map<String, Set<String>> groupByActualDataNodes(Collection<TableRuleConfiguration> configurations) {
        Map<String, List<TableRuleConfiguration>> collect = configurations.stream().collect(Collectors.groupingBy(TableRuleConfiguration::getActualDataNodes));
        Map<String, Set<String>> containSet = new HashMap<>();
        for (Entry<String, List<TableRuleConfiguration>> entry : collect.entrySet()) {
            //同一个datasource分为一组
            Map<String,Set<String>> sameDatabaseMap=new HashMap<>();
            entry.getValue().stream().map(TableRuleConfiguration::getLogicTable).forEach(e->{
                String currentDatabase = getFirstDataSourceNameByLogicTableName(e);
                Set<String> tableNames = sameDatabaseMap.getOrDefault(currentDatabase, new HashSet<>());
                tableNames.add(e);
                sameDatabaseMap.put(currentDatabase,tableNames);
            });
            for (Entry<String, Set<String>> sameDatabaseSet : sameDatabaseMap.entrySet()) {
                Set<String> value = sameDatabaseSet.getValue();
                containSet.put(value.iterator().next(),value);
            }
        }
        return containSet;
    }
    private String getFirstDataSourceNameByLogicTableName(String logicTableName){
        TableRule tableRule = shardingRule.getTableRule(logicTableName);
        DataNode dataNode = tableRule.getActualDataNodes().iterator().next();
        return dataNode.getDataSourceName();
    }

这样就达到了优化的效果

并添加配置

spring.shardingsphere.props.max.connections.size.per.query=10
spring.shardingsphere.props.group.metadata.load.enabled=true

运行结果如下,减少重复的表元数据加载,并采用异步加载,启动速度快了一些

配置修改后的关注问题

可以关注到对于max.connections.size.per.query,我们从1→10,有什么需要注意的呢?

1.createConnections

我们可以先看一下一次查询的执行过程

1.org.apache.shardingsphere.underlying.pluggble.prepare.BasePrepareEngine#prepare

这里主要解析sql,做路由&生成需要执行的单元信息,生成执行上下文

2.接下来是重点关注的逻辑

根据执行上下文初始化执行器

我们需要关注的是生成执行单元中获取链接的逻辑

具体在 org.apache.shardingsphere.shardingjdbc.executor.PreparedStatementExecutor#obtainExecuteGroups

一路debug会走到

我们需要关注的是生成执行单元中获取链接的逻辑

具体在 org.apache.shardingsphere.shardingjdbc.executor.PreparedStatementExecutor#obtainExecuteGroups

一路debug会走到

在这里可以看到该配置的影响,他会根据实际要执行的sql单元,确认链接模式,关于链接模式的进一步说明,可以参考官网说明

链接模式说明
MEMORY_STRICTLY
内存限制模式,当对单个物理库查询时,链接数足够时会采用此模式,单个库有多个链接
CONNECTION_STRICTLY
链接限制模式,对单个物理库查询时,只有0或1个链接可用

这两种限制模式在创建链接时有差异

链接限制模式直接创建,对于内存限制模式,需要一次性获取执行链接

主要影响是单个逻辑sql有多个分片sql执行场景时所需要的连接数,由于我们使用的druid链接池,获取链接的方法为com.alibaba.druid.pool.DruidDataSource#getConnectionDirect

对于配了超时(druid.maxWait),如果maxActive<max.connections.size.per.query,会报无法获取足够链接的错误

对于没配超时,会出现一直等待的场景com.alibaba.druid.pool.DruidDataSource#takeLast,造成假死

重点保证链接池的maxActive>=max.connections.size.per.query即可

2.executeQuery

org.apache.shardingsphere.shardingjdbc.executor.PreparedStatementExecutor#getQueryResult

内存限制模式,会把链接hold住,相当于游标方式获取结果,对于链接限制模式,会把数据加载到内存,链接可以释放

其他

1.本地启动时也可以配置druid链接池进行异步初始化,加快启动速度

spring.datasource.druid.async-init=true

2.在sharding-jdbc执行过程中,会发现如下逻辑

会抽取第一个任务给主线程执行,其他交由线程池异步处理

减少主线程的空等待时间,我们一些异步多任务的执行也可以参考一下该做法 

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

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

相关文章

玉米种子质量检测系统源码分享

玉米种子质量检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer…

数据结构——栈和队列(队列的定义、顺序队列以及链式队列的基本操作)

目录 队列&#xff08;queue&#xff09;的定义 顺序队——队列的顺序表示和实现 顺序队列&#xff08;循环队列&#xff09;的类型定义 顺序队列上溢问题的解决方法 ​编辑 循环队列的基本操作 队列的基本操作——队列的初始化 队列的基本操作——求队列的长度 队列的…

[数据集][目标检测]岩石种类检测数据集VOC+YOLO格式4766张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4766 标注数量(xml文件个数)&#xff1a;4766 标注数量(txt文件个数)&#xff1a;4766 标注…

CurrentHashMap的底层原理

CurrentHashMap在jdk1.8之前使用的是分段锁&#xff0c;在jdk1.8中使用"CAS和synchronized"来保证线程安全。 jdk1.8之前的底层实现 CurrentHashMap在jdk1.8之前&#xff0c;通过Segment段来实现线程安全。 Segment 段 ConcurrentHashMap 和 HashMap 思路是差不多…

TDengine 签约前晨汽车,解锁智能出行的无限潜力

在全球汽车产业转型升级的背景下&#xff0c;智能网联和新能源技术正迅速成为商用车行业的重要发展方向。随着市场对环保和智能化需求的日益增强&#xff0c;企业必须在技术创新和数据管理上不断突破&#xff0c;以满足客户对高效、安全和智能出行的期待。在这一背景下&#xf…

如何通过 PhantomJS 模拟用户行为抓取动态网页内容

引言 随着网页技术的不断进步&#xff0c;JavaScript 动态加载内容已成为网站设计的新常态&#xff0c;这对传统的静态网页抓取方法提出了挑战。为了应对这一挑战&#xff0c;PhantomJS 作为一个无头浏览器&#xff0c;能够模拟用户行为并执行 JavaScript&#xff0c;成为了获…

探索数据结构:初入算法之经典排序算法

&#x1f511;&#x1f511;博客主页&#xff1a;阿客不是客 &#x1f353;&#x1f353;系列专栏&#xff1a;渐入佳境之数据结构与算法 欢迎来到泊舟小课堂 &#x1f618;博客制作不易欢迎各位&#x1f44d;点赞⭐收藏➕关注 一、插入排序 步骤&#xff1a; 从第一个元素开…

OpenAI 刚刚推出 o1 大模型!!突破LLM极限

北京时间 9 月 13 日午夜&#xff0c;OpenAI 正式发布了一系列全新的 AI 大模型&#xff0c;专门用于应对复杂问题。 这一新模型的出现代表了一个重要突破&#xff0c;其具备的复杂推理能力远远超过了以往用于科学、代码和数学等领域的通用模型&#xff0c;能够解决比之前更难的…

Python和R均方根误差平均绝对误差算法模型

&#x1f3af;要点 回归模型误差评估指标归一化均方根误差生态状态指标神经网络成本误差计算气体排放气候算法模型 Python误差指标 均方根误差和平均绝对误差 均方根偏差或均方根误差是两个密切相关且经常使用的度量值之一&#xff0c;用于衡量真实值或预测值与观测值或估…

HarmonyOS开发实战( Beta5.0)骨架屏实现案例实践

鸿蒙HarmonyOS开发往期必看&#xff1a; HarmonyOS NEXT应用开发性能实践总结 最新版&#xff01;“非常详细的” 鸿蒙HarmonyOS Next应用开发学习路线&#xff01;&#xff08;从零基础入门到精通&#xff09; 介绍 本示例介绍通过骨架屏提升加载时用户体验的方法。骨架屏用…

无法加载用户配置文件怎么解决?

你有没有遇到过这种问题&#xff0c;蓝屏提示“User Profile Services服务登录失败。无法加载用户配置文件”。为什么会出现问题呢&#xff1f;可能的原因包括&#xff1a; 用户配置文件损坏&#xff1a;用户的配置文件可能已损坏&#xff0c;导致系统无法读取。 权限问题&…

linux更换阿里镜像源

第一步&#xff1a;进入 /etc/yum.repos.d目录下 cd /etc/yum.repos.d 第二步&#xff1a;编辑 CentOS-Base.repo 打开该文件 vi CentOS-Base.repo 第三步&#xff1a;点击键盘i&#xff0c;进入编辑模式 删除文件的全部内容&#xff1a;将阿里下面配置复制粘贴进取 [base] nam…

Ribbon (WPF)

Ribbon (WPF) 在本文中主要包含以下内容&#xff1a; Ribbon组件和功能应用程序菜单快速访问工具栏增强的工具提示 Ribbon是一个命令栏&#xff0c;它将应用程序的功能组织到应用程序窗口顶部的一系列选项卡中。Ribbon用户界面(UI)增加了特性和功能的可发现性&#xff0c;使用…

神经网络学习笔记——如何设计、实现并训练一个标准的前馈神经网络

1.从零设计并训练一个神经网络https://www.bilibili.com/video/BV134421U77t/?spm_id_from333.337.search-card.all.click&vd_source0b1f472915ac9cb9cdccb8658d6c2e69 一、如何设计、实现并训练一个标准的前馈神经网络&#xff0c;用于手写数字图像的分类&#xff0c;重…

如何制作Vector Vflash中加载的DLL文件--自动解锁刷写过程中27服务

案例背景&#xff1a; vFlash 是一种易于使用的工具&#xff0c;用于对一个或多个 ECU 进行刷写软件。由于方法灵活&#xff0c;它可以支持各种汽车原始设备制造商的不同刷写规范。它支持通过 CAN、CAN FD、FlexRay、LIN、以太网/DoIP 和以太网/SoAd 对 ECU 进行刷写。 vFlas…

SpringSecurity原理解析(六):SecurityConfigurer 解析

1、SecurityConfigurer SecurityConfigurer 在 Spring Security 中是一个非常重要的接口&#xff0c;观察HttpSecurity 中的很多 方法可以发现&#xff0c;SpringSecurity 中的每一个过滤器都是通过 xxxConfigurer 来进行配置的&#xff0c;而 这些 xxxConfigurer 其实都是 Sec…

针对Docker容器的可视化管理工具—DockerUI

目录 ⛳️推荐 前言 1. 安装部署DockerUI 2. 安装cpolar内网穿透 3. 配置DockerUI公网访问地址 4. 公网远程访问DockerUI 5. 固定DockerUI公网地址 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下…

GBI(生成式商业智能)实际业务生产落地运用上的探索和实践

前言 最近在探索如何发展AI在业务上的驱动力时了解到了生成式商业智能这一概念&#xff0c;同时本人也在探索ChatBI这一技术的实际落地运用&#xff0c;其实二者几乎在实现效果层面是一个意思&#xff0c;GBI(Generative Business Intelligence)是偏向业务方面&#xff0c;而C…

[000-01-008].第05节:OpenFeign高级特性-超时控制

我的后端学习大纲 SpringCloud学习大纲 1.1.OpenFeign超时的情况&#xff1a; 在Spring Cloud微服务架构中&#xff0c;大部分公司都是利用OpenFeign进行服务间的调用&#xff0c;而比较简单的业务使用默认配置是不会有多大问题的&#xff0c;但是如果是业务比较复杂&#xff…

UiBot教程:实现复杂流程图的高效方法

在自动化测试和RPA&#xff08;机器人流程自动化&#xff09;领域&#xff0c;使用UiBot绘制复杂流程图是日常工作中常见的挑战之一。如何在繁杂的逻辑中保持高效&#xff1f;如何实现复杂流程的自动化设计而不迷失于其中&#xff1f;这是许多测试工程师和自动化开发者所面临的…