2024年2月

前言


公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍、功能特点、使用方式以及部分功能截图等(打不开或者打开GitHub很慢的同学可以优先查看公众号推文,文末一定会附带项目和框架源码地址)。注意:排名不分先后,都是十分优秀的开源项目和框架,每周定期更新分享(欢迎关注公众号:
追逐时光者
,第一时间获取每周精选分享资讯

对于绝大多数编程语言来说,类型都是编程的基础,WebAssembly自然也不例外。总的来说,WebAssembly涉及的类型不多,很好掌握,接下来我们就来介绍一下WebAssembly编程涉及到的几种类型。本篇内容参考
WebAssembly Spec

一、Value Type
二、Function
三、Memory
四、Table
五、Global
六、External

一、Value Type

值类型(Value Type)在WebAssembly中主要分为三种,它们分别是数字(Number )、向量(Vector)和引用(Reference)。

image

1. Number

WebAssembly涉及数字只包含整数和浮点数,具体又有32位(4字节)和64位(8字节),所以WebAssembly支持的数字类型只包括i32、i64、f32、f64四种。

image

2. Vector

WebAssembly对单指令多数据(SIMD)提供了原生的支持,这进一步提升了它的性能。SIMD指令将一个128位的向量作为操作数,我们可以将多个整数或者浮点数组合成一个向量。比如某个支持i32的指令,采用SIMD方式进行执行理论上可以获得4倍的性能。

image

3. Reference

顾名思义,一个引用类型的变量代表一个指向某个目标对象的指针。目前WebAssembly设计的引用类型只有两种,一种是引用来源于宿主应用的JavaScript对象。由于对于wasm模块来说,这是一种外部引用,所以被定义成externref。另一种被命名位funcref的则是对自身Native函数的引用。

image

二、Function

函数本质上一组输入参数与一组输出结果的映射。虽然WebAssembly的函数目前还不支持返回多个结果,但是后续版本肯定会支持。输入参数列表和输出结果列表可以统一表示为“结果类型(resulttype)”,具体来说就是一组值类型(数字、向量和引用)的有序组合,所以函数类型自然就表示成一个resulttype对象到另一个resulttype的映射。

image

image

三、Memory

Memory和Table都是一组连续的、限定长度的“区间”,所以WebAssembly专门定义了limits这样一个类型来表示“长度限制”。一个limits必需使指定最小边界(min),可缺省的最长边界如果没有指定,意味着可以无限“扩容”。

image

Memory以单纯的二进制字节的形式来存储一组数据。由于数据类型确定,我们只需要指定“长度限制”,所以Memory可以通过limits来表示。

image

四、Table

Memory和Table的不同指出在于它们存储的元素类型,前者为单纯的二进制字节,后者则是指定类型的对象。具体来说,Table的元素为“引用”,要么是指向宿主应用提供的JavaScript对象(externref),要么是自身定义的函数(funcref)。

image

五、Global

用来表示“全局变量”的Global存储值类型(数字、向量和引用),mut(Mutable)用来表示对应的全局变量是否允许修改。

image

六、External

这里External表示可以导出的,作为宿主程序的“外部”对象。宿主应用可以从加载的wasm模块导出的对象类型包括:函数、Table、Memory和Global。

image

最近做了几个
WPF
+
MudBlazor
的小东西,每次从头搭建环境比较繁琐,然鹅搭建过程还没啥技术含量,索性就直接做了个模板,方便以后使用。

1. 介绍

  • 一个用来创建 .NET 8 + WPF + MudBlazor 的项目模板
  • 适用于 VS2022

用法:
vs插件市场下载
or
自己通过 Github 源码编译

vsix

image

2. 模板打包方法

步骤都是基于
Github 仓库
中的代码来做示例

2.1 创建要打包的项目 WpfMudBlazor

这一步没什么好说的,想做什么样子的模板,就码怎样的代码即可。

2.2 替换模板关键字 TemplateCode

该项目是 WpfMudBlazor 的副本。在项目中替换了关键字后,会导致无法编译,所以选择复制一份出来,方便以后升级。

在这个项目中,仅仅是将项目名称和解决方案替换成创建项目时用户输入的名称。(使用
$safeprojectname$
关键字替换掉原有内容即可)

<Project Sdk="Microsoft.NET.Sdk.Razor">

	<PropertyGroup>
		<OutputType>WinExe</OutputType>
		<TargetFramework>net8.0-windows</TargetFramework>
		<Nullable>enable</Nullable>
		<ImplicitUsings>enable</ImplicitUsings>
		<UseWPF>true</UseWPF>
		<RootNamespace>$safeprojectname$</RootNamespace>
	</PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Wpf" Version="8.0.6" />
	</ItemGroup>

	<ItemGroup>
		<ProjectReference Include="..\$safeprojectname$.Pages\$safeprojectname$.Pages.csproj" />
	</ItemGroup>

</Project>
namespace $safeprojectname$
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            InitializeBlazor();
        }
    }
}

