开源分布式数据库PolarDB-X源码解读——PolarDB-X源码解读(二):CN启动流程

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 开源分布式数据库PolarDB-X源码解读——PolarDB-X源码解读(二):CN启动流程

本文主要讲解PolarDB-X的CN节点(polardbx-sql)的启动过程包括参数加载、元信息加载等过程并对启动过程中设计的模块做简单的介绍。  


CN Server层的代码主要包含在polardbx-server模块中,main函数位于TddlLauncher


主逻辑入口在CobarServer.init()方法中。


CN启动分为以下几个流程:


1.CobarServer对象的创建


该类是单例,可以通过CobarServer.getInstance()获取。


2.参数加载


路径:TddlLauncher.main() -> CobarServer.new() -> CobarConfig.new() -> CobarConfig.initCobarConfig() -> ServerLoader.load()


CN的参数均为key-value的形式。


ServerLoader.load()中,CN主要从以下位置读取参数:


 server.properties文件,其默认位置在classpath的根目录中,所以一般使

 用           IDE          进         行            开          发          时 ,     加          载          的         是

polardbx-server\src\main\resources\server.properties文件


String conf = 
System.getProperty("server.conf","classpath:server.properties");


 Java运行参数中


例如java...-DserverPort=8527


 环境变量中


serverProps.putAll(System.getProperties());
serverProps.putAll(System.getenv());


以上参数来源中,server.properties的优先级最低,环境变量中的优先级最高,当包含同名参数时,高优先级的参数来源会覆盖低优先级的参数来源。


加载的参数,会保存在SystemConfig中,该类为单例,可以通过CobarServer.getInstance().getConfig().getSystem()获取。


3.从MetaDB读取元数据,并初始化实例级的系统组件


路径:

TddlLauncher.main()->CobarServer.new()->CobarConfig.new()->CobarConfig.initCobarConfig()->ServerLoader.load()->ServerLoader.initPolarDbXComponents()


1)初始化元数据库的连接池


MetaDbDataSource.initMetaDbDataSource


会使用SystemConfig中存储的MetaDB的地址、端口、用户名、密码、库名等信息,建立与MetaDB的连接。


MetaDbDataSource
            .initMetaDbDataSource(this.system.getMetaDbAddr(), this.system.getMetaDbName(),
                this.system.getMetaDbProp(),
                this.system.getMetaDbUser(),
                this.system.getMetaDbPasswd());


MetaDbDataSource是一个单例,实现了JDBC的程序中可以使用MetaDbDataSource.getInstance().getConnection()获取与MetaDB的连接(实现了JDBC接口),并使用该连对MetaDB进行访问。


2)对系统表进行创建或者升级


SchemaChangeManager.getInstance().handle();


polardbx-gms\src\main\resources\ddl\中保存了系统表的表结构,并且使用alter语句记录了每一次版本的变更。SchemaChangeManager在初始化时,会检测当前每个系统表的表结构版本,如果版本较老,会依次使用这些语句对alter语句进行更新。


3)读取实例ID信息


ServerInstIdManager.getInstance();


一个PolarDB-X集群也称为一个实例,在MetaDB中有一个唯一的实例ID。同时,PolarDB-X实例有主实例与只读实例两种模式。当存在只读实例时,必然存在一个主实例。该步骤从MetaDB中读取实例ID,如果当前实例为只读实例,则还会读取其主实例的实例ID。  


4)MetaDbConfigManager


先简单介绍下这个东西是干啥的。


我们存在MetaDB中的配置信息,如果发生变化,CN需要能感知到这个变化。例如,某个表增加了一个列。作为CN,如何能感知到这个变化呢?


一个比较简单的思路是对元数据做轮询,各几秒查一次。但如果让每个模块都去做这样的事情,会有个比较大的问题是对MetaDB的访问压力会很大。


MetaDbConfigManager对此作了封装,作了一个统一的轮询机制。但由于每个模块元数据表的格式千变万化,所以MetaDbConfigManager并不是直接轮询每个模块的元数据表,而是轮询config_listener这张表。这张表的几个关键列是data_id、op_version、gmt_modified。


我们可以为每个data_id在代码中注册一个listener,当MetaDbConfigManager轮询到data_id的op_version发生变化的时,会回调这个listener,一般情况下,各模块实现的listener会按需要再读取对应的元数据表。


例如,d1.t1表在config_listener中有一行记录:


polardbx.meta.table.d1.t1


往t1表中增加了一个列,我们会修改MetaDB中的columns表,同时,我们会修改config_listener表中 polardbx.meta.table.d1.t1这行记录的op_version,进行+1的操作。


几秒钟后,MetaDbConfigManager会轮询到这行记录的op_version列发生了变化,会回调表结构管理模块的listener(类:TableMetaListener),该listener会重新加载d1.t1表的元数据。


在启动时,CN会初始化MetaDbConfigManager用于做轮询的线程和定时任务。


5)MetaDbInstConfigManager


