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

news2024/10/6 10:34:24

目录

环境

背景

探寻

元数据的加载策略

如何解决

升级版本到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.coloradmin.cn/o/1418406.html

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

相关文章

如何本地搭建Tale博客网站并发布到公网分享好友远程访问——“cpolar内网穿透”

文章目录 前言1. Tale网站搭建1.1 检查本地环境1.2 部署Tale个人博客系统1.3 启动Tale服务1.4 访问博客地址 2. Linux安装Cpolar内网穿透3. 创建Tale博客公网地址4. 使用公网地址访问Tale 前言 今天给大家带来一款基于 Java 语言的轻量级博客开源项目——Tale&#xff0c;Tale…

什么是协程goroutine?

文章目录 一、进程和线程进程和线程的痛点为什么 Java 坚持多线程不选择协程&#xff1f; 二、什么是协程CSP并发模型golang 线程模型和GMP一直创建协程会出什么问题 三、参考 一、进程和线程 进程就是应用程序的启动实例&#xff0c;进程拥有代码和打开的文件资源、数据资源、…

Pytest中doctests的测试方法应用!

在 Python 的测试生态中&#xff0c;Pytest 提供了多种灵活且强大的测试工具。其中&#xff0c;doctests 是一种独特而直观的测试方法&#xff0c;通过直接从文档注释中提取和执行测试用例&#xff0c;确保代码示例的正确性。本文将深入介绍 Pytest 中 doctests 的测试方法&…

【大厂AI课学习笔记】1.2 人工智能的应用(1)

目录 1.2 人工智能的应用 1.2.1 产业中人工智能的应用 金融 教育 医疗 交通 制造 ——智慧金融 智能风控 智能理赔 智能投研 &#xff08;声明&#xff1a;本学习笔记学习原始资料来自于腾讯&#xff0c;截图等资料&#xff0c;如有不合适摘录的&#xff0c;请与我联…

SQL注入-sqli-labs-master第一关

实验环境&#xff1a; Nginx.1.15.11 MySQL&#xff1a;5.7.26 实验步骤&#xff1a; 1.第一步&#xff1a; 在id1后加入一个闭合符号&#xff0c;如果报错&#xff0c;再在后面加上 -- 将后面注释掉&#xff0c;如果不报错&#xff0c;则证明为字符型。 http://127.0.0.1/…

离线生成双语字幕,一键生成中英双语字幕,基于AI大模型,ModelScope

离线生成双语字幕整合包,一键生成中英双语字幕,基于AI大模型 制作双语字幕的方案网上有很多&#xff0c;林林总总&#xff0c;不一而足。制作双语字幕的原理也极其简单&#xff0c;无非就是人声背景音分离、语音转文字、文字翻译&#xff0c;最后就是字幕文件的合并&#xff0c…

Codeforces Round 921 (Div. 2)补题

We Got Everything Covered!&#xff08;Problem - A - Codeforces&#xff09; 题目大意&#xff1a;要求找出一个长度最短的字符串&#xff0c;满足任意由前k个字母组成的长度为n的字符串都是它的子序列。输出字符串。 思路&#xff1a;这道题我本来想的很简单&#xff0c;…

springboot+vue3支付宝接口案例-第二节-准备后端数据接口

springbootvue3支付宝接口案例-第二节-准备后端数据接口&#xff01;今天经过2个小时的折腾。准备好了我们这次测试支付宝线上支付接口的后端业务数据接口。下面为大家分享一下&#xff0c;期间发生遇到了一些弯路。 首先&#xff0c;我们本次后端接口使用的持久层框架是JPA。这…

【复现】广联达-linkworks SQL注入漏洞_31

目录 一.概述 二 .漏洞影响 三.漏洞复现 1. 漏洞一&#xff1a; 四.修复建议&#xff1a; 五. 搜索语法&#xff1a; 六.免责声明 一.概述 LinkWorks是梦龙科技根据企业信息化的多样性、灵活性和复杂性&#xff0c;结合多年从事项目管理、办公管理、事务处理等方面的经验…

sql注入的学习