VS提供了很多模板关键字,如果需要制作更复杂的模板,可以参考
微软官方文档-模板参数

2.3 导出模板文件 TemplateOutput

  1. 用vs打开模板项目
  2. 项目 -> 导出模板
  3. 选择项目模板 -> 选择对应项目
    友情提醒:多项目模板时记得要分别导出每一个项目
    image
    image
  4. 将所有项目的模板放入同一级目录中并解压(导出项目模板为
    .zip
    文件)
  5. 在根目录中再新建一个
    *.vstemplate
    文件(该文件为所有项目的模板文件)
    image
    image
    image
  6. 编辑新建的
    .vstemplate
    模板文件
    • 根据自己的需求配置模板的项目说明
    • 注意
      ProjectCollection
      节点中子项目的引用路径
<VSTemplate Version="3.0.0" Type="ProjectGroup"
            xmlns="http://schemas.microsoft.com/developer/vstemplate/2005">
	<TemplateData>
		<Name>WPF MudBlazor (.NET 8)</Name>
		<Description>一个用来创建 .NET 8 + WPF + MudBlazor 的项目模板</Description>
		<ProjectType>CSharp</ProjectType>
		<PlatformTag>C#</PlatformTag>
		<PlatformTag>WPF</PlatformTag>
		<PlatformTag>MudBlazor</PlatformTag>
		<PlatformTag>桌面</PlatformTag>
		<PlatformTag>混合开发</PlatformTag>
		<DefaultName>WpfMudBlazor</DefaultName>
		<Icon>icon.ico</Icon>
	</TemplateData>
	<TemplateContent>
		<ProjectCollection>
			<ProjectTemplateLink ProjectName="$safeprojectname$" CopyParameters="true">
				WpfMudBlazor\MyTemplate.vstemplate
			</ProjectTemplateLink>
			<ProjectTemplateLink ProjectName="$safeprojectname$.Pages" CopyParameters="true">
				WpfMudBlazor.Pages\MyTemplate.vstemplate
			</ProjectTemplateLink>
		</ProjectCollection>
	</TemplateContent>
</VSTemplate>
  1. 编辑所有子项目的
    .vstemplate
    模板文件,在
    TemplateData
    节点加入
    <Hidden>true</Hidden>

    这个设置是为了最终导入模板后,所有的子项目不会显示在vs的项目模板位置
  <TemplateData>
	  <!-- 为了节约空间,不显示文件中的其它原始配置 -->
    <Hidden>true</Hidden>
  </TemplateData>
  1. 将当前文件夹的所有内容打包成新的模板文件
    *.zip
    image

将这个模板文件直接复制到第3步中vs默认模板路径下,重启vs,新建项目,你会发现模板已经生效。
后续步骤为将模板文件打包成
VSIX
扩展安装文件(非必须)

2.4 打包扩展 WpfMudBlazorTemplate

该步骤需要在 VS 中额外安装“
Visual Studio
扩展开发”组件

  1. 创建一个新的“空白 VSIX 项目”
    image
  2. 将上一节中的模板文件复制到当前项目中,并且选择
    始终复制
  3. 双击项目中的
    source.extension.vsixmanifest
    文件来编辑扩展信息
  • 元数据
    Metadata
    -> 根据自己需求填写
    image
  • 资产文件
    Assets
    -> 新建
    image
  • 其它几个选项为扩展的最低运行环境等等,看一眼差不多也能看懂个七七八八,按需配置即可
  1. 编译并生成项目,搞定(
    .VSIX
    文件在生成目录)

单项目模板的生成就更简单了,上述步骤只减不增

开心一刻

今天,小区有个很漂亮的姑娘出嫁

我对儿子说:你要好好学习,认真写作业,以后才能娶到这么漂亮的老婆

儿子好像听明白了,思考了一会,默默的收起了作业本

然后如释重负的跟我说到:爸,我以后还是不娶老婆了

环境准备

后文要讲的
重点
是标准
SQL
,与具体的数据库没关系,所以理论上来讲,所有的关系型数据库都应该支持

但理论是理论,事实是事实,大家需要结合当下的实际情况来看问题

关系型数据库很多,后文主要基于
MySQL
8.0
.
30

来讲解,偶尔会插入
PostgreSQL
14.1

,没有特殊说明的情况下,都是基于
MySQL 8.0.30

MySQL
建表
tbl_ware
,并初始化数据