初始化实例级的配置项,这里的配置项类似于MySQL中的System Variables,例如SQL的内存大小限制之类的。该阶段会从MetaDB的inst_config表中加载所有的配置项并保存在内存中。


并且会注册对应的listener,这样当inst_config表发生变化的时候,会回调MetaDbInstConfigManager的listener。  


6)ConnPoolConfigManager


这个是用来存连接池(CN与DN之间的连接池)相关的配置项的(其实它用到的配置MetaDbInstConfigManager中都有),也是从inst_config中读取。


7)StorageHaManager


这个比较重要。


目前每一组DN(这里的组指一个Paxos组)内选举的结果是存储在该组DN自己的系统表中,并不会写到MetaDB中。因此CN需要有机制去从DN的系统表中探测各节点的角色。


StorageHaManager会从storage_info表中读取所有的DN节点的连接信息,并且内部会有轮询的线程去检测角色信息。当DN发生HA的时候,StorageHaManager会探测到这个变化,并感知到最新的Leader等角色。


8)初始化系统库


initSystemDbIfNeed初始化information_schema和polardbx等几个系统库。


4.创建线程池


路径:CobarServer.new()


几个主要的线程池的创建:


 managerExecutor,负责Manager端口的请求。

 killExecutor,专门用来执行kill指令

 serverExecutor,各种SQL都是由这个线程池执行


5.CobarServer.init


路径:TddlLauncher.main()->CobarServer.init()  


1)逻辑库(TDataSource)的初始化


入口在GmsAppLoader.initDbUserPrivsInfo,类里的“App”指的就是一个逻辑库。这里会加载每个逻辑库的用户权限信息以及最重要的TDataSouce。


