分表过多引起的问题/Apache ShardingSphere元数据加载慢

目录

环境

背景

探寻

元数据的加载策略

如何解决

升级版本到5.x

调大max.connections.size.per.query

max.connections.size.per.query分析

服务启动阶段相关源码

服务运行阶段相关源码

受到的影响

注意事项(重要)

其他


环境

  • Spring Boot 2.2.13
  • Sharding JDBC 4.1.1

背景

因项目特殊性问题,系统需要处理大量数据,有多个数据源,且因数据过多每个数据源都有分表,导致启动时加载过慢

2024-01-10 10:12:25:088[main][INFO][][c.alibaba.druid.pool.DruidDataSource.init(1009)]{dataSource-1} inited
2024-01-10 10:12:25:243[main][INFO][][ShardingSphere-metadata.loadShardingSchemaMetaData(131)]Loading 5 logic tables' meta data.
2024-01-10 10:12:25:527[main][INFO][][ShardingSphere-metadata.load(70)]Loading 4947 tables' meta data.
2024-01-10 10:13:14:312[main][INFO][][ShardingSphere-metadata.createMetaData(59)]Meta data load finished, cost 49078 milliseconds.

日志信息中,可以看出其中一个数据源ShardingSphere正在加载大量的表元数据(近5000个表)。耗时接近一分钟

探寻

元数据的加载策略

ShardingSphere元数据的加载策略和优化方式

  • 使用 SQL 查询替换原生 JDBC 驱动连接:在 5.0.0-beta 版本之前,采用的方式是通过原生 JDBC 驱动原生方式加载。在 5.0.0-beta 版本中,逐步采用了使用数据库方言,通过 SQL 查询的方式,多线程方式实现了元数据的加载,进一步提高了系统数据加载的速度。
  • 减少元数据的加载次数:对于系统通用的资源的加载,遵循一次加载,多处使用。在这个过程中,也要权衡空间和时间,不断的进行优化,减少元数据的重复加载,提高系统整体的效率。

如何解决

升级版本到5.x

升级版本到5.x【5.x版本对元数据的加载做了优化:多线程加载,且相同分表只加载一个】

调大max.connections.size.per.query

(记得看最后注意事项)

max.connections.size.per.query是ShardingSphere中的参数,表示每个查询请求在每个分片中能够使用的最大连接数, 也就是执行sql的时候,对每一个数据库进行操作的时候的connection数量

在 application.properties 或 application.yml 文件中添加自定义配置来调整每个查询请求在每个分片中能够使用的最大连接数

spring.shardingsphere.datasource.[name].max-connections-size-per-query=20

其中,[name] 是数据源名称。你可以根据实际情况调整 max-connections-size-per-query 的值。
重新启动应用程序,新的配置将生效。

如果有个性化数据源,可以这么修改

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;@Bean(name = "dataSourceSharding")
public DataSource getShardingDataSource(@Qualifier("dataSource") DataSource dataSource) throws SQLException {// 分表规则ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();shardingRuleConfig.getTableRuleConfigs().add(/** user分表规则 */);//数据源Map<String, DataSource> result = new HashMap<>(Numbers.INT_16);result.put("dataSource", dataSourceBill);Properties properties = new Properties();properties.put(ConfigurationPropertyKey.MAX_CONNECTIONS_SIZE_PER_QUERY.getKey(), 20);return ShardingDataSourceFactory.createDataSource(result, shardingRuleConfig, properties);
}

max.connections.size.per.query分析

升级版本需要考虑的太多了, 还是分析下修改max.connections.size.per.query的影响吧

分析源代码发现,元数据的加载可以是单线程串行加载,也可以是多线程并行加载,而使用哪种策略,最终基于sharding-jdbc的一个配置:max.connections.size.per.query

max.connections.size.per.query默认值是1,此时元数据加载是单线程串行加载。而配置大于1时,会根据该配置的值,采用多线程并行加载。

修改这个参数,受影响的有启动时加载元数据和sql执行时

服务启动阶段相关源码