CREATE TABLE`tbl_ware` (
`ware_id`
BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '商品id',
`ware_name`
VARCHAR(100) NOT NULL COMMENT '商品名称',
`ware_category`
VARCHAR(100) NOT NULL COMMENT '商品类别',
`sale_unit_price`
INT COMMENT '销售单价',
`purchase_unit_price`
INT COMMENT '进货单价',
`registration_date` DATE COMMENT
'登记日期',PRIMARY KEY(`ware_id`) USING BTREE
) ENGINE
=InnoDB COMMENT='产品';

View Code

PostgreSQL
建表
tbl_ware
,并初始化数据


CREATE TABLEtbl_ware (
ware_id
INT PRIMARY KEY,
ware_name
VARCHAR(100) NOT NULL,
ware_category
VARCHAR(100) NOT NULL,
sale_unit_price
INT,
purchase_unit_price
INT,
registration_date DATE
);
INSERT INTO tbl_ware VALUES(1, 'T恤衫', '衣服', 100, 50, '2023-12-11'),
(
2, '打孔器', '办公用品', 25, 10, '2023-12-13'),
(
3, '运动T恤', '衣服', 150, 50, '2023-12-10'),
(
4, '菜刀', '厨房用具', 75, 30, '2023-12-15'),
(
5, '高压锅', '厨房用具', 600, 200, '2023-12-15'),
(
6, '叉子', '厨房用具', 7, 3, NULL),
(
7, '菜板', '厨房用具', 98, 30, '2023-12-12'),
(
8, '圆珠笔', '办公用品', 5, 2, '2023-12-15'),
(
9, '带帽卫衣', '衣服', 150, 90, NULL),
(
10, '砍骨刀', '厨房用具', 150, 69, '2023-12-13'),
(
11, '羽绒服', '衣服', 800, 200, NULL);

View Code

小计与合计

关于
小计与合计
,大家肯定不会陌生,甚至很熟悉

或多或少都实现过这样的功能,尤其是涉及到报表统计的时候,
小计与合计
是绕不过去的坎

那有哪些实现方式了,我们今天就来盘一盘

GROUP BY + 应用程序汇总

先通过数据库层面的
GROUP
BY

得到小计,类似如下

然后通过程序代码对
商品类别
的小计进行一个合计

我敢断定,这种方式肯定是大家用的最多的方式,因为我就是这么用的!

但是,如果加个限制条件:只用
SQL

此时如何实现小计和合计,各位该如何应对?

是不是有面试內味了?

GROUP BY + UNION ALL

直接上
SQL

这个
SQL
,大家都能看懂,我就不做过多解释了

补充问下,用
UNION
可以吗

答案是可以的,但由于两条
SELECT
语句的聚合键不同,一定不会出现重复行,可以使用
UNION
ALL

UNION
ALL


UNION
的不同之处在于它不会对结果进行排序,所以它有更好的性能

就从结果而言,是不是只用
SQL
实现了
小计与合计

但是,这可恶的
但是
来了

执行 2 次几乎相同的
SELECT
语句,再将其结果进行连接,你们不觉得繁琐吗?

在我看来不仅繁琐,效率也会因为繁琐而低下

面试官又会接着问了:在只用
SQL
的前提下,有没有更合适的实现方法?

此时,各位又该如何应对?

ROLLUP

我就不卖关子了,直接上绝招

斗胆问一句,这算实现了吗?

可能有小伙伴会说:这不能算实现了,没看到那么明显的
Null
吗?

如果非要较真的话,这么说也有道理,但是假若我们在展现层(比如前端)将
Null

合计
处理了?

为什么我不说在后端将
Null
处理成
合计

如果我们在后端将
Null
处理成
合计
,为什么不直接用方式:
GROUP
BY
+
应用程序汇总

不过,
Null
看着着实不爽,关键是坑还多:
神奇的 SQL 之温柔的陷阱 → 三值逻辑 与 NULL !

那就把它干掉,调整下
SQL

这下完美了吧,从结果上来看是完美了

但从整体上来看,我觉得还不够完美,主要有 2 点

1、
WITH
ROLLUP


MySQL
的独有写法

ROLLUP
的标准写法是
GROUP
BY
ROLLUP(列名1,列名2,...)

,例如在
PostgreSQL
实现小计与合计

主流的关系型数据库(
Oracle

SQL Server

DB2

PostgreSQL
)都是按
SQL
标准来实现的

唯独
MySQL
没有按标准来,她发挥了她的小任性,用
WITH
ROLLUP

坚持了自己的个性

2、
GROUPING

ROLLUP
,你认识吗

这是本文的
重点
(呼应开头了),请继续往下看

你们不要怀疑我是不是在套娃,请把怀疑去掉,我就是在套娃!

GROUPING

考虑到
MySQL
8.0
.
30

不支持
CUBE

GROUPING
SETS

,所以后面的
SQL
都基于
PostgreSQL
14.1

GROUPING
不会单独使用,往往会结合
ROLLUP