TDataSource在CN中与逻辑库一一对应。它是每个逻辑库执行SQL的入口。TDatSource的初始化逻辑在MatrixConfigHolder.init中,包含了以下内容:


 初始化拓扑,例如这个库包含了哪些DN,并初始化与这些DN之间的连接池

 (TopologyHandler.initPolarDbXTopology


 获取每个DN的信息,包括版本,特性的支持程度等

StorageInfoManager.init


 初始化分片的路由信息(mode='drds',TddlRuleManager.init

 mode='auto',PartitionInfoManager.init


 初始化表管理器(有哪些表、每个表有哪些列哪些索引等)(GmsTableMetaManager.init


 初始化事务管理器(transactionManager.init()


 创建Plan Cache(PlanCache planCache = new PlanCache(schemaName);


 启动DDL任务引擎(ddlEngineInit()


 统计信息管理器的初始化(StatisticManager.init


 SPM的初始化(PlanManager.init


2)GmsClusterLoader.loadPolarDbXCluster


加载集群信息,例如集群中有哪些CN节点等。初始化CclService,这个是用来做SQL限流的。


3)warmup


CN的SQL函数目前是反射机制进行加载的,这里主要作用是加载下所有的函数。


6.网络层的初始化


该步骤结束后,服务端口便会打开,该CN进程就能开始对外提供服务了。  


CN在这一步会启动Server端口与Manager端口两个端口。  


Server端口对应MySQL的3306端口,是提供给前端应用使用的。


Manager端口用于内部的管理使用,例如,SHOW PROCEESSLIST指令需要收集所有CN的执行情况,就会使用该端口进行收集。


1)NIOProcessor


processors = new NIOProcessor[system.getProcessors()];
for (int i = 0; i < processors.length; i++) {
      processors[i] = new NIOProcessor(i, "Processor" + i,this.serverExecutor);
processors[i].startup();
}


CN网络层处理请求的入口是NIOProcessor,每个NIOProcessor有读写两个线程。这里会来启动NIOProcessor的W与R线程。


2)NIOAcceptor


CN网络层用来处理连接建立请求的是NIOAcceptor,NIOAcceptor的new过程中,会打开端口进行监听:  


public NIOAcceptor(String name, int port, FrontendConnectionFactory factory, boolean online) throws IOException {
        super.setName(name);
        ...
            this.selector = Selector.open();
            this.serverChannel = ServerSocketChannel.open();
            this.serverChannel.socket().bind(new InetSocketAddress(port), 65535);
        ...
    }


同时,NIOAcceptor也是一个线程,会处理连接建立的请求。当连接建立后,NIOAcceptor.accept方法会将连接绑定到一个NIOProcessor上,由NIOProcessor继续处理该连接上后续的读写请求。


可以看出,对于服务端口来说,NIOAcceptor只有一个,NIOProcessor的数目则一般和CPU核数保持一致。


3)MPP Server的启动


CN作为MPP集群中的一个,还需要启动端口来进行CN之间的通信。


startMppServer会进行MPP服务的启动。  


4)CDC服务的启动


CobarServer.tryStartCdcManager会进行CDC的启动。


7:结语


至此,CN就启动完成了。这个过程包含了CN中绝大多数组件,鉴于篇幅原因,没有完整介绍每个组件的作用。如果你对这些组件中的哪一个感兴趣,欢迎留言给我们,我们会在后续的文章中,对一些关键的组件做进一步详细的介绍。  

相关实践学习
跟我学:如何一键安装部署 PolarDB-X
《PolarDB-X 动手实践》系列第一期,体验如何一键安装部署 PolarDB-X。
相关文章
|
6天前
|
Java 关系型数据库 MySQL
【MySQL × SpringBoot 突发奇想】全面实现流程 · xlsx文件,Excel表格导入数据库的接口(下)
【MySQL × SpringBoot 突发奇想】全面实现流程 · xlsx文件,Excel表格导入数据库的接口
44 0
|
6天前
|
Java 关系型数据库 MySQL
【MySQL × SpringBoot 突发奇想】全面实现流程 · xlsx文件,Excel表格导入数据库的接口(上)
【MySQL × SpringBoot 突发奇想】全面实现流程 · xlsx文件,Excel表格导入数据库的接口
47 0
|
1天前
|
存储 关系型数据库 分布式数据库
【PolarDB开源】深入PolarDB内核:探究存储计算分离架构的设计哲学
【5月更文挑战第20天】PolarDB是阿里巴巴的云原生分布式数据库,以其存储计算分离架构为核心,解决了传统数据库的扩展性和资源灵活性问题。该架构将数据存储和计算处理分开,实现高性能(通过RDMA加速数据传输)、高可用性(多副本冗余保证数据可靠性)和灵活扩展(计算资源独立扩展)。通过动态添加计算节点以应对业务流量变化,PolarDB展示了其在云时代应对复杂业务场景的能力。随着开源项目的进展,PolarDB将持续推动数据库技术发展。
28 6
|
1天前
|
关系型数据库 分布式数据库 PolarDB
【PolarDB开源】PolarDB开源之旅:从零开始搭建分布式数据库集群
【5月更文挑战第20天】PolarDB,阿里云自研的云原生分布式数据库,因其高性能、高可用和易用性备受瞩目。本文指导如何搭建PolarDB集群:准备硬件和软件环境,从GitHub克隆源码,构建Docker镜像,部署控制节点和计算节点,最后验证集群状态。通过开源,PolarDB旨在推动数据库技术进步,邀请用户一同探索其潜力,共创未来。
25 4
|
2天前
|
SQL Cloud Native 关系型数据库
云原生分布式数据库 PolarDB-X系统权限策略参考
阿里云RAM提供系统和自定义策略,系统策略由阿里云维护,不可修改,用于快速入门;自定义策略用户可创建、更新和删除。系统策略包括产品系统策略、服务角色策略和服务关联角色策略。产品如DRDS有预设的系统策略,如AliyunDRDSFullAccess、AliyunDRDSReadOnlyAccess等,用于不同级别的访问权限。建议遵循最小授权原则,详细授权操作见官方文档。
8 0
|
4天前
|
Cloud Native 关系型数据库 分布式数据库
祝贺!阿里云PolarDB斩获数据库国际顶会ICDE 2024工业赛道最佳论文
阿里云斩获国际顶会ICDE 2024最佳论文,0.5秒实现数据库跨机实例迁移。
祝贺!阿里云PolarDB斩获数据库国际顶会ICDE 2024工业赛道最佳论文
|
4天前
|
关系型数据库 数据库 数据安全/隐私保护
使用PostgreSQL进行高级数据库管理
【5月更文挑战第17天】本文介绍了使用PostgreSQL进行高级数据库管理,涵盖性能调优、安全性加强和备份恢复。性能调优包括索引优化、查询优化、分区和硬件配置调整;安全性涉及权限管理、加密及审计监控;备份恢复则讨论了物理备份、逻辑备份和持续归档。通过这些实践,可提升PostgreSQL的性能和安全性,确保数据资源的有效管理。
|
6天前
|
前端开发 关系型数据库 MySQL
【MySQL × SpringBoot 突发奇想】全面实现流程 · 数据库导出Excel表格文件的接口
【MySQL × SpringBoot 突发奇想】全面实现流程 · 数据库导出Excel表格文件的接口
38 0
|
6天前
|
算法 Go 分布式数据库
构建高可用的分布式数据库集群:使用Go语言与Raft共识算法
随着数据量的爆炸式增长,单一数据库服务器已难以满足高可用性和可扩展性的需求。在本文中,我们将探讨如何使用Go语言结合Raft共识算法来构建一个高可用的分布式数据库集群。我们不仅会介绍Raft算法的基本原理,还会详细阐述如何利用Go语言的并发特性和网络编程能力来实现这一目标。此外,我们还将分析构建过程中可能遇到的挑战和解决方案,为读者提供一个完整的实践指南。
|
6天前
|
存储 Cloud Native 关系型数据库
PolarDB-X 是面向超高并发、海量存储和复杂查询场景设计的云原生分布式数据库系统
【5月更文挑战第14天】PolarDB-X 是面向超高并发、海量存储和复杂查询场景设计的云原生分布式数据库系统
44 2

相关产品

  • 云原生数据库 PolarDB