{@link org.apache.shardingsphere.sql.parser.binder.metadata.schema.SchemaMetaDataLoader#load}

    /*** Load schema meta data.** @param dataSource data source* @param maxConnectionCount count of max connections permitted to use for this query* @param databaseType database type* @return schema meta data* @throws SQLException SQL exception*/public static SchemaMetaData load(final DataSource dataSource, final int maxConnectionCount, final String databaseType) throws SQLException {List<String> tableNames;try (Connection connection = dataSource.getConnection()) {tableNames = loadAllTableNames(connection, databaseType);}log.info("Loading {} tables' meta data.", tableNames.size());if (0 == tableNames.size()) {return new SchemaMetaData(Collections.emptyMap());}List<List<String>> tableGroups = Lists.partition(tableNames, Math.max(tableNames.size() / maxConnectionCount, 1));Map<String, TableMetaData> tableMetaDataMap = 1 == tableGroups.size()? load(dataSource.getConnection(), tableGroups.get(0), databaseType) : asyncLoad(dataSource, maxConnectionCount, tableNames, tableGroups, databaseType);return new SchemaMetaData(tableMetaDataMap);}private static Map<String, TableMetaData> load(final Connection connection, final Collection<String> tables, final String databaseType) throws SQLException {try (Connection con = connection) {Map<String, TableMetaData> result = new LinkedHashMap<>();for (String each : tables) {result.put(each, new TableMetaData(ColumnMetaDataLoader.load(con, each, databaseType), IndexMetaDataLoader.load(con, each, databaseType)));}return result;}}

maxConnectionCount对应的就是max.connections.size.per.query

服务运行阶段相关源码

假设我们的用户很多,进行了分表,分表数量10,对应的表为:user_1,user_10
当我们在查询用户,如select * from user where name='张三',这个是逻辑sql
sharding-jdbc会将逻辑sql改写成真实sql,也就是这样:

select * from user_1 where name='张三'
...
select * from user_10 where name='张三'
共10条真实sql

{@link org.apache.shardingsphere.sharding.execute.sql.prepare.SQLExecutePrepareTemplate#getSQLExecuteGroups}

{@link org.apache.shardingsphere.shardingjdbc.jdbc.adapter.AbstractConnectionAdapter#createConnections }

这两处源码涉及的max.connections.size.per.query包括两点:

  • 计算需要一次性获取多少个连接去执行所有的真实sql;
  • 归并方式,也就是源码中的ConnectionMode,它分为两种,一种叫内存限制模式,一种叫连接限制模式

当max.connections.size.per.query小于真实sql数量时,走的是连接限制模式(通俗理解:因为连接不够用,需要把sql执行完后,将查询结果先放到内存,然后释放连接用于查询其他sql),反之走的是内存限制模式(连接足够用,每个sql占据一个连接,查询结果不需要一次性放到内存,而是分批次拉取数据,在内存中做归并聚合)。

    private List<InputGroup<StatementExecuteUnit>> getSQLExecuteGroups(final String dataSourceName,final List<SQLUnit> sqlUnits, final SQLExecutePrepareCallback callback) throws SQLException {List<InputGroup<StatementExecuteUnit>> result = new LinkedList<>();int desiredPartitionSize = Math.max(0 == sqlUnits.size() % maxConnectionsSizePerQuery ? sqlUnits.size() / maxConnectionsSizePerQuery : sqlUnits.size() / maxConnectionsSizePerQuery + 1, 1);List<List<SQLUnit>> sqlUnitPartitions = Lists.partition(sqlUnits, desiredPartitionSize);ConnectionMode connectionMode = maxConnectionsSizePerQuery < sqlUnits.size() ? ConnectionMode.CONNECTION_STRICTLY : ConnectionMode.MEMORY_STRICTLY;List<Connection> connections = callback.getConnections(connectionMode, dataSourceName, sqlUnitPartitions.size());int count = 0;for (List<SQLUnit> each : sqlUnitPartitions) {result.add(getSQLExecuteGroup(connectionMode, connections.get(count++), dataSourceName, each, callback));}return result;}

受到的影响

默认情况下,max.connections.size.per.query=1

  • 如果分片数据在两个数据库,默认情况下,执行引擎执行的时候,就是每个数据库都会有一个connection去查询。
  • 如果是一个数据库两个表,就是串行查询的,第一次查询的全部结果会全部放在了内存里面等待第二次查询的结果然后再一起合并

配置的变更影响有三点

  • 启动时加载元数据的逻辑
  • sql执行时的逻辑
  • 查询结果归并的逻辑

注意事项(重要)

  • max.connections.size.per.query的配置不能大于datasource的最大线程数,否则一旦分表数量大,就会因为无法一次获取足够的连接而报错
  • 如果代码中有很多不带分片参数的分表查询,而max.connections.size.per.query又设置的比较大,会极大的消耗数据库连接,可能导致其他业务逻辑无法获取连接而报错
  • 如果代码中有不带分片参数的分表查询,而max.connections.size.per.query又设置的比较小,会走连接限制模式,所有数据会放到内存后再做聚合,如果查询结果较大,可能爆掉内存;
  • 只要代码中避免掉不带分片参数的查询更新操作,适当加大max.connections.size.per.query的值,可以提升启动速度而不会对项目的运行造成任何影响。

其他

Apache ShardingSphere分表的简单使用和常见问题-CSDN博客

持续更新ing!

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

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

相关文章

【linux】Debian防火墙

Debian系统默认没有安装防火墙&#xff0c;但用户可以根据需要自行选择并安装一个防火墙以增强系统安全性。 一、查看Debian 桌面系统的防火墙是否关闭 在Debian及其他基于Linux的桌面系统中&#xff0c;防火墙功能通常是由iptables或nftables规则集控制的&#xff0c;而ufw&…

docker容器启动后修改或添加端口 nacos容器 版本2.x需要额外开放9848、9849

1.输入docker ps -a查看需要修改的容器ID: 记录下、 docker ps -a 2.停止docker systemctl stop docker 3.进入docker 容器文件夹&#xff0c;找到对应容器的位置&#xff1a; docker的默认文件夹应该是/var/lib/docker 如果不是root用户查看的话&#xff0c;可能会出现权限…

LeetCode 热题 100 | 子串

目录 1 560. 和为 K 的子数组 2 239. 滑动窗口最大值 3 76. 最小覆盖子串 菜鸟做题第二周&#xff0c;语言是 C 1 560. 和为 K 的子数组 题眼&#xff1a;“子数组是数组中元素的连续非空序列。” 解决本问题的关键就在于如何翻译问题。子数组 s 的和可以看作数组 i 的…

智能充电桩,机器人 wifi蓝牙 解决方案

新联鑫威低功耗高性价比sdio wifi/蓝牙combo的模块单频2.4g的CYWL6208&#xff0c;双频2.4g/5g CYWL6312可以应用到一些低延时 高性能 低功耗 联网需求的交流直流充电桩&#xff0c;扭力扳手&#xff0c;agv机器人&#xff0c;目前支持主流的stm32F4/GD32F4 瑞萨 psoc的主控&am…

Shell脚本——循环语句(for、while和until循环)

一、命令 1.echo命令 echo -n 表示不换行输出 echo -e 输出转义字符&#xff0c;将转义后的内容输出到屏幕上 常见转义字符&#xff1a; \b 相当于退格键 转义后相当于退格键&#xff08;backspace&#xff09;&#xff0c;但是前提是“\b”存在字符。“\b”表示删除前一个…

Maven(上):Maven介绍、安装配置及工程构建

1. Maven介绍 Maven 是一款为 Java 项目管理构建、依赖管理的工具&#xff08;软件&#xff09;&#xff0c;使用 Maven 可以自动化构建、测试、打包和发布项目&#xff0c;大大提高了开发效率和质量。 Maven就是一个软件&#xff0c;掌握安装、配置、以及基本功能 &#xff…

二叉树的最大深度[简单]

优质博文&#xff1a;IT-BLOG-CN 一、题目 给定一个二叉树root&#xff0c;返回其最大深度。 二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null,null,15,7] 输出&#xff1a;3 示例 2&#xff1a…

Java面试——基础篇

目录 1、java语言有哪些优点和缺点? 2、JVM 、 JDK 和 JRE的关系 3、为什么说 Java 语言“编译与解释并存”&#xff1f; 4、Java和c的区别 5、基本数据类型 5.1、java的8种基本数据类型&#xff1a; 5.2、基本类型和包装类型的区别&#xff1a; 5.3、包装类型的缓存机…

【大数据】YARN常用命令及Rest API

YARN 1.YARN常用命令 1.1 作业 命令说明yarn application -list列出所有的applicationyarn application -list -appStates [ALL、NEW、NEW_SAVING、SUBMITTED、ACCEPTED、RUNNING、FINISHED、FAILED、KILLED]根据application状态过滤yarn application -kill [applicationId]…

c# 解决ini中文乱码

乱码仅仅是因为编码规则导致 解码时对应文件的码制即可 public class IniConfig{private string inipath AppDomain.CurrentDomain.BaseDirectory "Config.ini";public bool CanRead(){if (File.Exists(inipath)){return true;}return false;}//声明API函数[DllImp…

大数据开发之Scala

第 1 章&#xff1a;scala入门 1.1 概述 scala将面向对象和函数式编程结合成一种简洁的高级语言 特点 1、scala和java一样属于jvm语言&#xff0c;使用时都需要先编译为class字节码文件&#xff0c;并且scala能够直接调用java的类库 2、scala支持两种编程范式面向对象和函数式…

小黑艰难的前端啃bug之路:内联元素之间的间隙问题

今天开始学习前端项目&#xff0c;遇到了一个Bug调了好久&#xff0c;即使margin为0&#xff0c;但还是有空格。 小黑整理&#xff0c;用四种方法解决了空白问题 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></tit…

应该如何选择轻量级低代码、重量级低代码、轻量级低代码+定制、纯定制软件开发?

引言 在当今数字化时代&#xff0c;软件开发成为推动业务创新和增强竞争力的关键因素。然而&#xff0c;在众多的软件开发方法中&#xff0c;如何选择适合自己项目的方法成为了一个至关重要的问题。本文将探讨轻量级低代码、重量级低代码、轻量级低代码定制、以及纯定制这四种…

[MQ]常用的mq产品图形管理web界面或客户端

一、MQ介绍 1.1 定义 MQ全称为Message Queue&#xff0c;消息队列是应用程序和应用程序之间的通信方法。 如果非要用一个定义来概括只能是抽象出来一些概念&#xff0c;概括为跨服务之间传递信息的软件。 1.2 MQ产品 较为成熟的MQ产品&#xff1a;IBMMQ&#xff08;IBM We…

目标检测 - RCNN系列模型

文章目录 1. RCNN2. Fast-RCNN3. Faster-RCNN 1. RCNN 论文&#xff1a;Rich feature hierarchies for accurate object detection and semantic segmentation 地址&#xff1a;https://arxiv.org/abs/1311.2524 分为两个阶段&#xff1a; 目标候选框Object ProposalsProposal…

算法------(10)堆

例题&#xff1a;&#xff08;1&#xff09;AcWing 838. 堆排序 我们可以利用一个一维数组来模拟堆。由于堆本质上是一个完全二叉树&#xff0c;他的每个父节点的权值都小于左右子节点&#xff0c;而每个父节点编号为n时&#xff0c;左节点编号为2*n&#xff0c;右节点编号为2*…

kubeSphere DevOps自定义容器 指定nodejs版本

✨✨✨✨✨✨ &#x1f380;前言&#x1f381;基于内置镜像构建&#x1f381;把镜像添加基础容器中&#x1f381;检查容器是否配置成功&#x1f381;不生效的原因排查&#x1f381;按步骤执行如下命令 &#x1f380;前言 由于我本地的开发环境node是16.18.1,而自带容器node的版…

VUE项目目录与运行流程(VScode)

各目录对应名称含义 main.js&#xff08;导入App.vue&#xff0c;基于App.vue创建结构渲染index.html&#xff09; //核心作用&#xff1a;导入App.vue&#xff0c;基于App.vue创建结构渲染index.html//1.导入Vue核心包 import Vue from vue//2.导入App.vue根组件 import App f…

MSB20M-ASEMI小功率家电专用MSB20M

编辑&#xff1a;ll MSB20M-ASEMI小功率家电专用MSB20M 型号&#xff1a;MSB20M 品牌&#xff1a;ASEMI 封装&#xff1a;UMSB-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;2A 功率(Pd)&#xff1a;50W 芯片个数&#xff1a;4 引…

20240122面试练习题10

1. Redis为什么执行这么快&#xff1f; 二、Redis为什么这么快&#xff1f; 1、完全基于内存&#xff0c;数据存在内存中&#xff0c;绝大部分请求是纯粹的内存操作&#xff0c;非常快速&#xff0c;跟传统的磁盘文件数据存储相比&#xff0c;避免了通过磁盘IO读取到内存这部分…