CUBE

GROUPING
SETS

其中之一来使用

ROLLUP

关于
ROLLUP
,前面已经演示了一个案例

商品类别

NULL
的那一行,没有聚合键,也就相当于没有
GROUP
BY

子句,这时会得到全部数据的
合计行

该合计行记录称为
超级分组记录(super
group
row)

,虽然听上去很屌,但还是希望大家把它当做未使用
GROUP
BY


合计行
来理解

正是因为
合计行

ware_category
列的键值不明确,所以会默认使用
NULL

前面的案例只有一个聚合列,如果再加一列
registration_date
,会是什么结果?

就问你们看的懵不懵?

反正我有 2 点比较懵:

1、每一行记录的含义是什么?

2、这么多
Null
,分别表示什么

关于懵点 1,如果大家细看的话,还是能看明白每一行记录的含义的

至此,相信大家对
ROLLUP
的作用有一定感觉了

总结下,
ROLLUP
作用就如其名一样,能够得到像从小计到合计,从最小的聚合级开始,聚合单位逐渐扩大的结果

GROUP
BY
ROLLUP(ware_category)

时,那么结果就是以
ware_category
归类的
小计
加上这些
小计

合计
,一共 3 + 1 = 4 条记录

GROUP
BY
ROLLUP(ware_category,registration_date)

时,那么结果就是以
ware_category,registration_date
归类的
小计
加上
GROUP
BY
ROLLUP(ware_category)

的结果,一共 9 + 4 = 13 条记录

如果聚合列有 3 列,大家还能明白每一行记录的含义吗

关于懵点 2,
Null
看着确实难受,关键是难以区分:到底是值是
Null
,还是超级分组记录的
Null

所以为了避免混淆,
SQL
标准就规定用
GROUPING
函数来判断超级分组记录的
NULL

如果
GROUPING
函数的值是 1,则表示是超级分组记录,0 则表示其他情况

我们调整下
SQL


SELECT 
    CASE WHEN GROUPING(ware_category) = 1
        THEN '商品类别 合计'
        ELSEware_categoryEND ASware_category,CASE WHEN GROUPING(registration_date) = 1
        THEN '登记日期 合计'
        ELSE TO_CHAR(registration_date, 'YYYY-MM-DD')END    ASregistration_date,SUM(purchase_unit_price) ASpurchase_unit_pricesFROMtbl_wareGROUP BYROLLUP(ware_category,registration_date)ORDER BY ware_category DESC, registration_date;

View Code

这样看着是不是清晰很多?

CUBE

语法和
ROLLUP
一样,我们直接看案例


SELECT 
    CASE WHEN GROUPING(ware_category) = 1
        THEN '商品类别 合计'
        ELSEware_categoryEND ASware_category,CASE WHEN GROUPING(registration_date) = 1
        THEN '登记日期 合计'
        ELSE TO_CHAR(registration_date, 'YYYY-MM-DD')END    ASregistration_date,SUM(purchase_unit_price) ASpurchase_unit_pricesFROMtbl_wareGROUP BYCUBE(ware_category,registration_date)ORDER BY ware_category DESC, registration_date;

View Code


ROLLUP
的结果相比,
CUBE
结果多了几行记录,而这几行记录就是
GROUP
BY
(registration_date)

的聚合记录

所谓
CUBE
,就是将
GROUP
BY

子句中的聚合键的
所有可能组合
的聚合结果集中到一个结果集中的功能

因此,组合的个数就 2 的 n 次方(n 是聚合键的个数)

本例中,聚合键有 2 个(
ware_category,registration_date
),所以组合个数就是 2 的 2 次方,即 4 个

如果再添加 1 个变为 3 个聚合键的话,那么组合的个数就是 2 的 3 次方,即 8 个

反观
ROLLUP
,组合个数就是 n + 1

提个疑问,
ROLLUP
的结果一定包含在
CUBE
的结果之中吗?

GROUPING SETS

该运算符主要用于从
ROLLUP
或者
CUBE
的结果中取出部分记录

例如,如果希望从
GROUP
BY
CUBE(ware_category,registration_date)

的结果中选出
商品类别

登记日期
各自作为聚合键的结果

可以这么实现


SELECT 
    CASE WHEN GROUPING(ware_category) = 1
        THEN '商品类别 合计'
        ELSEware_categoryEND ASware_category,CASE WHEN GROUPING(registration_date) = 1
        THEN '登记日期 合计'
        ELSE TO_CHAR(registration_date, 'YYYY-MM-DD')END    ASregistration_date,SUM(purchase_unit_price) ASpurchase_unit_pricesFROMtbl_wareGROUP BY GROUPING SETS (ware_category,registration_date);

View Code

提个问题,有
Null
的哪一行记录表示什么?

相比
ROLLUP

