分表过多引起的问题/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.mfbz.cn/a/357537.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据结构3、基于栈的后缀算术表达式求值

1 题目描述 图1 中缀表达式转化为后缀表达式题目描述 图2 基于栈的后缀算术表达式求值题目描述 2 题目解读 借助一个运算符栈&#xff0c;可将中缀表达式转化为后缀表达式&#xff1b;借助一个运算数栈&#xff0c;可对后缀表达式求值。借助一个运算符栈和一个运算数栈&#xf…

MongoDB安装以及卸载

查询id&#xff1a; docker ps [rootlocalhost ~]# docker stop c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rm c7a8c4ac9346 c7a8c4ac9346 [rootlocalhost ~]# docker rmi mongo sudo docker pull mongo:4.4 sudo docker images 卸载旧的 sudo docker stop mong…

Win10无法完成更新正在撤销更改的解决方法

在Win10电脑操作过程中&#xff0c;用户看到了“无法完成更新正在撤销更改”的错误提示&#xff0c;这样系统就不能成功完成更新&#xff0c;不知道如何操作才能解决此问题&#xff1f;以下小编分享最简单的解决方法&#xff0c;帮助大家轻松解决Win10电脑无法完成更新正在撤销…

BIO、NIO编程与直接内存、零拷贝

一、网络通信 1、什么是socket&#xff1f; Socket 是应用层与 TCP/IP 协议族通信的中间软件抽象层&#xff0c;它是一组接口&#xff0c;一般由操作 系统提供。客户端连接上一个服务端&#xff0c;就会在客户端中产生一个 socket 接口实例&#xff0c;服务端每接受 一个客户端…

【Linux网络编程】网络编程套接字(1)

【Linux网络编程】网络编程套接字(1) 目录 【Linux网络编程】网络编程套接字(1)源IP地址和目的IP地址端口号端口号和进程ID的关系 网络通信TCP协议UDP协议网络字节序socket编程接口简单的UDP网络程序 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2024.1.29 前言&#xff1…

SV-7101T网络音频终端 网络对讲终端

SV-7101是一款IP网络广播终端&#xff0c;主要作为网络播放器使用&#xff0c;其接收网络的音频数据&#xff0c;提供音频输出。SV-7101与服务器主控软件、有源音箱配套使用可实现主控室对HG7101终端进行定时打铃、实时语音广播和紧急广播等功能。 淘宝速购&#xff1a; SV-701…

Android中属性property_get和property_set的详细用法介绍

1&#xff0c;property_get和property_set的作用说明 在Android操作系统中&#xff0c;property_get和property_set是用于获取和设置系统属性的函数。这些属性通常用于存储和读取配置信息&#xff0c;例如设备配置、网络设置、系统参数等。 property_get函数用于获取指定属性…

websocket 通信协议

websocket是什么 答: 它是一种网络通信协议&#xff0c;是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 意思就是服务器可以主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息 属于服务器推送技术的一种. 为什么需要websocket? 疑问?…

(五)MySQL的备份及恢复

1、MySQL日志管理 在数据库保存数据时&#xff0c;有时候不可避免会出现数据丢失或者被破坏&#xff0c;这样情况下&#xff0c;我们必须保证数据的安全性和完整性&#xff0c;就需要使用日志来查看或者恢复数据了 数据库中数据丢失或被破坏可能原因&#xff1a; 误删除数据…

MySQL原理(二)存储引擎(3)InnoDB

目录 一、概况&#xff1a; 1、介绍&#xff1a; 2、特点&#xff1a; 二、体系架构 1、后台线程 2、内存池&#xff08;缓冲池&#xff09; 三、物理结构 1、数据文件&#xff08;表数据和索引数据&#xff09; 1.1、作用&#xff1a; 1.2、共享表空间与独立表空间 …

【C/C++ 05】快速排序

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序算法&#xff0c;其基本思想是&#xff1a;任取待排序序列中的某元素作为基准值&#xff0c;按照该基准值将待排序集合分割成两个子序列&#xff0c;左子序列中所有元素均小于基准值&#xff0c;右子序列中所有元素均大于…

MySQL原理(二)存储引擎(1)概述

一、存储引擎介绍 1、概念&#xff1a; &#xff08;1&#xff09;MySQL中的数据用各种不下同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎…

【数据结构与算法】7.详解队列的基本操作

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》|《数据结构与算法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢…

2024年最新 MySQL的下载、安装、启动与停止

一、MySQL的下载 MySQL最常用的2个版本&#xff1a; 社区版&#xff1a;免费开源&#xff0c;自由下载&#xff0c;不提供官方技术支持&#xff0c;大多数普通用户选择这个即可。企业版&#xff1a;需要付费&#xff0c;不能在线下载&#xff0c;可以使用30天&#xff0c;提供…

ctfshow web72

下载源码&#xff1a; 开启环境&#xff1a; 本题设置了 open_basedir()&#xff0c;将php所能打开的文件限制在指定的目录树中&#xff0c;包括文件本身。 因为 ini_set() 也被限制了&#xff0c;所以 open_basedir() 不能用 ini_set() 重新设置绕过。 使用 php 伪协议 glob:…

【网络】:网络套接字(UDP)

网络套接字 一.网络字节序二.端口号三.socket1.常见的API2.封装UdpSocket 四.地址转换函数 网络通信的本质就是进程间通信。 一.网络字节序 我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分, 磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网…

数据结构----链表介绍、模拟实现链表、链表的使用

文章目录 1. ArrayList存在的问题2. 链表定义2.1 链表的概念及结构2.2 链表的组合类型 3. 链表的实现3.1 单向、不带头、非循环链表的实现3.2 双向、不带头节点、非循环链表的实现 4.LinkedList的使用4.1 什么是LinkedList4.2 LinkedList的使用4.2.1. LinkedList的构造4.2.2. L…

npm 淘宝镜像正式到期

由于node安装插件是从国外服务器下载&#xff0c;如果没有“特殊手法”&#xff0c;就可能会遇到下载速度慢、或其它异常问题。 所以如果npm的服务器在中国就好了&#xff0c;于是我们乐于分享的淘宝团队干了这事。你可以用此只读的淘宝服务代替官方版本&#xff0c;且同步频率…

Docker 数据管理、容器互联、网络与资源控制

一、docker数据管理 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷(Data volumes)和数据卷容器(Datavolumes containers)。 1、数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿主机的目录挂载到数据卷上&#xff0c;对数据卷的修改操作立…

seata 分布式

一、下载安装seata 已经下载好的朋友可以跳过这个步骤。这里下载的是seata1.6.1这个版本。 1、进入seata官网 地址&#xff1a; https://seata.io/zh-cn/index.html 2、进入下载 3、点击下载地址 下载地址&#xff1a; https://github.com/seata/seata 二、配置seata 进入c…
最新文章