DMDPC是一款同时支持在线分析处理 (OLAP) 和在线事务处理 (OLTP) 的新型分布式数据库系统。它不仅保留了传统单机数据库的大部分功能,还提供了分布式计算集群所特有的高可用性、高扩展性、高性能、高吞吐量以及对用户透明等高级特性。本文借助命令行工具部署 DPC 集群。
DMDPC 的架构由三个核心组件组成:
DMDPC 支持多副本系统架构,确保数据安全性和高可用性。每个 BP 或 MP 节点可以配置成一个或多副本系统,以应对可能出现的各种故障情况。
在 DMDPC 中,数据根据用户指定的分布规则分布在不同的 BP 上。DMDPC 的核心在于对用户请求的并行执行。下面对查询流程分别进行详细说明。
在 DMDPC 查询流程中,SP 作为计划生成和调度中心,BP 执行具体的数据操作,而 MP 提供必要的元数据支持,共同协作完成客户端的查询请求,这一流程不仅适用于查询操作,而且可以扩展到了所有的DDL和DML操作,SP、BP和MP在整个数据处理流程中协同工作,实现了高效的分布式数据库服务。
RAFT 组名 | 角色 | 实例名称 | IP | PORT_NUM | AP_PORT_NUM | 路径 |
---|---|---|---|---|---|---|
RAFT_SP1 | SP | SP1 | 192.168.80.201 | 5237 | 6000 | /opt/dmdbms/dpc_data/sp1 |
RAFT_1 | BP | BP1 | 192.168.80.201 | 5238 | 6001 | /opt/dmdbms/dpc_data/bp1 |
RAFT_2 | BP | BP2 | 192.168.80.201 | 5239 | 6002 | /opt/dmdbms/dpc_data/bp2 |
缺省为 NULL 或者 MP_RAFT | MP | MP | 192.168.80.201 | 5240 | 6003 | /opt/dmdbms/dpc_data/mp |
在宿主机创建sp1、bp1、bp2、mp等目录,并将目录挂载到容器内的指定目录
# 加载 Docker 镜像
docker load -i dm8_20230808_rev197096_x86_rh6_64_single.tar
# 运行 Docker 容器
docker run -d --name dm8_dpc --privileged=true --network host \
-p 5237:5237 \
-p 5238:5238 \
-p 5239:5239 \
-p 5240:5240 \
-p 12000:12000 \
-v /home/ct/dm8/dpc_data/sp1:/opt/dmdbms/dpc_data/sp1 \
-v /home/ct/dm8/dpc_data/bp1:/opt/dmdbms/dpc_data/bp1 \
-v /home/ct/dm8/dpc_data/bp2:/opt/dmdbms/dpc_data/bp2 \
-v /home/ct/dm8/dpc_data/mp:/opt/dmdbms/dpc_data/mp \
dm8_single:dm8_20230808_rev197096_x86_rh6_64
# 进入容器内部
docker exec -it dm8_dpc bash
进入达梦bin目录,初始化 4 个数据库实例,实例名称分别为 SP1、BP1、BP2 和 MP。
cd /opt/dmdbms/bin
./dminit path=/opt/dmdbms/dpc_data/sp1 instance_name=SP1 port_num=5237 ap_port_num=6000 dpc_mode=SP
./dminit path=/opt/dmdbms/dpc_data/bp1 instance_name=BP1 port_num=5238 ap_port_num=6001 dpc_mode=BP
./dminit path=/opt/dmdbms/dpc_data/bp2 instance_name=BP2 port_num=5239 ap_port_num=6002 dpc_mode=BP
./dminit path=/opt/dmdbms/dpc_data/mp instance_name=MP port_num=5240 ap_port_num=6003 dpc_mode=MP
为 SP、BP 和 MP 实例配置 MP.INI 文件。并将 MP.INI 文件内容分别写入 SP(SP1)、BP(BP1、BP2)和 MP 中。要求端口号与MP、BP和SP上的ap_port_num不冲突。
# 进入所有实例对应的目录
cd /opt/dmdbms/dpc_data/sp1/DAMENG/
cd /opt/dmdbms/dpc_data/bp1/DAMENG/
cd /opt/dmdbms/dpc_data/bp2/DAMENG/
cd /opt/dmdbms/dpc_data/mp/DAMENG/
# 添加并配置 MP.INI 文件
vi mp.ini
mp_host = 192.168.80.201
mp_port = 12000
在DMDPC 运行过程中,MP 需要始终处于开启状态。所以MP必须第一个启动。
# 启动 MP
cd /opt/dmdbms/bin
./dmserver /opt/dmdbms/dpc_data/mp/DAMENG/dm.ini dpc_mode=MP
将 MP、SP 和 BP 等实例加入集群,增加 1 个 MP、1 个 SP 和 2 个 BP 节点。只有在注册当前登录 MP 节点后,才可以注册其余节点。后续增加 MP、SP 节点和 BP 节点无先后之分。
# 搭建DMDPC过程中加入MP、SP和BP,必须登录MP进行操作
cd /opt/dmdbms/bin
./disql SYSDBA/[email protected]:5240
# 注册当前MP实例,MP的RAFT组名可以指定为NULL或者'MP_RAFT'
SP_CREATE_DPC_INSTANCE('MP_RAFT','MP','MP',6003,5240, '192.168.80.201', '192.168.80.201','NORMAL',1,'MP instance');
# 注册RAFT组,名为RAFT_1
SP_CREATE_DPC_RAFT('BP','RAFT_1');
# 在RAFT_1组内注册BP实例BP1
SP_CREATE_DPC_INSTANCE('RAFT_1','BP1','BP',6001,5238, '192.168.80.201', '192.168.80.201','NORMAL',1,'BP instance');
# 注册RAFT_2
SP_CREATE_DPC_RAFT('BP', 'RAFT_2');
# 在RAFT_2内注册BP实例BP2
SP_CREATE_DPC_INSTANCE('RAFT_2','BP2','BP',6002,5239, '192.168.80.201', '192.168.80.201', 'NORMAL', 1, 'BP instance');
# 注册一个BP组,名为BG_1
SP_CREATE_DPC_BP_GROUP('BG_1', 'bp group1');
# 往BP组中添加RAFT组
SP_BP_GROUP_ADD_RAFT('BG_1', 'RAFT_1');
SP_BP_GROUP_ADD_RAFT('BG_1', 'RAFT_2');
# 增加SP,也要注册RAFT组
SP_CREATE_DPC_RAFT('SP', 'RAFT_SP1');
#在RAFT_SP1内注册SP实例SP1
SP_CREATE_DPC_INSTANCE('RAFT_SP1','SP1','SP',6000,5237, '192.168.80.201', '192.168.80.201','NORMAL', 2, 'SP instance');
# 注册一个容错域:FDOM_1 (可选)
SP_CREATE_FAULT_DOMAIN ('FDOM_1','fuzhou');
# 往容错域中添加实例
SP_FAULT_DOMAIN_MV_INST('FDOM_1','MP');
SP_FAULT_DOMAIN_MV_INST('FDOM_1','BP1');
SP_FAULT_DOMAIN_MV_INST('FDOM_1','BP2');
select * from DPC_BP_GROUP;
select * from DPC_BP_RAFT;
select * from DPC_INSTANCE;
启动 SP 和 BP 没有先后之分。MP、SP、BP 全部正常启动,且均处于 OPEN 状态,才是一个正常的 DMDPC 系统。
cd /opt/dmdbms/bin
./dmserver /opt/dmdbms/dpc_data/bp1/DAMENG/dm.ini dpc_mode=BP
./dmserver /opt/dmdbms/dpc_data/bp2/DAMENG/dm.ini dpc_mode=BP
./dmserver /opt/dmdbms/dpc_data/sp1/DAMENG/dm.ini dpc_mode=SP
Windows 本地使用 dmfldr 工具将数据快速载入到 DMDPC 分布式数据库中。
dmfldr.exe SYSDBA/[email protected]:5237 control='D:\DMDBMS\bin\test\out1.ctl' mode='in'
public class DMfldrTest {
/**********自定义init函数,设置属性信息,并初始化当前快速装载实例**********/
public synchronized static boolean init(Instance instance, int threadNum, String tableName)
{
// 分配句柄
instance.allocInstance();
// 设置必要的属性信息
String host = "192.168.80.201";
String port = "5237";
String userName = "SYSDBA";
String password = "SYSDBA";
instance.setAttribute(Properties.FLDR_ATTR_SERVER, host);
instance.setAttribute(Properties.FLDR_ATTR_PORT, port);
instance.setAttribute(Properties.FLDR_ATTR_UID, userName);
instance.setAttribute(Properties.FLDR_ATTR_PWD, password);
// 其余属性用户均可根据实际情况选择性设置
// 设置编码
//System.out.println(System.getProperty("file.encoding"));
//System.out.println(getFileCharsetName("/home/boss/dm/ct-test/" + tableName + ".txt"));
try {
instance.setAttribute(Properties.FLDR_ATTR_DATA_CHAR_SET,System.getProperty("file.encoding"));
} catch (Exception e) {
e.printStackTrace();
}
// 设置是否插入自增列
//instance.setAttribute(Properties.FLDR_ATTR_SET_INDENTITY, "0");
// fldr task线程默认为cpu个数,如果客户端线程数大于cpu个数,则必须设置该参数
instance.setAttribute(Properties.FLDR_ATTR_TASK_THREAD_NUM, String.valueOf(threadNum));
// 装载日志
String dmHome = System.getProperty("DM_HOME");
if(dmHome != null && !dmHome.equals("")){
if(dmHome.endsWith("/") || dmHome.endsWith("\\")){
instance.setAttribute(Properties.FLDR_ATTR_BAD_FILE, dmHome + "BADFILE_2.TXT");
instance.setAttribute(Properties.FLDR_ATTR_LOG_FILE, dmHome + "FLDRLOG_2.TXT");
}else{
instance.setAttribute(Properties.FLDR_ATTR_BAD_FILE, dmHome + File.separator + "BADFILE_2.TXT");
instance.setAttribute(Properties.FLDR_ATTR_LOG_FILE, dmHome + File.separator + "FLDRLOG_2.TXT");
}
}
//初始化当前快速装载实例
return instance.initializeInstance(null, null, null, "TEST."+tableName);
}
public static void main(String[] args) {
Instance instance = new Instance();
String tableName = "TEST_DEVICE";
String DATA_PATH = "D:/DMDBMS/bin/test/";
String filename = "out";
boolean success = init(instance,1,tableName);
if(success){
String ctl = "LOAD DATA\r\n" +
"INFILE '" + DATA_PATH + filename + ".txt'\r\n" +
"INTO TABLE TEST." + tableName + "\r\n" +
"FIELDS '|'\r\n";
System.out.println(ctl);
//根据ctrl内容进行数据载入
instance.setControl(ctl);
instance.finish();
// 返回实际插入的行数
long rowHaveCopied = Long.parseLong(instance.getAttribute(Properties.FLDR_ATTR_COMMIT_ROWS));
if(rowHaveCopied == 0L){
System.out.println(tableName + "导入异常");
}
else{
System.out.println(tableName+"导入成功" + ",已复制行数:" + rowHaveCopied);
}
instance.uninitialize();
instance.free();
}else{
byte[] message = null;
try {
message = instance.getErrorMsg().getBytes("utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
;
System.out.println("出错信息:" + message);
instance.free();
}
}
}