CUBE
相比,
GROUPING
SETS

的使用场景特别少,有所了解即可

总结

GROUPING

作用很明显,就是为了区分
超级分组记录

NULL
和原始数据
NULL

说白了,就是为了标识出
合计
记录

ROLLUP

做个等价替换,方便大家理解

GROUP
BY
ROLLUP(ware_category,registration_date)

等价于

如果是 3 个聚合键了,等价情况是怎么样的?

CUBE

同样做个等价替换

GROUP
BY
CUBE(ware_category,registration_date)

等价于

如果是 3 个聚合键了,等价情况又是怎么样的?

参考

《SQL基础教程》

本文分享自华为云社区《
GaussDB(DWS)细粒度容灾使用介绍
》,作者: 天蓝蓝。

1. 前言

适用版本:【8.2.1.210及以上】

当前数仓承载的客户业务越来越多,从而导致客户对于数仓的可靠性要求不断增加。尤其在金融领域,容灾备份机制是信息系统必须提供的能力之一。本文介绍了在云上环境的双集群(不跨Region不跨VPC)后台手动部署并使用细粒度容灾的主要步骤,使得用户能快速方便得搭建起细粒度容灾。

2. 细粒度容灾简介

对于MPPDB集群的容灾而言,目前业界的常见方案要么是部署两套规格配置同等的集群,要么通过逻辑双加载方式去实现,这两个方案缺点比较明显,存在架构复杂、建设成本高等问题,不仅使得灾备部署难度增大,还导致资源浪费。在此背景下,GaussDB(DWS)基于列存表实现细粒度容灾能力,既满足核心分析型业务在极端场景的业务连续性要求,同时也能大幅降低容灾方案的建设成本,而且容灾架构轻量化,容灾系统易运维、易操作、易演练,从而帮助用户迅捷、经济、按需构建核心业务的容灾系统。

相比于传统的容灾方案,细粒度容灾有以下优势:

  • 主集群和备集群双活(Active-Active)(备集群除灾备表只读外,其余表可读写)
  • 主备集群只需满足DN整数倍关系,降低备集群建设资源,方便灵活部署
  • 利用列存表的数据和元数据分离的特点,通过元数据逻辑同步 + 数据物理同步的方式,同步增量,结合UDF的增量抽取/回放接口,保障表级数据一致性
  • 粒度可控,支持表级、schema级、库级容灾
  • 支持表DDL,部分DCL元数据同步,达到切换可用

细粒度容灾示意图

3. 容灾前准备

3.1 配置互信

前提条件

  • 确保ssh服务打开。
  • 确保ssh端口不会被防火墙关闭。
  • 确保所有机器节点间网络畅通。

配置互信

依次登录主备集群各节点沙箱外,执行步骤2-4。

将主集群和备集群所有节点ip和hostname添加到/home/Ruby/.ssh/authorized_keys和/var/chroot/home/Ruby/.ssh/authorized_keys的from列表中。

将主集群和备集群所有节点ip和hostname的映射添加到/etc/hosts和/var/chroot/etc/hosts文件中。

遍历主集群和备集群所有节点ip和hostname,执行以下命令。

ssh-keyscan -t rsa -T 120 ${ip/hostname} >> /home/Ruby/.ssh/known_hosts
ssh
-keyscan -t rsa -T 120 ${ip/hostname} >> /var/chroot/home/Ruby/.ssh/known_hosts

校验互信

从主集群任一节点能免密ssh到备集群任一节点且从备集群任一节点能免密ssh到主集群任一节点,即代表互信已配置成功。

3.2 配置容灾配置文件

  1. Ruby用户分别登录主备集群主节点沙箱内。
  2. 创建容灾目录,假设目录为/DWS/data2/finedisaster。
    mkdir -p /DWS/data2/finedisaster
  3. 在/DWS/data2/finedisaster目录下,创建容灾配置文件backupRestore.ini和主备集群倒换配置文件sw_backupRestore.ini。

注意:

细粒度容灾的容灾备份过程支持与物理细粒度备份业务并行执行。可通过以下措施隔离:

两个备份任务指定不同的metadata目录和media目录,实现备份数据隔离。容灾备份目录通过容灾配置文件(backupRestore.ini和sw_backupRestore.ini)中的参数primary-media-destination和primary-metadata-destination指定。

细粒度容灾的容灾备份端口与物理细粒度备份的master-port指定为不同的值,实现备份进程端口的隔离。容灾的备份端口通过容灾配置文件(backupRestore.ini和sw_backupRestore.ini)中的参数backup-port指定。

容灾配置文件backupRestore.ini

以下是backupRestore.ini文件示例,可根据具体业务场景修改配置参数值。