1.首先我们应该确定sql注入的类型 利用id1 and 11 和id1 and 12 判断是数字类型注入还是字符型注入&#xff0c;如果两者都可以正常显示界面&#xff0c;则为字符型注入&#xff0c;否则是数字型 两个都正常显示&#xff0c;所以为字符型注入&#xff08;也可以使用id2-1&…

浏览器——HTTP缓存机制与webpack打包优化

文章目录 概要强缓存定义开启 关闭强缓存协商缓存工作机制通过Last-Modified If-Modified-Since通过ETag If-None-Match 不使用缓存前端利用缓存机制&#xff0c;修改打包方案webpack 打包webpack 打包名称优化webpack 默认的hash 值webapck其他hash 类型配置webpack打包 web…

Mysql运维篇(三) MySQL备份与恢复

一路走来&#xff0c;所有遇到的人&#xff0c;帮助过我的、伤害过我的都是朋友&#xff0c;没有一个是敌人。如有侵权&#xff0c;请留言&#xff0c;我及时删除&#xff01; 一、物理备份与逻辑备份 1、物理备份&#xff1a;备份数据文件&#xff0c;转储数据库物理文件到某…

isctf---misc

你说爱我&#xff1f;尊嘟假嘟 转换为ook编码 def convert_string(input_str):# 定义转换映射关系mapping {"你说爱我": "Ook.","尊嘟": "Ook!","假嘟": "Ook?"}# 遍历字符串&#xff0c;进行转换for key, va…

谷歌seo内容营销怎么做?

谷歌SEO内容营销就像是在为两个观众准备一场表演&#xff0c;一边是用户&#xff0c;一边则是谷歌搜索引擎&#xff0c;那么所谓的内容营销&#xff0c;你自然需要知道你的观众想看什么&#xff0c;这就是关键词研究&#xff0c;帮你了解用户在搜索什么&#xff0c;然后&#x…

什么是NAT?NAT类型有哪些?

晚上好&#xff0c;我的网工朋友。NAT是一种地址转换技术&#xff0c;它可以将IP数据报文头中的IP地址转换为另一个IP地址&#xff0c;并通过转换端口号达到地址重用的目的。 在大多数网络环境中&#xff0c;我们都需要通过 NAT 来访问 Internet。 NAT作为一种缓解IPv4公网地址…

数据预处理:纠偏

纠偏 1、偏态分布2、什么是纠偏3、数据纠偏 1、偏态分布 频数分布有正态分布和偏态分布之分。正态分布是指多数频数集中在中央位置&#xff0c;两端的频数分布大致对称 偏态分布是指频数分布不对称&#xff0c;集中位置偏向一侧。若集中位置偏向数值小的一侧&#xff0c;称为正…

航空MRO行业研究:2027年全球市场将达1200亿美元

随着全球航空运输业的不断发展&#xff0c;越来越多的航空器进入了使用年限&#xff0c;需要进行维修、修理和大修。这促进了航空MRO市场的不断增长。此外&#xff0c;航空器设备和技术的不断更新换代&#xff0c;也对航空MRO市场带来了新的机遇和挑战。 航空MRO市场是指维修、…

【Android】实现简易购物车功能(附源码)

先上结果&#xff1a; 代码&#xff1a; 首先引入图片加载&#xff1a; implementation com.github.bumptech.glide:glide:4.15.1配置权限清单&#xff1a; <!-- 网络权限 --><uses-permission android:name"android.permission.INTERNET"/><uses…

ubuntu22.04@laptop安装配置VNC服务端

ubuntu22.04laptop安装&配置VNC服务端 1. 源由2. 系统安装3. VNC安装3.1 系统更新3.2 lightdm安装3.3 x11vnc安装3.4 x11vnc配置3.5 x11vnc自启动3.6 x11vnc状态查询 4. 演示视频 1. 源由 最近系统搞TensorFlow, OpenCV, Python&#xff0c;折腾各种环境&#xff0c;把系统…

数据库管理-第139期 做大还是做小-Oracle名称哪些事(20240125)

数据库管理139期 2024-01-25 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09;1 问题2 排查3 扩展总结 第139期 做大还是做小-Oracle名称哪些事&#xff08;20240125&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle A…