# Configuration file forSyncDataToStby tool
# The backup life cycle
life
-cycle=10m
# The table that records backups info
backuprestoreinfo
-file=backuprestoreinfo.csv
# The cluster user name
forcluster
username
=Ruby
# The primary cluster env
setprimary-env=# The standby cluster envsetstandby-env=# Time interval between each full backup,uint: min
full
-backup-exec-time-interval=N/A
# Time interval between each backup
backup
-exec-time-interval=2# One of the backup hosts
primary
-host-ip=XXX.XXX.XXX.XX
# The media type that restore backup files, DISK
media
-type=disk
# The number of process that should be used. Range
is (1~32)
parrallel
-process=4# The compression level that should be usedfor backup. Range(0~9)
compression
-level=1compression-type=2# Media destinationwherethe backup must be stored
primary
-media-destination=/DWS/data2/finedisaster/mediadata
# Metadata destination
wherethe metadata must be stored
primary
-metadata-destination=/DWS/data2/finedisaster/metadata
# The master
-port inwhich the backup must be executed
backup
-port=XXXX
# Logging level
forthe log contents of backup:FATAL,ERROR,INFO,DEBUG
primary
-cluster-logging-level=INFO
# Time interval between each restore,
uint: min
restore
-interval=2# One of the restore hosts
restore
-host-ip=XXX.XXX.XXX.XX
# Media destination
where the backup contents must be stored inthe standby cluster
restore
-media-destination=/DWS/data2/finedisaster/mediadata
# Metadata destination
where the backup contents must be stored inthe standby cluster
restore
-metadata-destination=/DWS/data2/finedisaster/metadata
# The master
-port inwhich the restore must be executed
restore
-port=XXXX
# Logging level
forthe log contents of restore
standby
-cluster-logging-level=INFO
# The maximum number of log files that should be created. Range
is (5~1024).
log
-file-count=50# The retry times of checking cluster balancefor resuem backup or doubleclusters
check
-balance-retry-times=0# cluster id
primary
-cluster-id=66666666666611-6666661-6666661-6666661-666666666666666666666666standby-cluster-id=22222222-2222-2222-2222-222222222222# Processes tablesfor which fine-grained disaster recovery isno longer performed
# Value should be
'none', 'log-desync-table', 'drop-desync-table'desync-table-operation=drop-desync-table
# Number of CPU cores that can be used by each DR process. Range
is (1~1024).
cpu
-cores=8# The max number of rch files that can be reserved on master cluster before sent to standby cluster.0means no limit.
local
-reserve-file-count=160

主备集群倒换配置文件sw_backupRestore.ini相比于backupRestore.ini只需颠倒下列参数

  • primary-host-ip / restore-host-ip
  • backup-port / restore-port
  • primary-media-destination / restore-media-destination
  • primary-metadata-destination / restore-metadata-destination
  • primary-cluster-id / standby-cluster-id

配置文件主要参数说明

参数名 参数含义
username 执行双集群脚本的OS用户,设置成GaussDB(DWS)集群相同的OS用户
primary-env 主集群的环境变量存储文件的绝对路径
standby-env 备集群的环境变量存储文件的绝对路径
backup-exec-time-interval 增量备份周期
primary-host-ip 主集群执行备份的节点ip
compression-type 备份文件的压缩算法类型
compression-level 备份文件的压缩级别
primary-media-destination 主集群上存放备份文件的绝对路径
primary-metadata-destination 主集群上备份元数据的绝对路径
backup-port Roach主代理备份进程的执行端口
restore-interval 恢复周期
restore-host-ip 备集群执行恢复的节点ip
restore-media-destination 备集群存放主集群同步的备份文件的路径
restore-metadata-destination 备集群存放主集群同步的元数据信息的路径
restore-port 恢复进程执行端口
primary-cluster-id 指定主集群的UUID
standby-cluster-id 指定备集群的UUID
new-disaster-recovery-suppressed-time-windows 指定主集群不发起新一轮备份的时间窗口
desync-table-operation 指定不再进行细粒度容灾的表,在备集群上的处理方式
cpu-cores 指定容灾每个进程能使用的cpu核数
local-reserve-file-count 在发送到备用群集之前,可以在主群集上保留的最大rch文件数。0表示无限制。

3.3 主备集群准备

Ruby用户登录主集群主节点沙箱内。

设置集群GUC参数。

local-dn-num为本集群DN数,remote-dn-num为对方集群DN数,可替换为实际值。

python3 $GPHOME/script/DisasterFineGrained.py -t prepare --local-dn-num 6 --remote-dn-num 3 --config-file /DWS/data2/finedisaster/backupRestore.ini

3.4 容灾表数据准备

主集群连接数据库,准备容灾表数据。

新建容灾表

create table schema1.table_1(id int, name text) with (orientation = column, colversion="2.0", enable_disaster_cstore="on", enable_delta=false) DISTRIBUTE BY hash(id);

存量表(非容灾表)转为容灾表

alter table schema1.table_1 set (enable_disaster_cstore='on');--查询表的分布方式select pclocatortype from pg_catalog.pgxc_class where pcrelid = 'schema1.table_1'::regclass::oid limit 1;--若表的分布方式为H(HASH分布),获取一个hash表的分布列,后续以'id'列分布为例select pg_catalog.getdistributekey('schema1.table_1');--对表做重分布
alter table schema1.table_1 DISTRIBUTE BY HASH(id);
--若表的分布方式为N(ROUNDROBIN分布),对表做重分布
alter table schema1.table_1 DISTRIBUTE BY ROUNDROBIN;
--若表的分布方式为R(REPLICATION分布),对表做重分布
alter table schema1.table_1 DISTRIBUTE BY REPLICATION;

4. 细粒度容灾操作

4.1 定义发布

Ruby用户登录主集群主节点沙箱,以下操作均在沙箱内进行。

连接数据库,通过发布语法指定需要容灾的主表,语法如下:

--发布所有表
CREATE PUBLICATION _pub_for_fine_dr FOR ALL TABLES;
--发布schema
CREATE PUBLICATION _pub_for_fine_dr FOR ALL TABLES IN SCHEMA schema1, schema2;
--发布表和schema
CREATE PUBLICATION _pub_for_fine_dr FOR ALL TABLES IN SCHEMA schema1, TABLE schema2.table_1;
--增加一个发布表
ALTER PUBLICATION _pub_for_fine_dr ADD TABLE schema1.table_1;
--增加发布SCHEMA
ALTER PUBLICATION _pub_for_fine_dr ADD ALL TABLES IN SCHEMA schema2;

定义发布后,将容灾publication放到参数文件中,以dbname.pubname形式,例如:

echo 'dbname._pub_for_fine_dr' > /DWS/data2/finedisaster/pub.list

若需要解除发布,通过DisasterFineGrained.py脚本下发cancel-publication命令

# 1、创建需要取消的容灾对象列表文件
# 解除发布表
echo
'db_name.schema_name.table_name' > /DWS/data2/finedisaster/config/disaster_object_list.txt
# 解除发布SCHEMA
echo
'db_name.schema_name' > /DWS/data2/finedisaster/config/disaster_object_list.txt

#
2、下发cancel-publication命令
python3 $GPHOME
/script/DisasterFineGrained.py -t cancel-publication --disaster-object-list-file /DWS/data2/finedisaster/config/disaster_object_list.txt --config-file /DWS/data2/finedisaster/backupRestore.ini

#
3、取消全部发布
python3 $GPHOME
/script/DisasterFineGrained.py -t cancel-publication --config-file /DWS/data2/finedisaster/backupRestore.ini --all-cancel

4.2 启动容灾

注意:

云上集群沙箱内无法启动crontab任务,需要在主备集群沙箱外手动添加定时备份恢复任务

HCS 8.3.0及以上环境,沙箱外crontab设置ssh到沙箱内的定时任务,为了防止沙箱逃逸,ssh前需要加上"sudo python3 /rds/datastore/dws/​​​XXXXXX/sudo_lib/checkBashrcFile.py && source /etc/profile && source ~/.bashrc && ",否则定时任务不生效。checkBashrcFile.py文件路径与版本号有关。

主集群主节点沙箱外设置定时任务,crontab -e。注意替换主节点IP。

*/1 * * * * nohup ssh XXX.XXX.XXX.XXX "source /etc/profile;if [ -f ~/.profile ];then source ~/.profile;fi;source ~/.bashrc;nohup python3 /opt/dws/tools/script/SyncDataToStby.py -t backup  --config-file /DWS/data2/finedisaster/backupRestore.ini --disaster-fine-grained --publication-list /DWS/data2/finedisaster/pub.list  >>/dev/null 2>&1 &"  >>/dev/null 2>&1 &

备集群主节点沙箱外设置定时任务,crontab -e。注意替换主节点IP。

*/1 * * * * nohup ssh XXX.XXX.XXX.XXX "source /etc/profile;if [ -f ~/.profile ];then source ~/.profile;fi;source ~/.bashrc;nohup python3 /opt/dws/tools/script/SyncDataToStby.py -t restore  --config-file /DWS/data2/finedisaster/backupRestore.ini  --disaster-fine-grained  >>/dev/null 2>&1 &" >>/dev/null 2>&1 &

成功启动备份确认。

Ruby用户分别登录主备集群主节点沙箱,查询SyncDataToStby.py进程是否存在。

ps ux | grep SyncDataToStby | grep -v grep

Ruby用户登录主/备集群主节点沙箱,使用roach的show-progress监控工具,查看备份/恢复进度。show-progress命令提供主备集群备份恢复进度等信息, 显示结果为json格式,各字段含义请参考产品文档。

python3 $GPHOME/script/SyncDataToStby.py -t show-progress --config-file /DWS/data2/finedisaster/backupRestore.ini

系统回显:

{"primary cluster": {"key": "20231109_212030","priorKey": "20231109_211754","actionType": "Backup","progress": "100.00%","backupRate": {"producerRate": "0MB/s","compressRate": "0MB/s","consumerRate": "0MB/s"},"currentStep": "FINISH","unrestoreKeys": "N/A","failedStep": "INIT","errorMsg": "","errorCode": "","actionStartTime": "2023-11-09 21:20:28","actionEndTime": "2023-11-09 21:20:49","updateTime": "2023-11-09 21:20:50"},"standby cluster": {"key": "20231109_175002","priorKey": "N/A","actionType": "Restore","progress": "100.00%","backupRate": {"producerRate": "0MB/s","compressRate": "0MB/s","consumerRate": "0MB/s"},"currentStep": "FINISH","unrestoreKeys": "20231109_211754,20231109_212030","failedStep": "INIT","errorMsg": "","errorCode": "","actionStartTime": "2023-11-09 17:53:07","actionEndTime": "2023-11-09 17:53:15","updateTime": "2023-11-09 17:53:15"},"apply": {"backupState": "waiting","restoreState": "waiting","backupSuccessTime": "2023-11-09 21:21:24","restoreSuccessTime": "2023-11-09 17:53:55"},"latestBarrierTime": "","recovery point objective": "3:32:17","failover recovery point time": ""}

show-progress命令显示的主要字段释义如下

  • priorKey:该备份集是基于这个backup key生成的。
  • actionType:备份集当前的操作类型。
    取值包括如下:
    • Backup,表示备份阶段。
    • Restore,表示恢复阶段。
  • progress:备份或恢复操作的进度。
  • currentStep:备份或恢复正在执行的步骤。
  • unrestoreKeys:待恢复的key列表。
  • failedStep:备份或恢复失败的步骤,初始值默认为INIT。
  • errorMsg:备份或恢复失败的错误信息,如果成功,该字段则显示成功的信息。
  • errorCode:备份或恢复的错误码,该字段为预留字段,暂未使用。
  • actionStartTime:当前操作的开始时间。
  • actionEndTime:当前操作的结束时间。
  • updateTime:当前操作进度的刷新时间。
  • backupState:当前备份状态(backuping/stopped/waiting/abnormal)。
  • restoreState: 当前恢复状态(restoring/stopped/waiting/abnormal)。
  • backupSuccessTime:上次成功备份结束的时间。
  • restoreSuccessTime:上次成功恢复结束的时间。
  • latestBarrierTime:上次主备集群一致点时间。
  • recovery point objective:rpo时间(当前主集群时间到最后一个恢复成功的备份集备份开始时间)。
  • failover recovery point time:failover未同步时间点。

4.3 结果验证

4.3.1 功能验证

同步前后进行数据一致性校验,主表、备表数据进行checksum校验,检查是否同步正确(需排除由于接入业务导致的差异)。

4.3.2 性能验证

通过show-progress监控工具可以看到最近一次备份、恢复耗时。

5. 解除容灾

5.1 停止容灾

# 主备集群取消沙箱外的定时容灾任务,在备份/恢复任务前添加注释符“#”,取消定时任务
crontab
-e
# 主集群Ruby用户登录主节点沙箱,停止备份
python3 $GPHOME
/script/SyncDataToStby.py -t stop-backup --config-file /DWS/data2/finedisaster/backupRestore.ini --disaster-fine-grained
# 备集群Ruby用户登录主节点沙箱,停止恢复
python3 $GPHOME
/script/SyncDataToStby.py -t stop-restore --config-file /DWS/data2/finedisaster/backupRestore.ini --disaster-fine-grained

5.2 删除容灾

警告

当不再需要容灾任务的时候,可以解除主备关系,恢复备集群的读写能力。删除容灾前需要先停止容灾。

Ruby用户登录主集群主节点的沙箱内。

python3 $GPHOME/script/SyncDataToStby.py -t set-independent --config-file /DWS/data2/finedisaster/backupRestore.ini --disaster-fine-grained

系统回显:

Delete csv file.
Delete roachbackup file
fromXXX.XXX.XXX.XX
Delete roachbackup file
fromXXX.XXX.XXX.XX
Clear cluster disaster state.

6. 总结

本文介绍了在云上环境的双集群(不跨Region不跨VPC)后台手动部署并使用细粒度容灾的主要步骤,分为容灾前准备、细粒度容灾操作和解除容灾,使得用户能快速方便得搭建起细粒度容灾。

点击关注,第一时间了解华为云新鲜技术~