• [技术干货] 【第四届openGauss征文活动参赛作品】openGauss易知易会的几个实用特性
    使用openGauss已经有很长一段时间了,本文将介绍几个简单易用的数据库特性。单列显示整行数据where比较列合并独立写布尔列using关键字domain单列显示整行数据首先我们准备测试数据表:create table users(id int,name text,email text,deleted_at timestamp,delete boolean); insert into users values(1,'jerome','chong.peng@enmotech.com',null,false); insert into users values(2,'sara','lynn.wood@poland.com','2001-09-11',true); insert into users values(3,'dolores','th000@sky.com',null,false); insert into users values(4,'evan','rachel.moore@hollywood.com',null,false);通常我们使用如下语句进行查询openGauss# select * from users; id | name | email | deleted_at | delete ----+---------+----------------------------+---------------------+-------- 1 | jerome | chong.peng@enmotech.com | | f 2 | sara | lynn.wood@poland.com | 2001-09-11 00:00:00 | t 3 | dolores | th000@sky.com | | f 4 | evan | rachel.moore@hollywood.com | | f (4 rows)也可以使用下面的语句进行查询,尤其是列较多时openGauss# select users from users; users ------------------------------------------------------- (1,jerome,chong.peng@enmotech.com,,f) (2,sara,lynn.wood@poland.com,"2001-09-11 00:00:00",t) (3,dolores,th000@sky.com,,f) (4,evan,rachel.moore@hollywood.com,,f) (4 rows)上面是将所有列作为行类型返回单列,可以比较简洁的返回数据。where比较列合并假设我们有以下查询:select id, name, email from users where name = 'dolores' and email = 'th000@sky.com';根据名称和邮箱查询用户,有的时候where条件后面可能会出现1=1select id, name, email from users where 1=1 and name = 'dolores' and email = 'th000@sky.com';应用层需要比较方便进行where条件拼接。其实可以去掉and,使用如下语句:select id, name, email from users where (name, email) = ('dolores','th000@sky.com');可以查询到同样的结果 id | name | email ----+---------+--------------- 3 | dolores | th000@sky.com (1 row)我们还可以使用in来满足or条件,例如下面的查询:select id, name, email from users where deleted_at is null and ( (name = 'dolores' and email = 'th000@sky.com') or (name = 'evan' and email = 'rachel.moore@hollywood.com') );可以将其缩短为:select id, name, email from users where deleted_at is null and (name, email) in (('dolores','th000@sky.com'),('evan','rachel.moore@hollywood.com'));这可以使查询更短且更易于阅读。独立写布尔列接下来的查询,获取未删除的用户,比较常见的是这种写法:select id, name, email from users where delete = false;多数人并不知道布尔值不需要与另一个布尔值进行比较,可以这样写:select id, name, email from users where not delete;这样阅读起来也更好,结果如下: id | name | email ----+---------+---------------------------- 1 | jerome | chong.peng@enmotech.com 3 | dolores | th000@sky.com 4 | evan | rachel.moore@hollywood.com (3 rows)using关键字当我们做多张表的join连接时,如果join字段的名称相同可以使用using关键字来简化语句select ... from t1 join t2 on t1.id = t2.id;可以改写为:select ... from t1 join t2 using (id);多个字段还可以使用逗号进行分隔:on t1.a = t2.a and t1.b = t2.b改写为using (a,b);domaindomain也是比较有用的一个特性,例如可以很多需要进行相同限制的列创建自定义类型:create domain my_addr varchar(100) not null default 'n/a';或者是作为别名支持兼容性数据类型:create domain binary_float as float; 本文总结了几个有帮助的实用特性,大家在日常使用过程中可以进一步挖掘。
  • [技术干货] 【第四届openGauss征文活动参赛作品】参与鲲鹏应用创新大赛,openGuas赛道的体会
    我们是参与的是关于openGauss赛道,我们做的是关于基于openGuass全密态数据库相关的内容。收获有很多,从openGauss在本地的部署,再到对openGauss源码的学习,再到对openGauss全密态能力的了解,对openGauss的了解越来越深,也越发感觉他的丰富的功能特性。1.openGauss的本地部署,我使用的是这个教程 地址 cid:link_0由于开始对linux命令的不熟悉,从而导致使用openGauss的一键部署脚本的使用出了很多问题,所以没出一次问题,linux环境就出了很多问题,导致无法继续下去,因此只能选择重装系统,再无数次重装系统重新跑脚本,改配置下,终于成功在本地部署了openGauss数据库。主要有以下几个问题:(1).虚拟机的硬盘分配过小,导致openGauss安装部署失败,最开始我只分配了20G硬盘和1G内存,导致openGauss的最后一步python运行脚本安装部署openGauss报错,后来使用的是60G硬盘和4G内存,就能部署成功了。(2).由于在本地虚拟机使用的是centos 7.6操作系统,这个系统上的python默认版本是python2,而安装部署openGauss脚本的要使用python3版本,因此需要重新安装python版本,但是脚本那一步在部署的时候会导致python命令和yum命令都没办法使用,因此只需要使用yum install python3 并且使用python3 执行最后的python文件脚本就行。 不用改配置的bak内容,改了反而更容易报错。(3).要保住虚拟机能联网,由于教程会在线下载内容,因此不联网,也会导致安装部署不成功,而且要改教程脚本里的ip地址。2.对openGauss源码的学习,主要是对openGauss的安全特性源码进行学习,包括密态等值大小比较。目前还在学习过程中,包括openGauss的全密态数据库特性也在学习中,等后续学了内容再更新。
  • [技术干货] 【第四届openGauss征文活动参赛作品】一种可能是目前最快的从ORACLE同步数据到MogDB(openGauss)的方式
    一、前言目前openGauss支持oracle中的绝大部分数据类型,基本上不用考虑类型转换的问题。所以从oracle到MogDB(openGauss)的数据同步无非就是从oracle里查出来再插入到MogDB(openGauss)中,只是查出来的结果是存成文件还是放内存而已。用文件的形式,oracle端有sqluldr2可以快速导出,MogDB(openGauss)端可以用copy命令快速载入;如果是放内存,则需要一定量的代码开发,而且目前通用的数据同步工具在导入时,大多用的是逐行insert命令,效率很低。所以,是否存在一种可能,只利用现有的工具,就能实现数据的高速同步且不需要存文件?在说这个方案前,先简单说明一下这个方案的几个知识点。二、知识点1.sqluldr2sqluldr2是楼方鑫针对Oracle数据库开发的数据快速导出工具,应该绝大多数oracle用户都用过,因为它依旧是目前从oracle中导出文本数据最快的方式了,速度远超oracle官方的sqlplus spool导出。但是大多数人基本上都只使用其导出文件的功能,而不知道这个工具还可以导出标准输出(所谓标准输出即不生成文件,直接打印在屏幕上)sqluldr2完整文档:https://www.doc88.com/p-6522591706862.html2.gsql/psql(\copy 元命令)gsql/psql的"\copy"元命令(注意区别于sql命令中的"copy",“copy"是服务器端的,”\copy"是客户端的),常用于和表和文件之间的导入和导出,效率很快,因为已经指定了表,不需要再进行字段类型的识别,绕开了sql解析引擎,直接写入表中。但大多数人很少用到 stdin和stdout(除非是基于其他高级语言进行数据导入导出的开发),这里的stdin即为标准输入,如果执行"\copy 表名 from stdin",则会让你继续输入数据,然后客户端会把数据保存到对应的表中。3.gsql/psql (-c参数)gsql/psql 的 "-c"参数,可以在连接数据库后马上执行sql命令然后断开登录4.linux ( | 管道符)linux的管道符"|",可以用于输入的重定向,即把前面一条命令的输出,作为后面一条命令的输入三、方案说明通过以上内容,很自然的可以联想到一种方式,即使用sqluldr2的标准输出,直接作为gsql \copy的标准输入。所谓的两点之间直线最短,用最快的导出加上最快的导入,且省去中间存储文件的阶段,理想状态下,这个速度仅受限于源端或目标端最慢的那一端,比如导出7分钟,导入8分钟,一般总计传输时间就是15分钟,但是用本文的方案,这个传输时间可能就只有8分钟了,因为它是导入导出同时进行的!四、操作步骤需要有个服务器能同时连接oracle及MogDB(openGauss),当然直接用这两个服务器之一也可以,只是注意要安装另一个数据的客户端,本文测试是在mogdb数据库的服务器上执行的oracle客户端下载链接https://www.oracle.com/cn/database/technologies/instant-client/linux-x86-64-downloads.htmlMogDB(openGauss)没有提供单独的客户端压缩包,客户端在数据库的安装包中对应的tools压缩包,比如MogDB-3.0.1-CentOS-64bit-tools.tar.gzhttps://mogdb.io/downloads/mogdb/安装客户端,这两个客户端的安装方式差不多,就是解压,然后配置环境变量LD_LIBRARY_PATH,比如配置连接Oracle的环境变量如下(如果需要永久配置则要修改对应的配置文件) export LD_LIBRARY_PATH=/opt/mogdb/instantclient_21_7:$LD_LIBRARY_PATH 下载sqluldr2程序这个就自己在网上搜吧,基本下载下来是一个压缩包,里面有两个windows版的和两个linux版的,我们需要的是"sqluldr2_linux64_10204.bin"这个文件,下完后可以把文件名改短点,比如"sqluldr2"先找个小表测试下sqluldr2能否导出文件,文件正常生成,说明oracle客户端配置正确 ./sqluldr2 scott/tiger@192.168.163.108/orcl query=emp quote=0x22 field="," degree=8 file=123.csv [omm@MiWiFi-R3G-srv mogdb]$ cat 123.csv 7369,"SMITH","CLERK",7902,"1980-12-17 00:00:00",800,,20 7499,"ALLEN","SALESMAN",7698,"1981-02-20 00:00:00",1600,300,30 7521,"WARD","SALESMAN",7698,"1981-02-22 00:00:00",1250,500,30 7566,"JONES","MANAGER",7839,"1981-04-02 00:00:00",2975,,20 7654,"MARTIN","SALESMAN",7698,"1981-09-28 00:00:00",1250,1400,30 7698,"BLAKE","MANAGER",7839,"1981-05-01 00:00:00",2850,,30 7782,"CLARK","MANAGER",7839,"1981-06-09 00:00:00",2450,,10 7788,"SCOTT","ANALYST",7566,"1987-04-19 00:00:00",3000,,20 7839,"KING","PRESIDENT",,"1981-11-17 00:00:00",5000,,10 7844,"TURNER","SALESMAN",7698,"1981-09-08 00:00:00",1500,0,30 7876,"ADAMS","CLERK",7788,"1987-05-23 00:00:00",1100,,20 7900,"JAMES","CLERK",7698,"1981-12-03 00:00:00",950,,30 7902,"FORD","ANALYST",7566,"1981-12-03 00:00:00",3000,,20 7934,"MILLER","CLERK",7782,"1982-01-23 00:00:00",1300,,10在目标端建立一个同样的表 gsql -d postgres -r -p 26000 create schema scott; create table SCOTT.EMP ( empno NUMBER(4) not null, ename VARCHAR2(10), job VARCHAR2(9), mgr NUMBER(4), hiredate DATE, sal NUMBER(7,2), comm NUMBER(7,2), deptno NUMBER(2) ); ★测试通过管道传输数据,没有报错 ./sqluldr2 scott/tiger@192.168.163.108/orcl query=emp quote=0x22 field="," degree=8 file=- |gsql -d postgres -Umogdb -WEnmo@123 -hlocalhost -p26000 -c "\copy scott.emp from stdin DELIMITER ',' quote '\"' csv" 在目标端查询scott.emp表,数据和源端一致MogDB=# select * from scott.emp; empno | ename | job | mgr | hiredate | sal | comm | deptno -------+--------+-----------+------+---------------------+---------+---------+-------- 7369 | SMITH | CLERK | 7902 | 1980-12-17 00:00:00 | 800.00 | | 20 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 00:00:00 | 1600.00 | 300.00 | 30 7521 | WARD | SALESMAN | 7698 | 1981-02-22 00:00:00 | 1250.00 | 500.00 | 30 7566 | JONES | MANAGER | 7839 | 1981-04-02 00:00:00 | 2975.00 | | 20 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 00:00:00 | 1250.00 | 1400.00 | 30 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 00:00:00 | 2850.00 | | 30 7782 | CLARK | MANAGER | 7839 | 1981-06-09 00:00:00 | 2450.00 | | 10 7788 | SCOTT | ANALYST | 7566 | 1987-04-19 00:00:00 | 3000.00 | | 20 7839 | KING | PRESIDENT | | 1981-11-17 00:00:00 | 5000.00 | | 10 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 00:00:00 | 1500.00 | 0.00 | 30 7876 | ADAMS | CLERK | 7788 | 1987-05-23 00:00:00 | 1100.00 | | 20 7900 | JAMES | CLERK | 7698 | 1981-12-03 00:00:00 | 950.00 | | 30 7902 | FORD | ANALYST | 7566 | 1981-12-03 00:00:00 | 3000.00 | | 20 7934 | MILLER | CLERK | 7782 | 1982-01-23 00:00:00 | 1300.00 | | 10 (14 rows)五、简单性能测试由于硬盘空间不够,测试数据库也是虚拟机和docker中的,就不做详细的测试了,只做个简单的100万数据测试 SQL> select count(1) from scott.T_TEST1; COUNT(1) ---------- 1000000 直接管道传输,用时7s [omm@MiWiFi-R3G-srv mogdb]$ echo $(date) Thu Sep 15 04:24:23 EDT 2022 [omm@MiWiFi-R3G-srv mogdb]$ ./sqluldr2 scott/tiger@192.168.163.108/orcl query=SCOTT.T_TEST1 quote=0x22 field="," degree=8 file=- |gsql -d postgres -Umogdb -WEnmo@123 -hlocalhost -p26000 -c "\copy SCOTT.T_TEST1 from stdin DELIMITER ',' quote '\"' csv" [omm@MiWiFi-R3G-srv mogdb]$ echo $(date) Thu Sep 15 04:24:30 EDT 2022 在目标端删除测试表,重建,然后用导出文件再导入的方式,导出5s,导入7s,一共12s [omm@MiWiFi-R3G-srv mogdb]$ echo $(date) Thu Sep 15 04:20:00 EDT 2022 [omm@MiWiFi-R3G-srv mogdb]$ ./sqluldr2 scott/tiger@192.168.163.108/orcl query=SCOTT.T_TEST1 quote=0x22 field="," degree=8 file=1234.csv 0 rows exported at 2022-09-15 04:20:00, size 0 MB. 1000000 rows exported at 2022-09-15 04:20:05, size 48 MB. output file 1234.csv closed at 1000000 rows, size 48 MB. [omm@MiWiFi-R3G-srv mogdb]$ echo $(date) Thu Sep 15 04:20:05 EDT 2022 [omm@MiWiFi-R3G-srv mogdb]$ echo $(date) Thu Sep 15 04:23:03 EDT 2022 [omm@MiWiFi-R3G-srv mogdb]$ gsql -d postgres -Umogdb -WEnmo@123 -hlocalhost -p26000 -c "\copy SCOTT.T_TEST1 from '/opt/mogdb/1234.csv' DELIMITER ',' quote '\"' csv" [omm@MiWiFi-R3G-srv mogdb]$ echo $(date) Thu Sep 15 04:23:10 EDT 2022 大致判断,这次测试的速度上限受导入影响,如果再优化一下数据库相关参数,耗时可以更低,但这里主要是提供一个可行的快速传输数据的思路,就不去做参数调整了。六、总结了解了这个原理,完全可以写一个程序或者shell脚本,通过把配置进行参数化处理,来制作一个Oracle到openGauss的通用高速数据传输工具。本文作者: DarkAthena本文链接: https://www.darkathena.top/archives/transport-data-from-oracle-to-opengauss-faster版权声明: 本博客所有文章除特别声明外,均采用CC BY-NC-SA 3.0 许可协议。转载请注明出处!
  • [技术干货] 【第四届openGauss征文活动参赛作品】openGauss初学者快速入门
    前言我第一次接触openGauss是再2022年8月份,当时有一个鲲鹏应用大赛,我对于openGauss很陌生,在网上看了很多有关openGauss的视频,我在bilibili上看到松鼠会发布的openGauss讲解是最多的,然后我就对着视频搭建openGauss,当时还好我电脑上一直有centos7的虚拟环境,所以我直接利用centos搭建openGauss,这里给大家分享一下我入门的学习是怎么样的。一、openGauss是什么?官方解释:openGauss是一款全面友好开放,携手伙伴共同打造的企业级开源关系型数据库。openGauss提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。openGauss深度融合华为在数据库领域多年的研发经验,结合企业级场景需求,持续构建竞争力特性。openGauss网站 (https://opengauss.org/zh/ ) 提供了有关openGauss软件的最新信息。个人理解:我认为是一个基于Linux的一款国内数据库,其实和Mysql等数据库一样,但是主要还是觉得这是我们骄傲的民族企业华为开发的,安全方面我会更放心。最主要的原因是openGauss属于开源数据库,对我们开发者来说是很有好的,学习起来也很方便。二、openGauss软件架构下面这张图也是我从官方的资料中找到的:这张图片,我们可以很明显的看到,在openGauss数据库中,是有一个单独的备份机制存在,也是对数据防止丢失做的一种机制吧。其中图片中的名词下面附上官方解释:OM运维管理模块(Operation Manager)。提供数据库日常运维、配置管理的管理接口、工具。个人理解:方便维护的管理员模块CM数据库管理模块(Cluster Manager)。管理和监控数据库系统中各个功能单元和物理资源的运行情况,确保整个系统的稳定运行。个人理解:针对数据库的管理模块客户端驱动客户端驱动(Client Driver)。负责接收来自应用的访问请求,并向应用返回执行结果。客户端驱动负责与openGauss实例通信,发送应用的SQL命令,接收openGauss实例的执行结果。openGauss(主备)openGauss主备(Datanode)。负责存储业务数据、执行数据查询任务以及向客户端返回执行结果。openGauss实例包含主、备两种类型,支持一主多备。建议将主、备openGauss实例分散部署在不同的物理节点中。个人理解:安全机制的考虑Storage服务器的本地存储资源,持久化存储数据。个人理解:就是长久的本地资源三、openGauss特点特点针对特点解释高性能通过列存储、向量化执行引擎、融合引擎等关键技术,实现百亿数据量查询秒级响应高可用同城跨AZ(Available Zone)容灾,数据不丢失,分钟级恢复高安全性支持访问控制、加密认证、数据库审计、动态数据脱敏等安全特性,提供全方位端到端的数据安全保护可维护性好支持WDR诊断、慢SQL诊断、Session诊断等多种维护手段,准确快速定位问题。具备AI4DB能力,能够通过AI算法实现数据库自调优、自监控、自诊断等我个人认为最大的特点就是,高安全性,但是目前还在发展阶段,openGauss的开发人员真的是需要加把劲了,之前我在学习openGauss的时候,就发现网站是存在漏洞的,而且很多,如果不更加细致的去处理好的话,对于数据的安全真的是不敢恭维!!!! 希望官方大大还是更加注意开发质量四、个人学习1.下载我个人选择的是极简版,也是为了方便,在文章开始我也说了,我主要还是搭建在centos7上面​2.安装环境centos7​3.基础配置解压之后,我们按照顺序输入下面的命令,使用VIM打开config文件。 vim /etc/selinux/config 修改“SELINUX”的值“disabled”,这里用的是vim编辑器,如果不懂的话请自学一下 SELINUX=disabled 重启centos。 reboot 检查防火墙状态。 systemctl status firewalld 在没有关闭的情况下,关闭防火墙 systemctl disable firewalld.service systemctl stop firewalld.service4.其他配置设置编码,/etc/profile文件下添加“export LANG=Unicode vim /etc/profile 设置时间 date #查看时间 将/usr/share/zoneinfo/目录下的时区文件复制为/etc/localtime文件下面 cp /usr/share/zoneinfo/配置的时间地点 /etc/localtime基本上因为centos7很多配置是已经默认的,所以相对而言搭建很简单。五、建议1.推广形式可以加强,首先,我了解到推广的形式,主要还是比赛,活动,我认为可以发布会,公众号视频,还可以找博主在一些平台上,撰写一个月,效果可能会更好。2.建议可以出版openGauss的书籍,出版,推广书籍(这个我看到捉虫活动有这样的书籍)3.另外是,我个人感觉对于openGauss的学习视频资料太少了,可以联合直播,扩展了解的人群。4.可以在行业中找到突破口,目前大厂数据库更换的话太昂贵,可以先占领中小型公司的市场。最后,支持华为,支持松鼠会,支持openGauss
  • [技术干货] 【第四届openGauss征文活动参赛作品】奇思妙想——通过Go语言自制安装openGauss二进制程序
    前言巧妙利用go语言自制openGauss安装二进制程序,经测试15s即可安装完成一、安装go语言环境1. 下载解压go[root@node1 ~]# wget https://golang.google.cn/dl/go1.19.1.linux-amd64.tar.gz[root@node1 ~]# tar -zxvf go1.19.1.linux-amd64.tar.gz -C /usr/local2. 添加环境变量export PATH=$PATH:/usr/local/go/bin3. 测试运行package main import fmt func main(){ fmt.Println("hello,world!") }[root@node1 ~]# go run test.go hello,world!二、 openGauss安装 (15s安装完成)1. 源代码[root@node1 ~]# cat gaussdb.go package main import ( "fmt" "io/ioutil" "os/exec" ) func main() { cmd := exec.Command("/bin/bash", "-c", `useradd omm ;echo "Enmo@123" | passwd --stdin omm > /dev/null ;mkdir -p /opt/mogdb/software;chown -R omm:omm /opt/;tar -xf openGauss-3.1.0-CentOS-64bit.tar.bz2 -C /opt/mogdb/software; su - omm -c "echo 'export GAUSSHOME=/opt/mogdb/software' >> /home/omm/.bashrc ;echo 'export PATH=\$GAUSSHOME/bin:\$PATH' >> /home/omm/.bashrc ;echo 'export LD_LIBRARY_PATH=\$GAUSSHOME/lib:\$LD_LIBRARY_PATH' >> /home/omm/.bashrc;source /home/omm/.bashrc;gs_initdb --pgdata=/opt/mogdb/data --nodename=primary --pwpasswd=Enmo@123 --encoding=UTF-8 --locale=en_US.UTF-8 > /dev/null ;echo \"port=26000\" >> /opt/mogdb/data/postgresql.conf;echo \"listen_addresses = '0.0.0.0'\" >> /opt/mogdb/data/postgresql.conf;echo \"password_encryption_type = 0\" >> /opt/mogdb/data/postgresql.conf;echo \"log_directory = 'pg_log'\" >> /opt/mogdb/data/postgresql.conf;echo \"remote_read_mode=non_authentication\" >> /opt/mogdb/data/postgresql.conf;echo \"host all all 0.0.0.0/0 md5\" >> /opt/mogdb/data/pg_hba.conf;gs_ctl start -D /opt/mogdb/data > /dev/null ;gsql -d postgres -p 26000 -c'select version();select pg_postmaster_start_time();';echo -e 'data_user is omm ! \ndata_port is 26000 ! \ndata_path is /opt/mogdb/data ! \ndata_soft is /opt/mogdb/software !'"`) // cmd := exec.Command("/bin/bash", "-c", `df -h;ls`) //创建获取命令输出管道 stdout, err := cmd.StdoutPipe() if err != nil { fmt.Printf("Error:can not obtain stdout pipe for command:%s\n", err) return } //执行命令 if err := cmd.Start(); err != nil { fmt.Println("Error:The command is err,", err) return } //读取所有输出 bytes, err := ioutil.ReadAll(stdout) if err != nil { fmt.Println("ReadAll Stdout:", err.Error()) return } if err := cmd.Wait(); err != nil { fmt.Println("wait:", err.Error()) return } fmt.Printf("stdout:\n\n %s", bytes) }2. go run 测试运行准备安装包[root@node1 ~]# ls openGauss-3.1.0-CentOS-64bit.tar.bz2 gaussdb.go gaussdb.go openGauss-3.1.0-CentOS-64bit.tar.bz2go run[root@node1 ~]# go run gaussdb.go stdout: version ------------------------------------------------------------------------------------------------------------------------------------------------------ (openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row) pg_postmaster_start_time ------------------------------- 2022-09-28 13:38:28.550462+08 (1 row) data_user is omm ! data_port is 26000 ! data_path is /opt/mogdb/data ! data_soft is /opt/mogdb/software !连接测试 ```s [root@node1 ~]# su - omm Last login: Wed Sep 28 13:39:38 CST 2022 [omm@node1 ~]$ gsql -d postgres -p26000 -r gsql ((openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help.openGauss=# select version(); version(openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row)openGauss=# \q [omm@node1 ~]$ logout- go build 二进制 ```s [root@node1 ~]# go build gaussdb.go [root@node1 ~]# ls gaussdb openGauss-3.1.0-CentOS-64bit.tar.bz2 gaussdb.go gaussdb gaussdb.go openGauss-3.1.0-CentOS-64bit.tar.bz2清理环境[root@node1 ~]# cat a.sh pkill -9 gaussdb rm -rf /opt/mogdb/* userdel -r omm [root@node1 ~]# sh a.sh安装 ```s [root@node1 ~]# date;./gaussdb;date Wed Sep 28 13:53:12 CST 2022 stdout: version(openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row)pg_postmaster_start_time2022-09-28 13:53:27.034021+08 (1 row)data_user is omm ! data_port is 26000 ! data_path is /opt/mogdb/data ! data_soft is /opt/mogdb/software ! Wed Sep 28 13:53:27 CST 2022- 连接测试 ```s [root@node1 ~]# su - omm Last login: Wed Sep 28 13:53:19 CST 2022 [omm@node1 ~]$ gsql -d postgres -p26000 -r gsql ((openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help. openGauss=# select version(); version ------------------------------------------------------------------------------------------------------------------------------------------------------ (openGauss 3.1.0 build 2c0ccaf9) compiled at 2022-09-25 19:32:58 commit 0 last mr on x86_64-unknown-linux-gnu, compiled by g++ (GCC) 7.3.0, 64-bit (1 row) openGauss=# \q
  • [技术干货] 【第四届openGauss征文活动参赛作品】openGauss账本数据库,你不知道的那些事儿
    openGauss账本数据库,你不知道的那些事儿摘要本文将通过对比官方文档关于 “设置账本数据库” 中的几个章节,结合源码来说说文档中操作步骤背后的原理。账本数据库概述你知道的那些事儿官方文档账本数据库融合了区块链思想,将用户操作记录至两种历史表中:用户历史表和全局区块表。当用户创建防篡改用户表时,系统将自动为该表添加一个hash列来保存每行数据的hash摘要信息,同时在blockchain模式下会创建一张用户历史表来记录对应用户表中每条数据的变更行为;而用户对防篡改用户表的一次修改行为将记录至全局区块表中。由于历史表具有只可追加不可修改的特点,因此历史表记录串联起来便形成了用户对防篡改用户表的修改历史。你不知道的那些事儿操作步骤1.创建防篡改模式。openGauss=# CREATE SCHEMA ledgernsp WITH BLOCKCHAIN;首先在这个SQL中我们可以看到WITH BLOCKCHAIN ,这里说明创建出来的SCHEMA与普通的SCHEMA不同,但就行不同在哪里我们后面会提到。从语法解析看,增加了对BLOCKCHAIN的处理,标记了是否为账本模式。 CreateSchema ::= CREATE SCHEMA schema_name [ AUTHORIZATION user_name ] [WITH BLOCKCHAIN] [ schema_element [ ... ] ];CreateSchemaStmt 结构中增加了bool类型字段hasBlockChaintypedef struct CreateSchemaStmt { NodeTag type; char *schemaname; /* the name of the schema to create */ char *authid; /* the owner of the created schema */ bool hasBlockChain; /* whether this schema has blockchain */ List *schemaElts; /* schema components (list of parsenodes) */ TempType temptype; /* if the schema is temp table's schema */ List *uuids; /* the list of uuid(only create sequence or table with serial type need) */ } CreateSchemaStmt;你不知道的限制账本数据库对于ALTER SCHEMA的几个限制1、dbe_perf和snapshot两个模式不能ALTER为blockchain模式。 if (withBlockchain && ((strncmp(nspName, "dbe_perf", STR_SCHEMA_NAME_LENGTH) == 0) || (strncmp(nspName, "snapshot", STR_SNAPSHOT_LENGTH) == 0))) { ereport(ERROR, (errcode(ERRCODE_OPERATE_FAILED), errmsg("The schema '%s' doesn't allow to alter to blockchain schema", nspName))); }2、系统模式不能 ALTER 为blockchain模式。 if (withBlockchain && !g_instance.attr.attr_common.allowSystemTableMods && !u_sess->attr.attr_common.IsInplaceUpgrade && IsReservedName(nspName)) ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("The system schema \"%s\" doesn't allow to alter to blockchain schema", nspName)));3、包含了表的SCHEMA不能ALTER为blockchain模式。 /* * If the any table exists in the schema, do not change to ledger schema. */ StringInfo existTbl = TableExistInSchema(HeapTupleGetOid(tup), TABLE_TYPE_ANY); if (existTbl->len != 0) { if (withBlockchain) { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("It is not supported to change \"%s\" to blockchain schema which includes tables.", nspName))); } else { ereport(ERROR, (errcode(ERRCODE_RESERVED_NAME), errmsg("It is not supported to change \"%s\" to normal schema which includes tables.", nspName))); } }查看模式2.在防篡改模式下创建防篡改用户表。openGauss=# CREATE TABLE ledgernsp.usertable(id int, name text);你不知道的限制创建账本表的同时会自动创建一个“历史表”和“历史表的索引”。在建表时CreateCommand会调用AlterCreateChainTables,如果是账本表再去调用create_hist_relation来创建历史表CreateCommand -> AlterCreateChainTables -> create_hist_relation/* * AlterCreateChainTables * If it is a ledger usertable, that should invoking this function. * then create a history table. */ void AlterCreateChainTables(Oid relOid, Datum reloptions, CreateStmt *mainTblStmt) { Relation rel = NULL; rel = heap_open(relOid, AccessExclusiveLock); /* Ledger user table only support for the regular relation. */ if (!rel->rd_isblockchain) { heap_close(rel, NoLock); return; } create_hist_relation(rel, reloptions, mainTblStmt); heap_close(rel, NoLock); }历史表命名规则,参见函数get_hist_namebool get_hist_name(Oid relid, const char *rel_name, char *hist_name, Oid nsp_oid, const char *nsp_name) { errno_t rc; if (!OidIsValid(relid) || rel_name == NULL) { return false; } nsp_oid = OidIsValid(nsp_oid) ? nsp_oid : get_rel_namespace(relid); nsp_name = (nsp_name == NULL) ? get_namespace_name(nsp_oid) : nsp_name; int part_hist_name_len = strlen(rel_name) + strlen(nsp_name) + 1; if (part_hist_name_len + strlen("_hist") >= NAMEDATALEN) { rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%d_%d_hist", nsp_oid, relid); securec_check_ss(rc, "", ""); } else { rc = snprintf_s(hist_name, NAMEDATALEN, NAMEDATALEN - 1, "%s_%s_hist", nsp_name, rel_name); securec_check_ss(rc, "", ""); } return true; }表名最大长度 #define NAMEDATALEN 64如果没有超过长度限制:schema_table_hist如果超过长度限制:schema(oid)_talbe(oid)_hist,因为oid是unsigned int 类型最大值为4294967295为10位,所以这种命名规则的最大长度为10+1+10+1+4+\0=27,因此永远不会超过最大长度64。omm=# create schema aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa with blockchain; CREATE SCHEMA omm=# create table aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb(id int); CREATE TABLE历史表索引命名规则,参见函数get_hist_name /* now create index for this new history table */ char hist_index_name[NAMEDATALEN]; rc = snprintf_s(hist_index_name, NAMEDATALEN, NAMEDATALEN - 1, "gs_hist_%u_index", relid);命名规则:gs_hist_$(账本表oid)_index。3、修改防篡改用户表数据对防篡改用户表执行INSERT/UPDATE/DELETE。openGauss=# INSERT INTO ledgernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); INSERT 0 3 openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id; id | name | hash ----+-------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob | 8fcd74a8a6a4b484 3 | peter | f51b4b1b12d0354b (3 rows) openGauss=# UPDATE ledgernsp.usertable SET name = 'bob2' WHERE id = 2; UPDATE 1 openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id; id | name | hash ----+-------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 3 | peter | f51b4b1b12d0354b (3 rows) openGauss=# DELETE FROM ledgernsp.usertable WHERE id = 3; DELETE 1 openGauss=# SELECT *, hash FROM ledgernsp.usertable ORDER BY id; id | name | hash ----+------+------------------ 1 | alex | 1f2e543c580cb8c5 2 | bob2 | 437761affbb7c605 (2 rows)查看账本历史操作记录你知道的那些事儿官方文档前提条件系统中需要有审计管理员或者具有审计管理员权限的角色。数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。你不知道的那些事儿基本操作1、查询全局区块表记录。omm=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+---------------- -------------------------------------------------------------- 1 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | a41714001181a294 | 83927d11ba1fd678e8f4b0723a9cd5f2 | INSERT INTO led gernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); 2 | omm | omm | 2022-09-17 13:59:51.723068+00 | 16404 | ledgernsp | usertable | b3a9ed0755131181 | b5ee73b6c20c817230182f6373c78e20 | UPDATE ledgerns p.usertable SET name = 'bob2' WHERE id = 2; 3 | omm | omm | 2022-09-17 13:59:58.159596+00 | 16404 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | 0cc9938cf7f1ed7f7f1a03c29954380a | DELETE FROM led gernsp.usertable WHERE id = 3; (3 rows)注册钩子,在对账本做修改操作的时候注册的钩子函数ledger_ExecutorEnd被回调。/* * ledger_hook_init -- install of gchain block record hook. */ void ledger_hook_init(void) { t_thrd.security_ledger_cxt.prev_ExecutorEnd = (void *)ExecutorEnd_hook; ExecutorEnd_hook = ledger_ExecutorEnd; }生成globalhash规则全局区块表记录主要是生成globalhash.调用过程:ledger_ExecutorEnd --> ledger_gchain_append --> set_gchain_comb_string                                                                          --> get_next_g_blocknum                                                                          --> gen_global_hashset_gchain_comb_string,是一组字符串拼接成的:rel_name + nsp_name + query_string + rel_hashget_next_g_blocknum,用全局变量g_blocknum保存gen_global_hash,是的set_gchain_comb_string拼出来的串+上一条的hash值拼串然后再去hash——区块链的基本原理bool gen_global_hash(hash32_t *hash_buffer, const char *info_string, bool exist, const hash32_t *prev_hash) { errno_t rc = EOK; int comb_strlen; char *comb_string = NULL; /* * Previous block not exists means current insertion block is genesis, * then we use global systable as origin combine string for globalhash * generation. If previous block exists, we will use previous global * hash as combine string to calculate globalhash. */ if (!exist) { /* generate genesis block globalhash */ comb_strlen = strlen(GCHAIN_NAME) + strlen(info_string) + 1; comb_string = (char *)palloc0(comb_strlen); rc = snprintf_s(comb_string, comb_strlen, comb_strlen - 1, "%s%s", GCHAIN_NAME, info_string); securec_check_ss(rc, "", ""); } else { /* use previous globalhash and current block info to calculate globalhash. */ char *pre_hash_str = DatumGetCString(DirectFunctionCall1(hash32out, HASH32GetDatum(prev_hash))); comb_strlen = strlen(pre_hash_str) + strlen(info_string) + 1; comb_string = (char *)palloc0(comb_strlen); rc = snprintf_s(comb_string, comb_strlen, comb_strlen - 1, "%s%s", info_string, pre_hash_str); securec_check_ss(rc, "", ""); pfree_ext(pre_hash_str); } if (!pg_md5_binary(comb_string, comb_strlen - 1, hash_buffer->data)) { pfree(comb_string); ereport(ERROR, (errcode(ERRCODE_OUT_OF_MEMORY), errmsg("Failed to generate globalhash, out of memory"))); return false; } pfree(comb_string); return true; }在src/gausskernel/runtime/executor/nodeModifyTable.cpp中更新_hist表的hash值。通过set_user_tuple_hash得到账本表hash列的值。/* * set_user_tuple_hash -- calculate and fill the hash attribute of user table's tuple. * * tup: row data of user table * rel: user table * hash_exists: whether tuple comes with tuplehash. * * Note: if hash_exists is true, we should recompute * tuple hash and compare with tuplehash of itself. */ HeapTuple set_user_tuple_hash(HeapTuple tup, Relation rel, bool hash_exists) { uint64 row_hash = gen_user_tuple_hash(rel, tup); int hash_attrno = user_hash_attrno(rel->rd_att); if (hash_exists) { bool is_null; Datum hash = heap_getattr(tup, hash_attrno + 1, rel->rd_att, &is_null); if (is_null || row_hash != DatumGetUInt64(hash)) { ereport(ERROR, (errcode(ERRCODE_OPERATE_INVALID_PARAM), errmsg("Invalid tuple hash."))); } return tup; } Datum *values = NULL; bool *nulls = NULL; bool *replaces = NULL; /* Build modified tuple */ int2 nattrs = RelationGetNumberOfAttributes(rel); values = (Datum*)palloc0(nattrs * sizeof(Datum)); nulls = (bool*)palloc0(nattrs * sizeof(bool)); replaces = (bool*)palloc0(nattrs * sizeof(bool)); values[hash_attrno] = UInt64GetDatum(row_hash); replaces[hash_attrno] = true; HeapTuple newtup = heap_modify_tuple(tup, RelationGetDescr(rel), values, nulls, replaces); pfree_ext(values); pfree_ext(nulls); pfree_ext(replaces); return newtup; }校验账本数据一致性你知道的那些事儿官方文档数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。你不知道的那些事儿基本操作1、校验防篡改用户表ledgernsp.usertable与其对应的历史表是否一致。omm=# SELECT pg_catalog.ledger_hist_check('ledgernsp', 'usertable'); ledger_hist_check ------------------- t (1 row)校验用户权限 Only super user or audit admin have access right to blockchain nsp /* Only super user or audit admin have access right to blockchain nsp */ if (nsp_oid == PG_BLOCKCHAIN_NAMESPACE) { return gs_blockchain_aclmask(roleid, mask); }校验历史表hash值is_hist_hash_identity --> get_usertable_hash_sum                                --> get_histtable_hash_sum/* * is_hist_hash_identity -- check whether user table hash and history table hash are equal * * relid: user table oid * res_hash: hash sum of history table */ bool is_hist_hash_identity(Oid relid, uint64 *res_hash) { uint64 user_hash_sum; uint64 hist_hash_sum; char hist_name[NAMEDATALEN]; char *rel_name = get_rel_name(relid); if (!get_hist_name(relid, rel_name, hist_name)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("get hist table name failed."))); } Oid histoid = get_relname_relid(hist_name, PG_BLOCKCHAIN_NAMESPACE); if (!OidIsValid(histoid)) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("could not find hist table of \"%s\".", rel_name))); } user_hash_sum = get_usertable_hash_sum(relid); hist_hash_sum = get_histtable_hash_sum(histoid); *res_hash = hist_hash_sum; return user_hash_sum == hist_hash_sum; }2、查询防篡改用户表ledgernsp.usertable与其对应的历史表以及全局区块表中关于该表的记录是否一致。omm=# SELECT pg_catalog.ledger_gchain_check('ledgernsp', 'usertable'); ledger_gchain_check --------------------- t (1 row)校验是否为账本表ledger_usertable_check校验用户权限has_ledger_consistent_privilege校验历史表hash值is_hist_hash_identity计算/校验全局表hash get_gchain_relhash_sum/* * get_gchain_relhash_sum -- calculate relhash from gs_global_chain * * relid: user table oid */ static uint64 get_gchain_relhash_sum(Oid relid) { uint64 relhash = 0; HeapTuple tuple = NULL; /* scan the gs_global_chain catalog by relid */ Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock); Form_gs_global_chain rdata = NULL; TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { rdata = (Form_gs_global_chain)GETSTRUCT(tuple); if (rdata == NULL || rdata->relid != relid) { continue; } relhash += rdata->relhash; } heap_endscan(scan); heap_close(gchain_rel, AccessShareLock); return relhash; }归档账本数据库你知道的那些事儿官方文档前提条件:系统中需要有审计管理员或者具有审计管理员权限的角色。数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。数据库已经正确配置审计文件的存储路径audit_directory。你不知道的那些事儿基本操作1、对指定用户历史表进行归档操作。omm=# SELECT pg_catalog.ledger_hist_archive('ledgernsp', 'usertable'); ledger_hist_archive --------------------- t (1 row) omm=# SELECT * FROM blockchain.ledgernsp_usertable_hist; rec_num | hash_ins | hash_del | pre_hash ---------+------------------+------------------+---------------------------------- 4 | e78e75b00d396899 | 84e8bfc3b974e9cf | 6475a497b7a272a92bab012d7f3d615b (1 row)主要步骤如下:Copy user history table.Do unify and truncate.sum all hash_ins and hash_del for unification.Do real truncate.heap_truncate_one_relDo insertion for unified row.simple_heap_insertFlush history hash table cache.2、执行全局区块表导出操作omm=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+-------------------------------+-------+-----------+-----------+------------------+----------------------------------+---------------- -------------------------------------------------------------- 1 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | a41714001181a294 | 83927d11ba1fd678e8f4b0723a9cd5f2 | INSERT INTO led gernsp.usertable VALUES(1, 'alex'), (2, 'bob'), (3, 'peter'); 2 | omm | omm | 2022-09-17 13:59:51.723068+00 | 16404 | ledgernsp | usertable | b3a9ed0755131181 | b5ee73b6c20c817230182f6373c78e20 | UPDATE ledgerns p.usertable SET name = 'bob2' WHERE id = 2; 3 | omm | omm | 2022-09-17 13:59:58.159596+00 | 16404 | ledgernsp | usertable | 0ae4b4e4ed2fcab5 | 0cc9938cf7f1ed7f7f1a03c29954380a | DELETE FROM led gernsp.usertable WHERE id = 3; (3 rows) omm=# SELECT pg_catalog.ledger_gchain_archive(); ledger_gchain_archive ----------------------- t (1 row) omm=# SELECT * FROM gs_global_chain; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------- 2 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | 62a5b5ec53c47eca | 7252d09679b0b3836a2e63da17284ad5 | Archived. (1 row)gs_global_chain主要处理流程:Init and prepare bak dictionary.Using CopyStmt to copy global chain.Do unify and truncate.Using hash table to do unify, each hash_entry refers to one relid informations.Split gs_global_chain by relid, and accumulate rel_hash to a new record for each rel.Do rel truncate.Insert newest record to gchain order by relid.Flush global_hash cache.修复账本数据库你知道的那些事儿官方文档前提条件:系统中需要有审计管理员或者具有审计管理员权限的角色。数据库正常运行,并且对防篡改数据库执行了一系列增、删、改等操作,保证在查询时段内有账本操作记录结果产生。你不知道的那些事儿基本操作1、执行历史表修复操作omm=# select * from blockchain.ledgernsp_usertable_hist; rec_num | hash_ins | hash_del | pre_hash ---------+------------------+------------------+---------------------------------- 4 | e78e75b00d396899 | 84e8bfc3b974e9cf | 6475a497b7a272a92bab012d7f3d615b (1 row) omm=# SELECT pg_catalog.ledger_hist_repair('ledgernsp', 'usertable'); ledger_hist_repair -------------------- 0000000000000000 (1 row)[drawio] (rHmeQ8HWKS_RFXgP-oTUZINZguxBYqh2IV64Y0j5TAA.svg)2、执行全局区块表修复操作omm=# select * from gs_global_chain ; blocknum | dbname | username | starttime | relid | relnsp | relname | relhash | globalhash | txcommand ----------+--------+----------+------------------------------+-------+-----------+-----------+------------------+----------------------------------+----------- 2 | omm | omm | 2022-09-17 13:59:37.84824+00 | 16404 | ledgernsp | usertable | 62a5b5ec53c47eca | 7252d09679b0b3836a2e63da17284ad5 | Archived. (1 row) omm=# SELECT pg_catalog.ledger_gchain_repair('ledgernsp', 'usertable'); ledger_gchain_repair ---------------------- 62a5b5ec53c47eca (1 row)首先判断用户权限,之后通过get_gchain_relhash_sum函数计算relhash字段/* * get_gchain_relhash_sum -- calculate relhash from gs_global_chain * * relid: user table oid */ static uint64 get_gchain_relhash_sum(Oid relid) { uint64 relhash = 0; HeapTuple tuple = NULL; /* scan the gs_global_chain catalog by relid */ Relation gchain_rel = heap_open(GsGlobalChainRelationId, AccessShareLock); Form_gs_global_chain rdata = NULL; TableScanDesc scan = heap_beginscan(gchain_rel, SnapshotNow, 0, NULL); while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL) { rdata = (Form_gs_global_chain)GETSTRUCT(tuple); if (rdata == NULL || rdata->relid != relid) { continue; } relhash += rdata->relhash; } heap_endscan(scan); heap_close(gchain_rel, AccessShareLock); return relhash; }主要是计算并修复gs_global_chain中的relhash字段。总结账本数据库其实并不像我们想象的那么复制,实际上就是利用了区块链的最基本的原理,即当前记录的特征值 + 上一条记录特征值的hash值,再进行hash。下一条与上一条记录具有数据关联性,形成“链”的结构,如果篡改了其中的数据,则会导致“链”断开,导致不能与后面数据记录形成hash关联。_hist表记录了用户表每一步数据变化的过程,gs_global_chain表记录了所有防篡改模式下对用户表的操作记录。用户表结合_hist和global表就能完整记录和校验。
  • [技术干货] 【第四届openGauss征文活动参赛作品】openGauss Cluster Manager RTO Test
    一、环境介绍1. 软件环境类别版本下载链接备注OSopenEuler 20.03 (LTS)cid:link_1操作系统BenchmarkSQL5.0cid:link_3驱动版本:postgresql-9.3-1102.jdbc41.jar模拟TPCC压力的程序驱动为程序自带的pg驱动Golanggo1.18 linux/arm64cid:link_5cid:link_2cid:link_4模拟应用连接的程序openGauss3.0.0cid:link_0数据库2. 硬件环境主机CPU规格硬盘职责node1Kunpeng-920虚拟机16c/64g通用型SSD主库node2Kunpeng-920虚拟机16c/64g通用型SSD同步备库go程序node3Kunpeng-920虚拟机16c/64g通用型SSD异步备库BenchmarkSQL程序3. 架构图二、测试场景1. go程序多IP连接测试RTO原理:CM检测主库发生故障,不可访问时会自动选新主,go驱动通过target_session_attrs=read-write控制只连主库,通过SQL select sysdate,pg_is_in_recovery(); 查询结果时间戳查看RTOgo程序代码[root@cloud001-0003 go]# cat 1.go // Copyright © 2021 Bin Liu package main import ( "database/sql" "fmt" _ "gitee.com/opengauss/openGauss-connector-go-pq" "log" "os" "os/signal" "syscall" "time" ) /* 需要有访问dbe_perf.global_instance_time的权限 CREATE USER dbuser_monitor with login monadmin PASSWORD 'Mon@1234'; grant usage on schema dbe_perf to dbuser_monitor; grant select on dbe_perf.global_instance_time to dbuser_monitor; CGO_ENABLED=0 GOOS=linux GOARCH=arm64 */ var ( dsnExample = `DSN="postgres://gaussdb:secret@foo,bar,baz/mydb?sslmode=disable" DSN="postgres://gaussdb:secret@foo:1,bar:2,baz:3/mydb?sslmode=disable" DSN="user=gaussdb password=secret host=foo,bar,baz port=5432 dbname=mydb sslmode=disable" DSN="user=gaussdb password=secret host=foo,bar,baz port=5432,5432,5433 dbname=mydb sslmode=disable"` ) func main() { os.Setenv("DSN", "postgres://gaussdb:Enmo12345@172.16.0.65:26000,172.16.0.202:26000,172.16.0.193:26000/postgres?"+ "sslmode=disable&loggerLevel=debug&target_session_attrs=read-write") connStr := os.Getenv("DSN") if connStr == "" { fmt.Println("please define the env DSN. example:\n" + dsnExample) return } fmt.Println("DNS:", connStr) db, err := sql.Open("opengauss", connStr) if err != nil { log.Fatal(err) } var ( newTimer = time.NewTicker(1 * time.Second) doClose = make(chan struct{}, 1) ) go func() { for { select { case <-newTimer.C: if err := getNodeName(db); err != nil { fmt.Println(err) } case <-doClose: newTimer.Stop() return } } }() sigChan := make(chan os.Signal, 2) signal.Notify(sigChan, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) //nolint:staticcheck defer signal.Stop(sigChan) <-sigChan doClose <- struct{}{} } func getNodeName(db *sql.DB) error { var err error // tx, err := db.Begin() // if err != nil { // return err // } // defer tx.Commit() var sysdate string var pgIsInRecovery bool var nodeName string err = db.QueryRow("select sysdate,pg_is_in_recovery();"). Scan(&sysdate, &pgIsInRecovery) if err != nil { return err } var channel string // err = db.QueryRow("select channel from pg_stat_get_wal_senders() limit 1 "). // Scan(&channel) fmt.Println(sysdate, nodeName, pgIsInRecovery, channel) // if err != nil { // return err // } return nil } 模拟数据库故障[omm@cloud001-0002 data]$ mv db1/ db1.bakgo程序连接数据库及重连时间时间差 ```s 2022/04/11 16:02:13.614273 connector.go:222: info dialing server host 172.16.0.65 port 26000 2022/04/11 16:02:20.683716 connector.go:145: debug find instance host 172.16.0.202 port 26000RTO时间7s ## 2. BenchmarkSQL多IP连接测试RTO > 原理:CM检测主库发生故障,不可访问时会自动选新主,jdbc驱动通过target_session_type=master控制只连主库,通过SQL 程序执行时间戳查看RTO **BenchmarkSQL模拟负载及重连时间** ![](https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbstemp/20221010/cmtybbs/002/54e/1fe/05b5e1232d00254e1fe6c017a981df34.20221010064417.22465626059862641794283274131784:20221010074417:2400:25ECFB3EAB696C25B6BF32DEF9562B6C0DDF9F99D7F9AAB2DE38A263EA3F531D.png) ![](https://fileserver.developer.huaweicloud.com/FileServer/getFile/cmtybbstemp/20221010/cmtybbs/002/54e/1fe/05b5e1232d00254e1fe6c017a981df34.20221010064447.94841903110897844984354683484747:20221010074447:2400:0F348177270F1B142E52AC69DCBD7F52F0BDED979B0E27D5346E2CEE79DA32F6.png) - 时间差 ```s 16:02:13,561 [Thread-8] ERROR jTPCCTData : Unexpected SQLException in STOCK_LEVELsage: 153MB / 897MB 16:02:20,834 [Thread-57] FATAL jTPCCTerminal : Unexpected SQLException on rollback: This connection has been closed. RTO时间7.273s3. 数据库端观测时间数据库日志时间差2022-04-11 16:02:13.253 tid=1795056 StartAndStop ERROR: data path disc writable test failed, /opt/mogdb/data/data/db1. 2022-04-11 16:02:20.438 tid=1815697 LOG: failover msg from cm_server, data_dir :/opt/mogdb/data/data/db1 nodetype is 2 RTO时间7.185s总结在有负载情况(tpcc压测产生负载,数据库服务器CPU占用50%左右)进行主库宕机测试,以主库宕机为起始点,备库成功作为新主库启动成功为终止点RTO为7.185s以主库宕机为起始点,模拟TPCC压测的benchmarkSQL程序成功重新连接到新主库为终止点RTO为7.273s以主库宕机为起始点,模拟其它应用连接数据库的go程序成功重新连接到新主库为终止点7s由于go程序至精确到s,猜测实际时间为7.185s以上综上所述openGauss Cluster Manager RTO约为7s左右
  • [技术干货] 【第四届openGauss征文活动参赛作品】干货输出【开源Mall4j商城系统-MySQL5.7数据库底座替换探索实践】
    系统介绍Mall4j是国内领先的电商商城系统源码提供商,是众多知名企业搭建商城的不二选择。Mall4j商城系统支持企业自营模式,招商模式,O2O门店模式,满足企业各个发展阶段的商业模式,全面覆盖微信商城、小程序商城、手机触屏版商城、苹果APP商城、安卓APP商城及PC端,统一的数据,能很大程度提高销售利润和提升服务质量,让您一次开店,全通路覆盖。Mall4j商城系统可以将企业的资源进行有效的整合,让商家的布局更加广泛、配套更加完善、管理更加成熟。另外,mall4j商城系统可以帮助企业集合各个渠道的资源,充分利用资源和渠道来提升商城的销量。还有多种营销功能:团购、秒杀、会员积分、优惠券、满减满折等营销玩法,把消费变成了一种“游戏”,有趣,有效果。系统亮点Mall4j商城系统功能亮点: 1、系统具备高效的管理功能:商品管理、会员管理、订单管理、统计管理、支付管理、门店管理、库存管理等管理功能; 2、灵活多样的营销赋能模式:满减、团购、秒杀、分销、积分、O2O、优惠券、套餐、赠品等营销方式; 3、多终端适配:支持PC、H5、小程序、APP(android、IOS)。Mall4j商城系统技术亮点:技术主流,使用最流行框架技术,没有过多的技术债务。提供全部源码,无封装无加密,没有license。更新升级方便,通过git私服进行代码合并更新。代码前后端分离,代码质量高注释清晰,方便二次开发。mall4j商城版本开源版银河版宇宙版白洞版业务模式B2CB2CB2B2CS2B2C适用群体个人、初学者自营店、小规模企业自营+入驻、中小型企业自营+供应商/商家入驻加密程度全开源,无加密提供全部源码,无加密提供全部源码,无加密提供全部源码,无加密服务内容商业授权AGPL3.0协议永久授权永久授权永久授权代码更新Gitee、Github更新Git私服更新Git私服更新Git私服更新企业售后群×√√√功能列表√√√√技术文档√√√√操作手册×√√√合同签约×√√√基础功能前后端分离√√√√Spring Cloud脚手架√××√Spring Boot脚手架√√√√权限管理√√√√完整下单流程√√√√小程序端√√√√H5端√√√√PC端×√√√App端(iOS、Android)×√√√后台管理移动端××√×后台管理PC端√√√√国际化×√√×高级功能多商家入驻√×√√供应商端×××√平台抽佣××√√PC端装修××√√移动端装修××√√优惠券×√√√满减满折×√√√拼团活动×√√√秒杀活动×√√√商品预售×√√√优惠套餐××√√赠品××√√分销模块×√√√会员管理×√√√积分商城×√√√虚拟商品××√√用户自提×√√×同城配送×√√×小程序直播×√√√客服系统(IM)×√√√进销存管理××√√客户标签与营销×√√√数据分析×√√√数据报表×√√√消息推送×√√√技术组件技术版本说明Spring Boot2.1.6.RELEASEMVC核心框架Spring Security oauth22.1.5.RELEASE认证和授权框架MyBatis3.5.0ORM框架MyBatisPlus3.1.0基于mybatis,使用lambda表达式的Swagger-UI2.9.2文档生产工具Hibernator-Validator6.0.17.Final验证框架redisson3.10.6对redis进行封装、集成分布式锁等hikari3.2.0数据库连接池log4j22.11.2更快的log日志工具fst2.57更快的序列化和反序列化工具orika1.5.4更快的bean复制工具lombok1.18.8简化对象封装工具hutool4.5.0更适合国人的java工具集swagger-bootstrap1.9.3基于swagger,更便于国人使用的swagger u工具版本jdk1.8+mysql5.7+redis3.2+技术架构SOA会通过ESB来作为系统和服务之间的通信桥梁,ESB本身还提供服务地址的管理、不同系统之间的 协议转化和数据格式转化等等。消费者不需要关心目标的服务位置,实现了服务消费者和服务生产者的高度解耦,SOA可以消除信息孤岛并实现共享业务重用。Mall4j商城系统则是使用了比起SOA还要灵活好用的微服务架构,微服务关注的是解耦,努力降低业务之间的耦合度,实现更多服务的复用,在DevOps有更大自由程度的持续交付。Mall4j商城系统的微服务技术包括Spring Cloud API网关、服务注册与发现、配置中心、负载均衡、分布式事务,其它相关技术包括有容器化技术、轻量级虚拟化、容器快速启停、易管理扩展、版本控制、应用安全隔离Mall4j商城系统应用使用范围电商系统B2C商城系统 【品牌电商】 单用户商城系统,统一后台管理、多终端覆盖B2B2C商城系统【平台电商】 多用户商城系统,自营+多商家入驻电商平台S2B2C商城系统【供应链】 融合供应链、为供应商、经销商、零售商赋能O2O商城系统【新零售】 线上线下融合,助力传统零食企业转型与发展应用终端电商平台【PC端、可视化】 可视化编辑,完善购物体验及商品数据分析商城小程序【小程序】 轻应用,体验优质,快速抢占移动电商市场商城APP【UNIAPP】 IOS、Android双APP,聚拢用户购物更便捷H5商城【HTML5】 手机浏览器,微信商城、随时随地五单支付。业务场景社交电商【移动电商】 融合拼团、会员分销等多种前沿社交营销模式积分商城【留存复购】 结合会员体系 、打造积分运营体系,引老留新跨境电商【海外】 海外支付,多语言切换,为企业提供跨境方案直播电商【引流获客】 微信小程序直播+电商模式,低成本变现流量Mall4j商城系统替换因素考虑Mall4j商城系统复杂,涉及的技术组件多,我们准备用OpenGauss替换MySQL5.7,改动的的东西涉及项目依赖文件、配置文件、解析文件,尤其数据库的内核能力以及对 应用框架的兼容 程度非常重要,假设开发者要大费周章大面积的改动,或者要动jar包的配置,估计OpenGauss替换MySQL5.7的计划就要搁浅或者放弃了。代码结构[root@enmoedu1 yami-b2b2c]# tree -L 1 . ├── CHANGELOG.md ├── compile.sh ├── db ├── doc ├── docker-compose.yml ├── LICENSE ├── log ├── mall4m ├── mall4uni ├── mall4v ├── pom.xml ├── README.md ├── screenshot ├── yami-shop-admin 商城后端接口服务管理系统,数据库操作有关 ├── yami-shop-api 商城前端接口调用系统,数据库操作有关 ├── yami-shop-bean ├── yami-shop-common pom.xml含有mysql的调用 ├── yami-shop-quartz ├── yami-shop-security ├── yami-shop-service └── yami-shop-sys src/main/resources/mapper/SysMenuMapper.xml 数据库操作有关 15 directories, 6 files pom.xml/opt/projects/yami-b2b2c/yami-shop-common目录, pom.xml内容如下,关于mysql的jdbc需要注释 改成 org.postgresql postgresql 42.2.2 logback-prod.xml/opt/projects/yami-b2b2c/yami-shop-admin/src/main/resources/logback/logback-prod.xml/opt/projects/yami-b2b2c/yami-shop-api/src/main/resources/logback/logback-prod.xml里面确定唯一的PROJECT_PATHapplication-dev.yml与 application-prod.ymldev是开发环境的配置参数 prod是生产环境的配置参数/opt/projects/yami-b2b2c/yami-shop-admin/src/main/resources/里面有两个文件application-dev.yml 和 application-prod.yml/opt/projects/yami-b2b2c/yami-shop-api/src/main/resources里面有两个文件application-dev.yml 和application-prod.yml把原来的注释,新的JDBC连接串如下 url: jdbc:postgresql://XXXXXX:15400/mytest username: henley password: XXXX driver-class-name: org.postgresql.Driver编译打包准备对代码进行编译打包,只需要在主代码下运行以下命令,这边主代码路径是/opt/projects/yami-b2b2c,当前目录下执行mvn clean package -DskipTests成功执行后如下所示自动在下面两处地方生成jar包,一个是商城后台接口,一个是商城前端接口/opt/projects/yami-b2b2c/yami-shop-admin/target/yami-shop-admin-0.0.1-SNAPSHOT .jar/opt/projects/yami-b2b2c/yami-shop-api/target/yami-shop-api-0.0.1-SNAPS HOT.jar开发调试 以开发的模式运行以下两个后端服务,可以获取DEBUG日志-Dspring.profiles.active=dev 意味着用开发模式运行 -Dspring.profiles.active=prod 意味着用生产模式运行以下命令启动后台服务,运行成功会发现8085端口打开nohup java -jar -Dspring.profiles.active=dev "/opt/projects/yami-b2b2c/yami-shop-admin/target/yami-shop-admin-0.0.1-SNAPSHOT.jar" > "/opt/projects/yami-b2b2c/yami-shop-admin/target/log/yami-shop-admin-console.log" &以下命令启动前端服务,运行成功会发现8086端口打开nohup java -jar -Dspring.profiles.active=dev "/opt/projects/yami-b2b2c/yami-shop-api/target/yami-shop-api-0.0.1-SNAPSHOT.jar" > "/opt/projects/yami-b2b2c/yami-shop-api/target/log/yami-shop-api-console.log" &查看控制台日志输出后台服务日志tail -f ${PROJECT_PATH}/log/admin.log前端服务日志tail -f ${PROJECT_PATH}/log/api.log启动后端服务管理平台进入 mall4v-master管理目录 [root@enmoedu1 mall4v-master]# npm run dev直接访问cid:link_0 ,它会直接与后台服务8085以及前端服务8086连线。数据库梳理ER图Mall4j一共有56个表示例MySQL表相关56个表都要做不同程度的修改,要修改替换的地方,举例MySQL表。CREATE TABLE `qrtz_job_details` ( `SCHED_NAME` varchar(120) unsigned NOT NULL AUTO_INCREMENT , `JOB_NAME` varchar(200) NOT NULL, `JOB_GROUP` varchar(200) NOT NULL, `DESCRIPTION` varchar(250) DEFAULT NULL, `JOB_CLASS_NAME` varchar(250) NOT NULL, `IS_DURABLE` varchar(1) NOT NULL, `IS_NONCONCURRENT` varchar(1) NOT NULL, `IS_UPDATE_DATA` varchar(1) NOT NULL, `REQUESTS_RECOVERY` varchar(1) NOT NULL, `JOB_DATA` blob, `JOB_tinyint` tinyint(2), `JOB_smallint` smallint(10), `JOB_int` int(10), `JOB_bigint` bigint(10), file_path varchar(255) DEFAULT NULL COMMENT '文件路径', file_type varchar(20) DEFAULT NULL COMMENT '文件类型', upload_time datetime DEFAULT NULL COMMENT '上传时间', `JOB_DATA_double` double(12,2), PRIMARY KEY (`SCHED_NAME`,`JOB_NAME`,`JOB_GROUP`), KEY `IDX_QRTZ_J_REQ_RECOVERY` (`SCHED_NAME`,`REQUESTS_RECOVERY`), KEY `IDX_QRTZ_J_GRP` (`SCHED_NAME`,`JOB_GROUP`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 数据类型数据类型的改造是一段工作量blob 改成 bytea tinyint(2) 改成 smallint smallint(10) 改成 smallint int(10) 改成 int bigint(10) 改成 bigint double(12,2) 改成 double precision datetime 改成 date数据索引openGauss的数据索引与MySQL的不一样 create index IDX_QRTZ_J_REQ_RECOVERY on qrtz_job_details (SCHED_NAME,REQUESTS_RECOVERY); create index IDX_QRTZ_J_GRP on qrtz_job_details(SCHED_NAME,JOB_GROUP);数据注释openGauss的数据注释与MySQL的不一样comment on column qrtz_job_details.file_path is '文件路径'; comment on column qrtz_job_details.file_type is '文件类型';自增ID表openGauss的自增ID与MySQL不一样 ,与主流的Postgresql的一模一样,它用自己的序列函数。GRANT ALL PRIVILEGES ON TABLE tz_sys_log TO henley; create sequence public.tz_sys_log_id start with 846 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_log_id owner to henley; alter table tz_sys_log alter column id set default nextval('public.tz_sys_log_id');UUID表tz_user表有user_id使用的是uuidopenGauss要支持uuid,可以通过FUNCTION,下面定义sys_guid的FUNCTIONmytest=# \sf sys_guid CREATE OR REPLACE FUNCTION pg_catalog.sys_guid() RETURNS character varying LANGUAGE sql NOT FENCED NOT SHIPPABLE AS $function$ select upper(md5(random()::text || clock_timestamp()::text)) $function$; mytest=# select sys_guid() ; sys_guid ---------------------------------- 72F965C6E7FC0F5D547787E969D5596F (1 row) mytest=# select sys_guid() ; sys_guid ---------------------------------- 3FBC1EC7357AC11BA0B06F4D116E051A (1 row) mybatis的SQL表达XML由于openGauss语法对符号 不识别,要把 的特殊符号都去掉 /opt/projects/yami-b2b2c/yami-shop-sys/src/main/resources/mapper/SysMenuMapper.xmlSysMenuMapper.xml 把 `` 的特殊符号都去掉。举例一个增加会员的涉及的数据库相关的修改操作现在在后台管理页面,点击管理员列表,再单击新增, 直接转发请求到后端服务yami-shop-admin,会触发数据库新增数据查看后端服务日志 tailf /opt/projects/yami-b2b2c/yami-shop-admin/target/log/yami-shop-admin-console.log### Error updating database. Cause: org.postgresql.util.PSQLException: ERROR: permission denied for relation tz_sys_user Detail: N/A ### The error may exist in com/yami/shop/sys/dao/SysUserMapper.java (best guess) ### The error may involve com.yami.shop.sys.dao.SysUserMapper.insert-Inline ### The error occurred while setting parameters ### SQL: INSERT INTO tz_sys_user ( username, password, email, mobile, status, shop_id, create_time ) VALUES ( ?, ?, ?, ?, ?, ?, ? ) ### Cause: org.postgresql.util.PSQLException: ERROR: permission denied for relation tz_sys_user Detail: N/A ; bad SQL grammar []; nested exception is org.postgresql.util.PSQLException: ERROR: permission denied for relation tz_sys_user Detail: N/A以上错误主要是由于没有授权,通过超管进入 mytest数据库openGauss=# \c mytest; Non-SSL connection (SSL connection is recommended when requiring high-security) You are now connected to database "mytest" as user "omm". mytest=# GRANT ALL PRIVILEGES ON TABLE tz_sys_user TO henley;继续查看yami-shop-admin-console.log,报错变成ERROR: null value Caused by: org.postgresql.util.PSQLException: ERROR: null value in column "id" violates not-null constraint Detail: Failing row contains (null, 3, 1). at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2553) at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2285) at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:323) at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:481) at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:401) at 以上报错ERROR: null value 原因是自增ID的问题,通过以下方法实现自增ID。create sequence public.tz_sys_user_id start with 2 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_user_id owner to henley; alter table tz_sys_user alter column user_id set default nextval('public.tz_sys_user_id'); **tz_sys_user 表与 tz_sys_user_role表、tz_sys_log表有关联,同样需要以下授权 **GRANT ALL PRIVILEGES ON TABLE tz_sys_user_role TO henley; create sequence public.tz_sys_user_role_id start with 1 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_user_role_id owner to henley; alter table tz_sys_user_role alter column id set default nextval('public.tz_sys_user_role_id');注意tz_sys_log表已经有845条数据,所以新增的数据从846开始算。GRANT ALL PRIVILEGES ON TABLE tz_sys_log TO henley; create sequence public.tz_sys_log_id start with 846 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.tz_sys_log_id owner to henley; alter table tz_sys_log alter column id set default nextval('public.tz_sys_log_id');创建用户成功后 mytest=# select * from tz_sys_log where id = 846;最后总结开源Mall4j商城系统的数据库底座替换,把MySQL改成OpenGauss,对业务逻辑相关的代码侵入不大,主要工作量是数据库表结构方面重构,OpenGauss对微服务的功能是支持的。
  • [技术干货] 【第四届openGauss征文活动参赛作品】干货输出【SpringBoot + OpenGauss3开发入门】
    本文介绍如何快速安装OpenGauss3,OpenGauss3的安装这是笔者浓缩提炼的,并且在SpringBoot中集成使用OpenGauss3数据库。单机版openGauss3快速环境安装groupadd dbgroup useradd -g dbgroup omm # 可后面安装时创建 passwd omm #设置密码为Gauss_1234创建安装程序目标目录mkdir /home/omm/opengauss3 chown -R omm:dbgroup /home/omm/opengauss3下载opengauss3.00mkdir /opengauss3 cd /opengauss3 wget https://opengauss.obs.cn-south-1.myhuaweicloud.com/3.0.0/x86/openGauss-3.0.0-CentOS-64bit-all.tar.gz解压文件tar -zvxf openGauss-3.0.0-CentOS-64bit-all.tar.gz tar zxvf openGauss-3.0.0-CentOS-64bit-cm.tar.gz tar zxvf openGauss-3.0.0-CentOS-64bit-om.tar.gz设置opengauss集群配置文件,这里设单点安装[root@enmoedu1 opengauss3]# cat cluster_config.xml "/> 前置系统软件包yum install -y epel-release yum install -y bzip2 # 安装bzip2用于后面的解压openGauss安装包 sed -i 's/源IP/目标IP/g' cluster_config.xml sed -i 's/hdp1/你的主机名/g' cluster_config.xml 初始化系统安装配置参数,以必须管理员root的权限运行,进入opengauss3运行初始化程序[root@hdp1 ~]# cd /opengauss3/ [root@hdp1 opengauss3]# ./script/gs_preinstall -U omm -G dbgroup -X ./cluster_config.xml Parsing the configuration file. Successfully parsed the configuration file. Installing the tools on the local node. Successfully installed the tools on the local node. Setting host ip env Successfully set host ip env. Are you sure you want to create the user[omm] (yes/no)? no Preparing SSH service. Successfully prepared SSH service. Checking OS software. Successfully check os software. Checking OS version. Successfully checked OS version. Creating cluster's path. Successfully created cluster's path. Set and check OS parameter. Setting OS parameters. Successfully set OS parameters. Warning: Installation environment contains some warning messages. Please get more details by "/opengauss3/script/gs_checkos -i A -h hdp1 --detail". Set and check OS parameter completed. Preparing CRON service. Successfully prepared CRON service. Setting user environmental variables. Successfully set user environmental variables. Setting the dynamic link library. Successfully set the dynamic link library. Setting Core file Successfully set core path. Setting pssh path Successfully set pssh path. Setting Cgroup. Successfully set Cgroup. Set ARM Optimization. No need to set ARM Optimization. Fixing server package owner. Setting finish flag. Successfully set finish flag. Preinstallation succeeded.下面要以omm的用户正式运行安装程序,首先必须把权限赋给ommchown -R omm:dbgroup /opengauss3 切换到 omm,在/opengauss3目录下运行安装目录 [root@hdp1 opengauss3]# su omm [omm@hdp1 opengauss3]$ ./script/gs_install -X ./cluster_config.xml Parsing the configuration file. Check preinstall on every node. Successfully checked preinstall on every node. Creating the backup directory. Successfully created the backup directory. begin deploy.. Installing the cluster. begin prepare Install Cluster.. Checking the installation environment on all nodes. begin install Cluster.. Installing applications on all nodes. Successfully installed APP. begin init Instance.. encrypt cipher and rand files for database. Please enter password for database: Please repeat for database: begin to create CA cert files The sslcert will be generated in /home/omm/opengauss3/install/app/share/sslcert/om NO cm_server instance, no need to create CA for CM. Cluster installation is completed. Configuring. Deleting instances from all nodes. Successfully deleted instances from all nodes. Checking node configuration on all nodes. Initializing instances on all nodes. Updating instance configuration on all nodes. Check consistence of memCheck and coresCheck on database nodes. Configuring pg_hba on all nodes. Configuration is completed. Successfully started cluster. Successfully installed application. end deploy..验证服务进程是否激 活[root@hdp1 ~]# ps -eaf | grep omm root 14898 32160 0 15:55 pts/1 00:00:00 su omm omm 14899 14898 0 15:55 pts/1 00:00:00 bash omm 19411 1 9 16:08 ? 00:00:02 /home/omm/opengauss3/install/app/bin/gaussdb -D /home/omm/opengauss3/install/data/dn root 19784 360 0 16:09 pts/2 00:00:00 grep --color=auto omm命令行登录gsql -d postgres -p 15400 安装opengauss3注意事项之前安装mogdb,影响了opengauss3的环境,/home/omm/.bashrc 里面记录了安装后的变量,如果要卸载opengauss,必须要把.bashrc 下面所有的东西都去掉。# User specific aliases and functions export GPHOME=/home/omm/opengauss3/install/om export PATH=$GPHOME/script/gspylib/pssh/bin:$GPHOME/script:$PATH export LD_LIBRARY_PATH=$GPHOME/lib:$LD_LIBRARY_PATH export PYTHONPATH=$GPHOME/lib export GAUSSHOME=/home/omm/opengauss3/install/app export PATH=$GAUSSHOME/bin:$PATH export LD_LIBRARY_PATH=$GAUSSHOME/lib:$LD_LIBRARY_PATH export S3_CLIENT_CRT_FILE=$GAUSSHOME/lib/client.crt export GAUSS_VERSION=3.0.0 export PGHOST=/home/omm/opengauss3/tmp export GAUSSLOG=/var/log/omm/omm umask 077 export GAUSS_ENV=2 export GS_CLUSTER_NAME=dbClusterspringBoot应用集成OpenGaussSOA是一种粗粒度、松耦合服务架构,服务之间通过简单、精确定义接口进行通讯,不涉及底层编程接口和通讯模型。SOA可以看作是B/S模型、XML(标准通用标记语言的子集)/Web Service技术之后的自然延伸,面向服务架构,它可以根据需求通过网络对松散耦合的粗粒度应用组件进行分布式部署、组合和使用。服务层是SOA的基础,可以直接被应用调用,从而有效控制系统中与软件代理交互的人为依赖性。简而言之SOA可以消除信息孤岛并实现共享业务重用,我们通过SOA可以打造下图的复杂系统,其中蓝色用户服务 ,我们可以通过springboot + OpenGauss 技术实现。我们使用OpenGauss作为具体数据存储,使用开发工具创建一个数据库mysqltest,并在mysqltest数据库中创建一张表userennity和user1,创建语句如下:create table userentity( id int , username varchar(50), password varchar(50), user_sex varchar(10), nick_name varchar(50) ); create table user1( id int , name varchar(50), password varchar(50)); DEMO代码 +---src | +---main | | +---java | | | ---com | | | ---main | | | +---controler 具体业务逻辑 | | | +---mapper 定义实现DAO的CRUD实体操作 | | | +---model 实体类 | | | ---service 实现服务类注意UserControler是首先调用的service,继而去调用实体操作。 而UserEntityControler是通过mapper的封装去调用 DAO的CRUD的操作,如下无论是UserControler还是 UserEntityControler 都需要底层数据库对应用支持友好。 确定opengauss的用户、密码、端口及相关IP启动服务服务正在运行中 查看用户实体1查看用户实体2源代码下载:链接:cid:link_0 提取码:5wjdspringboot集成opengauss的FAQ用户名/密码不对spring报错### The error may involve com.main.mapper.UserMapper.getAll ### The error occurred while executing a query ### Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Failed to obtain JDBC Connection; nested exception is org.postgresql.util.PSQLException: 不明的原因导致驱动程序造成失败,请回报这个例外。] with root cause java.lang.NullPointerException: null而opengauss内部执行报错[omm@enmoedu1 ~]$ gsql -U henley -h 192.168.30.65 -p 15400 Password for user henley: gsql: FATAL: Invalid username/password,login denied.根本原因分析 openGauss默认是sha256,而登录则设成只允许md5登录,所以一直识用户名和密码错误解决方法及步骤 vi /home/omm/opengauss3/install/data/dn/postgresql.conf 修改设置 encryption_type = 1vi /home/omm/opengauss3/install/data/dn/pg_hba.conf增加设置 host all henley 0.0.0.0/0 md5用户没有对表的操作权限spring报错org.postgresql.util.PSQLException: ERROR: permission denied for relation userentity 详细:N/Aopengauss报错mytest=> SELECT id, userName, passWord, user_sex, nick_name FROM userentity; ERROR: permission denied for relation userentity DETAIL: N/A解决方法及步骤以postgres的身份登录root[omm@enmoedu1 ~]$ gsql -d postgres -p 15400 gsql ((openGauss 3.0.0 build 02c14696) compiled at 2022-04-01 18:12:34 commit 0 last mr ) Non-SSL connection (SSL connection is recommended when requiring high-security) Type "help" for help.切换到指定的数据库openGauss=# \c mytest; Non-SSL connection (SSL connection is recommended when requiring high-security) You are now connected to database "mytest" as user "omm".执行授权mytest=# GRANT ALL PRIVILEGES ON userentity TO henley; GRANT授权后能够正常,但是发现一个问题,现在我们是通过Postgresql的jdbc驱动去访问OpenGauss的,OpenGauss没有自己的原生jdbc驱动吗?答案是有的,而且还支持maven方式,见下。 但是笔者的运气很差,通过maven一直无法下载openGauss的core包,只能通过手动的方式下载。wget https://opengauss.obs.cn-south-1.myhuaweicloud.com/ 3.0.0/x86/openGauss-3.0.0-JDBC.tar.gz再在idea把jar包引入进来,引入步骤 Project Structure --> Project Settings --> Libraries --> Add(alt +insert) --> applyapplication.properties稍微修改一下 ==spring.datasource.url=jdbc:opengauss://192.168.30.65:15400/mytest== ==spring.datasource.driver-class-name=org.opengauss.Driver==#spring.datasource.url=jdbc:postgresql://XXXX:5432/mytest #spring.datasource.url=jdbc:postgresql://XXXX:15400/mytest spring.datasource.url=jdbc:opengauss://XXXX:15400/mytest spring.datasource.username=henley spring.datasource.password=XXXX spring.datasource.driver-class-name=org.opengauss.Driver #spring.datasource.driver-class-name=org.postgresql.Driver ### mybatis config ### mybatis.config-locations=classpath:mybatis/mybatis-config.xml mybatis.mapper-locations=classpath:mybatis/mapper/*.xml mybatis.type-aliases-package=com.main.model最后总结openGauss对业界知名的spring支持还算友好,直接用传统的postgresql驱动就可以接入使用,也有自己的opengauss驱动。如果使用顺利,还可以支持分布式配置、服务路由、负载均衡、熔断限流、链路监控这些功能,事实上在微服务的技术框架上也是支持的。
  • 【第四届openGauss征文活动参赛作品】SSM+MySQL替换探索 opengauss3对比postgresql12
    SSM介绍SSM(Spring+SpringMVC+MyBatis)框架集由Spring、MyBatis两个开源框架整合而成(SpringMVC是Spring中的部分内容),常作为数据源较简单的web项目的框架。SpringSpring就像是整个项目中装配bean的大工厂,在配置文件中可以指定使用特定的参数去调用实体类的构造方法来实例化对象。也可以称之为项目中的粘合剂。 Spring的核心思想是IoC(控制反转),即不再需要程序员去显式地new一个对象,而是让Spring框架帮你来完成这一切。SpringMVCSpringMVC在项目中拦截用户请求,它的核心Servlet即DispatcherServlet承担中介或是前台这样的职责,将用户请求通过HandlerMapping去匹配Controller,Controller就是具体对应请求所执行的操作。SpringMVC相当于SSH框架中struts。mybatismybatis是对jdbc的封装,它让数据库底层操作变的透明。mybatis的操作都是围绕一个sqlSessionFactory实例展开的。mybatis通过配置文件关联到各实体类的Mapper文件,Mapper文件中配置了每个类对数据库所需进行的sql语句映射。在每次与数据库交互时,通过sqlSessionFactory拿到一个sqlSession,再执行sql命令。 页面发送请求给控制器,控制器调用业务层处理逻辑,逻辑层向持久层发送请求,持久层与数据库交互,后将结果返回给业务层,业务层将处理逻辑发送给控制器,控制器再调用视图展现数据。SSM搭配的数据库无庸置疑,SSM经常搭配的数据库是MySQL或者是Postgresql,而国内使用MySQL的人比 Postgresql的人多, 所以本文主要内容关于SSM DEMO已经搭配用上MySQL,系统本身可以注册写入数据入库,可以从库中读取数据进行登录。SSM demo代码经过测试对比,SSM DEMO代码基于三个不同的数据库,除了JDBC连接串不同,如下。另外最大的不同就是数据库的ID自增机制不同,ID自增机制是数据库的一项基本功能,我们非常重视这一点,这个不同会不会导致DEMO代码要做相关的适配,好听叫做适配,不好听叫做业务侵入。我们现在开发的 SSM DEMO代码是在MySQL的基础上开发,优先满足了MySQL。Postgresql如果把MySQL改换成Postgresql ,运行程序的会报错,如下org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.jdbc.UncategorizedSQLException: ### Error updating database. Cause: org.postgresql.util.PSQLException: Returning autogenerated keys is only supported for 8.2 and later servers. ### SQL: insert into user1 (username, password, age) values (?, ?, ?) ### Cause: org.postgresql.util.PSQLException: Returning autogenerated keys is only supported for 8.2 and later servers.笔者用的是postgresql12,经过网上查阅 ,mybatis的定义配置必须要更改。 ==useGeneratedKeys="true" keyProperty="id"摘掉==改换成 重新在idea运行tomcat9如下,发现数据能够写入了,但是写入的数据没有id列为空值。mytest=# select * from user1; id | username | password | age ----+-----------+-----------+----- 1 | user1 | password1 | 18 | hexin.xue | cxc | 25这是由于postgresql没有自动增加id机制的功能,所以数值一直为空。 如果要实现自增ID,它是通过sequence去实现增加ID的。两个方法是给相关表增加sequence,一种是已建表的基础上增加sequence实现增加IDcreate sequence public.userid_seq start with 1 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.userid_seq owner to henley; alter table user1 alter column id set default nextval('public.userid_seq'); insert into user1 (username, password, age) values ('username1', 'password1', 100);另外一种重建表,建表就实现CREATE SEQUENCE sq_user_id START 1 INCREMENT 1 CACHE 20; create table user1(id int NOT NULL DEFAULT nextval('sq_user_id') , username varchar(50),password varchar(50),age int);建表后如果还有问题 ,下面再补刀alter sequence public.sq_user_id owner to henley; alter table user1 alter column id set default nextval('public.sq_user_id');增加sequence后,写入数据后ID例有值了。OpenGauss依然是同样代码,把jdbc的连接串改成OpenGauss, 我们启动tomcat,加载服务,我们惊喜的发现没有报错。 此处在postgresql12需要更改,在opengauss3不需要任何更改,spring指向的应用层没有报错。但是数据库底层ID列的数据仍然空值,OpenGauss也和Postgresql一样,都是用sequence 去实现ID的自增长。create sequence public.userid_seq start with 1 increment by 1 no minvalue no maxvalue cache 1; alter sequence public.userid_seq owner to henley; alter table user1 alter column id set default nextval('public.userid_seq');**体验源代码: **链接:cid:link_0 提取码:0kwg最后总结这是SSM开发中比较低端的DEMO代码,但是从中可见openGauss用心的包容,在接口层适配了mybatis,遇到自增ID提高了容错性,相对于postgresql多了一道方便。
  • [技术干货] 【第四届openGauss征文活动参赛作品】基于openGauss的五子棋AI项目
    1 前言openGauss是一款全面友好开放的企业级开源关系型数据库。openGauss采用木兰宽松许可证v2发行,提供面向多核架构的极致性能、全链路的业务、数据安全、基于AI的调优和高效运维的能力。本文采用openGauss设计一个AI小demo。2 方案意义人工智能被广泛用于棋类对弈的主要原因是:棋类对弈自古以来就被认为是人类智力活动的象征,若人工智能成功达到、甚至高于人类水平,则就代表AI的发展潜力,从而吸引更多研究者关注并投身其中; 棋类很适合作为新AI算法的标杆。棋类游戏规则简洁、输赢都在盘面,适合计算机求解。理论上只要在计算能力和算法上有新的突破,任何新的棋类游戏都有可能得到攻克。而在棋类游戏上的表现也可以直观体现出AI之间计算能力与算法的高低,是促进AI算法发展的有效途径。 就本五子棋智能对弈系统而言,其服务对象为同局域网下的多个终端,讲求联机互动、互相限制、互相博弈,打破了传统二人对弈五子棋规则中,“若无禁手,先手易胜;若为后手,十堵九输”的说法,是AI算法设计、网络通信、数据库等技术的综合应用。3 架构设计其中对弈数据库采用openGauss数据库4 数据表设计棋盘数据表的表头如图所示为将二维棋盘状态存入数据库中,令棋盘按 轴方向展开为一维序列玩家数据表的表头如图所示走子数据表的表头如图所示5 代码结构如图所示,依据模块化程序设计的基本思想,将整个项目按照功能划分为若干个小程序模块,每个小程序模块完成一个确定的功能,自顶向下、逐步分解、分而治之,各模块相对独立、功能单一、结构清晰。同时,在这些模块之间建立必要的联系,通过模块的互相协作完成整个功能的程序设计各模块具体设计如下:main.py:启动文件。config.py:变量管理文件。管理所有跨文件的全局变量。settings.py:配置文件。开发环境配置、第三方扩展插件参数配置、数据库的链接路径及其他配置等。apps:主体文件包。其初始化文件包括Flask类的实例创建以及工厂函数。该文件包下设三个子文件包,分别对应架构设计中的三张蓝图。exts:扩展文件包。第三方扩展插件的例化,创建映射对象等。 项目采用高度模块化设计的优点有:控制了程序设计的复杂性;提高了代码的重用性;易于维护和功能扩充;有利于团队开发等。6 项目演示7 总结基于openguass开发AI应用相当方便,也使我进一步掌握了数据库的常用技术,提高工程开发能力和面对未知问题的临场解决能力,对设计、创新、分析水平都有很大的帮助
  • [问题求助] cms报表算法及其理解求助
    问题来源】【必填】    海南农信【问题简要】【必填】     1、计算技能下面的等待时长(计算逻辑(排队+应答时长)/等待次数)对应在t_cms_callin_skill_day 表中哪些对应指标     2、t_cms_callin_skill_day 表中的succ_queue_time是不是指的是技能在队列中排队的时长,不包含应答时长吧【问题类别】【必填】   AICC cms 8.15.1【AICC解决方案版本】【必填】   AICC 8.15.1ICD V300R6c90u3spc700【期望解决时间】【选填】     尽快解决
  • [技术干货] 如何干涉MySQL优化器使用hash join?
    数据库的优化器相当于人类的大脑,大部分时候都能做出正确的决策,制定正确的执行计划,走出一条高效的路,但是它毕竟是基于某些固定的规则、算法来做的判断,有时候并没有我们人脑思维灵活,当我们确定优化器选择执行计划错误时该怎么办呢,语句上加hint,提示它选择哪条路是一种常见的优化方法。前言数据库的优化器相当于人类的大脑,大部分时候都能做出正确的决策,制定正确的执行计划,走出一条高效的路,但是它毕竟是基于某些固定的规则、算法来做的判断,有时候并没有我们人脑思维灵活,当我们确定优化器选择执行计划错误时该怎么办呢,语句上加hint,提示它选择哪条路是一种常见的优化方法。我们知道Oracle提供了比较灵活的hint提示来指示优化器在多表连接时选择哪种表连接方式,比如use_nl,no_use_nl控制是否使用Nest Loop Join,use_hash,no_use_hash控制是否使用hash join。但是MySQL长期以来只有一种表连接方式,那就是Nest Loop Join,直到MySQL8.0.18版本才出现了hash join, 所以MySQL在控制表连接方式上没有提供那么多丰富的hint给我们使用,hash_join与no_hash_join的hint只是惊鸿一瞥,只在8.0.18版本存在,8.0.19及后面的版本又将这个hint给废弃了,那如果我们想让两个表做hash join该怎么办呢?实验我们来以MySQL8.0.25的单机环境做一个实验。建两个表,分别插入10000行数据,使用主键做这两个表的关联查询。create table t1(id int primary key,c1 int,c2 int);create table t2(id int primary key,c1 int,c2 int);delimiter //CREATE PROCEDURE p_test()BEGINdeclare i int;set i=1;while i<10001 doinsert into t1 values(i,i,i);insert into t2 values(i,i,i);SET i = i + 1;end while;END;//delimiter ;查询一下两表使用主键字段关联查询时实际的执行计划,如下图所示:查询一下两表使用非索引字段关联查询时实际的执行计划,如下图所示:从执行计划可以看出,被驱动表的关联字段上有索引,优化器在选择表连接方式时会倾向于选择Nest Loop Join,当没有可用索引时倾向于选择hash join。基于这一点那我们可以使用no_index提示来禁止语句使用关联字段的索引。从上面的执行计划可以看出使用no_index提示后,优化器选择了使用hash join。当索引的选择性不好时,优化器选择使用索引做Nest Loop Join是效率是很低的。我们将实验的两个表中c1列的数据做一下更改,使其选择性变差,并在c1列上建普通索引。update t1 set c1=1 where id<5000;update t2 set c1=1 where id<5000;create index idx_t1 on t1(c1);create index idx_t2 on t2(c1);当我们执行sql :select t1.*,t2.* from t1 join t2 on t1.c1=t2.c1;这个查询结果会返回大量数据,被驱动表的关联字段c1列的索引选择性差,此时选择hash join是更明智的选择,但是优化器会选择走Nest Loop Join。我们可以通过实验验证一下hash join 与 Nest Loop Join的性能差异。可以看出使用hash join的耗时是使用Nest Loop Join的1/6,但是优化器根据成本估算时,使用Nest Loop Join的成本要比使用hash join的成本低很多,所以会去选择Nest Loop Join,这个时候就需要加上hint 提示禁止使用关联字段的索引,被驱动表上每次都全表扫描的代价是很高的,这样优化器估算后就会选择走hash join。MySQL官方文档里提到用BNL,NO_BNL的hint提示来影响hash join的优化,但是经过实验证明,在表连接关联字段上没有可用索引时,优化器估算成本后不会对被驱动表使用BNL全表扫描的方式做嵌套循环连接,而是会选择使用hash join,那这样NO_BNL在这个场景下就没有用武之地了。那么既然不用这个索引,把这个索引去掉不就可以了吗?为什么非要使用no_index的hint提示呢,我们要知道业务使用的场景何其多,此处不用,别处使用了这个索引效率可能会有大的提升啊,这个时候就凸显了hint的优势,只需要控制此语句的使用就好了。总结Nest Loop Join有其优势,它是response最快的连接方式,适用于返回数据量小的场景。当两个大表连接,返回大量数据,且关联字段的索引比较低效时,使用hash join就会比较高效,我们可以使用no_index的hint提示禁用关联字段的低效索引,促使优化器选择hash join。来源: GreatSQL社区
  • [技术干货] 解读《分布式数据库发展趋势研究报告》
    近期,由国家工业信息安全发展研究中心发布了2022年《分布式数据库发展趋势研究报告》。报告从数据库产业发展、分布式数据库产品价值、面临调整、技术路线、发展趋势、发展方向等多角度阐述了分布式数据库的诸多问题。本文,从个人角度谈谈对上述研究报告的解读。1、背景篇:不鸣则已、一鸣惊人伴随着数字化转型深化,企业对于数据的重视程度逐步加深。伴随着以云计算、5G、IOT、人工智能、区块链等新兴技术的发展,催着更多新兴数据场景的出现。这其中直观带来的就是数据规模呈现几何级增长、数据结构复杂度与日攀升。根据第三方机构的预测数据,全球数据存储量在未来几年将呈现爆炸式增长。除去数据规模外,在从数据采集、存储、传输、展现、分析和优化等方面都对数据的载体数据库提出了更高的要求。希望以此更好地实现企业对数据资产治理、增值与科学决策,数据高可靠高可用、数据在线分析等诉求,进一步发挥数据价值。上述诉求可以说为数据库提出了更高的要求,但传统数据库架构在超大规模、高并发、实时处理、数据安全等方面明显力不从心,此外高昂建设成本也难以迎合数字化时代的潮流趋势。相较于传统数据库,新兴分布式数据库的优势都凸显出来。突破规模化存储能力传统单机或集中式架构下,承载的数据规模受限于本地磁盘或可对接的外部存储空间。虽然后者可做到大规模(如PB级),但其建设周期长、扩展不灵活、投入成本高且依然还会面临IO的性能瓶颈。而分布式架构数据库,天然具有的数据分片能力,是有效解决超大规模数据承载的利器。突破高性能计算瓶颈作为承载计算的主要资源,CPU、内存资源对计算尤为重要。在传统单机或集中式架构下,上述资源仅能通过Scale UP方式进行扩展,其扩展能力有限,无法整合更多资源参与计算。而分布式架构数据库,通过网络可汇聚更多计算资源参与其中,形成更大规模的算力支持。在高并发、高性能计算领域更有优势。填补数据分析能力短板数据分析,是数据使用的重要方向之一,过去通常是由数据仓库等技术承载。这种在线数据库与离线数据仓库的架构,能在一定程度上解决数据分析问题,但在实时性、一致性、成本等方面存在短板。理想的方式在单一系统完成,但传统架构受限于资源,无法完全提供。而分布式架构数据库,通过其算力的整合可满足混合负载的业务压力,大幅度提升分析时效性,并减少数据冗余,灵活性大大提高。提升可用性与安全性传统架构下,数据库可用性更多取决于单点(或存储),设计上往往通过冗余硬件保护等方式去提升;但受限于架构约束难以达到非常高的可用性。针对数据安全,也通常是通过主备复制、备份等手段完成,但难以保证数据在线安全,需要窗口期完成恢复。分布式架构具备的存算分离、多副本、弹性扩展等能力,可有效提升整体可用性和数据安全。用户可根据需要,灵活调整架构,提升可用性和安全性。优化成本模型实现按需扩展传统架构数据库的成本相对高昂,这主要是由于其架构限制,向上扩展相对容易,水平扩展比较困难。为了保证快速的业务发展,通常在项目设计初期就需要按最大容量进行规划或为了更高的可靠性需要付出高额的成本。而分布式架构数据库则不同,其架构天然就支持灵活扩展能力(包括存储、计算),并可实现低成本的高可用解决方案(多副本)。上述能力,将有效降低企业在构建系统时的投入,特别是在面对快速、多变业务的场景更是如此。2、技术篇:百花齐放、百家争鸣从上图可见,分布式数据库已经发展多年,特别是近些年来已经逐步成熟并落地使用。从大的技术路线来看,可大致分为几种类型:路线:分布式中间件+单机数据库这一技术路线是在单机数据库系统上进行改造,主要解决计算存储的扩展性问题。上层为一组无状态计算节点,基于分片规则提供SQL解析,请求转发和结果合并的能力。下层为增强的单机数据库,提供单机数据库的存储和执行能力。这一架构通过数据在逻辑层的切割,可近似线性地对计算性能和存储容量进行扩展,具有可规模化扩展的能力。路线:分布式存储构建这一技术路线是通过构建分布式共享存储实现扩展,采用非对称计算节点,大部分公有云数据库是这条路线。这条路线有限地解决扩展性问题,跨地域数据一致性主要依赖分布式存储引擎。共享存储能够跨多个节点提供读写,上层的计算部分是无状态的一组节点组成。当有写能力的计算节点出现故障时,会自动从可用的读节点中自动选出一个作为写节点,实现写能力的高可用。路线:原生分布式这一技术路线是原生分布式数据库,各计算节点提供对等的读写服务。这条路线是根据分布式一致性协议做底层设计,与传统数据库有本质区别。原生分布式数据库将分布式存储、事务、计算有机的结合在一起,数据由系统自动打散并存储多个副本,通过一致性协议保证多个副本和事务日志的一致性,对分布式事务、全局MVCC等支持更为彻底。整个分布式结构是包裹在集群内部的,应用对此无感知。3、 趋势篇:石以砥焉、化钝为利随着分布式架构数据库在众多场景使用,再享受到其带来的收益之外,也对这一新架构产品提出了更多的挑战。这些挑战也为未来分布式数据库发展指明了方向。(1)融合化原生设计如上面谈到的分布式数据库存在不同路线,不同路线产品差异明显。从长期发展来看,不同路线产品呈现逐步融合的现象,各家各取所长,不断丰富产品能力。一方面相较于单机或集中式架构,分布式架构产品仍然存在诸多短板,可理解为基础能力补齐;另一方面用户对于分布式能力也提出了更高的要求,可理解为扩展能力增强。基础能力补齐​在分布式架构下,相较于单机或集中式架构,仍存在大量短板问题。这些会直接影响到用户的使用体验。如分布式事务的一致性保证问题,对于单机较容易实现的ACID,分布式环境中出现了更多的难题。分布式数据库将需要处理的事务进行拆分,再部署到不同的服务器上进行处理,理想状况下,整个过程需要全局一致性协议的保护,而分库分表两阶段的方式在一些意外情况下容易出现问题。再比如,分布式架构多采用存算分离架构,其天然会带来分层间网络开销问题,如何解决低延迟需求值得考虑。扩展能力增强​随着分布式数据库的使用,如何使用好这一架构成为核心。例如在分布式场景下,如何做好数据分片的智能化。分布式通过分库分表进行数据拆分,从而各表的数据量保持在阈值以下,从而应对高并发和海量数据,但如何高效的、高质量的进行分片,还需再探索。此外,作为以一种新架构产品,分布式数据库所具备的弹性扩缩容、按需扩展、海量支持、多副本细粒度控制等课题,都是值得深入挖掘。(2)负载一体化设计企业级应用的业务场景通常可以分为联机交易和实时分析两种,通常称为OLTP和OLAP的业务应用。由于是不同的应用场景,很多企业往往会选择多款数据库产品分别支持。这种组合式的解决方案要求数据在不同产品间进行流转,数据的同步过程就带来了时间延迟和数据不一致的风险,而且还会产生冗余数据,成本开销被迫提高,这在一定程度上限制了企业的发展。分布式数据库的出现,为企业解决上述问题带来了契机,这也是近年来HTAP(混合负载)的兴起,其旨在打破事务处理和分析之间“壁垒”。未来分布式数据库都应具备混合负载能力,即在支持高并发、事务性请求的同时,也对分析型的复杂查询提供了良好的支持,实现计算、I/O资源互不干扰。通过在线交易和分析互不影响,一站式地解决企业级应用的各种需求,从而大幅度降低成本,同时提高了企业决策的效率。(3)云与云原生设计根据全球知名咨询公司Gartner指出,“到2022年,75%的数据库将被部署或迁移到云平台…”云化无疑代表了未来。数据库作为IT基础设施,如何与云环境融合成为大家的问题。特别是分布式数据库,其架构需要大量资源构建。如何通过与云的结合,有效解决灵活部署、弹性扩缩容、资源管理乃至如何更好地利用云基础资源做到真正的云原生,这些都是分布式数据库需要考虑的。因此在分布式数据库产品设计层面就要充分适配云环境、兼容更多云技术,从而添加更多资源管控、多部署形态、云原生资源利用等云化方面的能力。(4)高可用一致性设计作为数据库的基本能力要求,服务高可用和数据一致性一直是企业选择数据库的重点考察要求。特别是随着数字化转型,更多数据参与到企业业务流转中,这些都对如可用性提出更好要求。传统数据库架构,在满足7×24小时的服务不中断和数据零丢失方面往往已经超出其可承受能力,或者即使能解决其成本也十分高昂。分布式数据库具备的分层、多组件、多节点架构成为解决高可用的基础,有效控制故障范围、主动发现自愈等手段可大幅提升服务可用性。同时,其多副本机制为数据一致性安全提供可能,这也是相较于传统架构的突破,可做到数据更为精细粒度的一致性,满足各种数据场景下对一致性的不同等级要求。(5)软硬结合异构设计硬件和软件是信息系统的核心组件,两者之间相辅相成,互相促进。新型硬件的出现,可以为数据库发展带来更多的收益。一方面以基础硬件如多核CPU、异构计算(如GPU、FPGA)持久化内存、高速网络为代表的硬件出现,为分布式数据库架构提供更多的想象空间;另一方面新型硬件也会为数据库设计带来更多挑战,如何利用好新硬件值得各数据库厂商思考,甚至会颠覆之前的设计模式。此外,作为关键基础设施,数据库还要为操作系统、芯片的灰度替换提供支持,而在关键行业及软件领域,数据库还需要提供对异构芯片的支持,从而提升数字化解决方案的严谨性,降低应用风险。(6)全密态安全设计当前信息安全已经上升到国家战略高度,诸多行业监管机构和政府部门对数据存储和使用都有明确的安全合规性要求。2021年11月公布的《中华人民共和国个人信息保护法》,监管部门已在金融等行业中推广数据加密,要求敏感数据采用加密的方式进行存储。而作为数据承载主体的数据库,有义务为此提供坚实的数据安全保障。那么在分布式数据库设计之初,就需考虑在数据传输、数据存储、数据计算等多方面的安全问题。诸如透明数据加密、透明数据传输、多密钥管理、国密算法支持、密态计算等方面,都需要考虑。(7)低成本集约化设计分布式数据库,作为一种新架构产品,对于企业来说会带来不小成本。从管理角度来看,分布式架构对运维人员都带来新的要求,如何管理好成为要点。产品是否提供完整的管理能力、是否提供完备的生态工具等,将直接影响最终使用成果。从资源角度来看,分布式架构需要一定资源投入,如何规划设计好并通过诸如租户能力有效降低使用成本很重要。(8)高兼容易迁移设计对于底层数据库替换,最为头疼的就是数据库的替换。大部分企业经过企业信息化的长期积累与革新,在内部积累了大量的业务系统。传统的企业级数据库产品提供了强大的能力,协助开发者快速便捷地构建应用程序,但同时也导致应用设计过度依赖数据库功能。适配新的数据库产品必须对应用代码进行大量修改。没有两个数据库是完全一样的,分布式数据库更是如此。其在底层架构、实现逻辑上必然存在差异。比较好的方式,就是提供高兼容能力,这将有利于大幅降低代码改造成本。目前大多数分布式数据库还不完全具备主流数据库生态的兼容能力,兼容的种类还不够丰富,兼容度还有待提高。此外,分布式架构对于设计上也有着特殊的要求,如何降低研发设计成本,近似透明地屏蔽这一差异很重要。此外,数据从传统集中式数据库迁移至分布式数据库是一项复杂且庞大的工程。从前期兼容评估、应用设计改造,到中期的业务测试、性能测试,指导最终迁移完成并保证迁移准确性等,这些都需要提供全流程的支持。这也是目前分布式产品普遍有所缺失的。希望未来分布式数据库产品将具备全方位、高标准、高可靠性的平滑迁移能力。4、发展篇:知之非艰、行之惟艰分布式数据库作为新技术架构,如何推进是需要多方位的支持。从近些年来看,从国家、行业、用户等多方位都给予大力支持。从政策层面,将在作为数据基础设施之一的数据库提升到一定高度,重点布局针对数据库分布式转型、应用创新战略。在行业方面,越来越多的数据库厂商加入进来,特别是以分布式为特征的产品已然成为主流。在用户方面,以金融、电信为代表的高数据价值企业,已经开始在核心生产系统逐步使用。但同时我们也看到,分布式数据库在推进中,仍然存在诸多不足之处,这也是未来需重点关注发展之处。(1)培育自有生态数据库要想用好,是需要从“产、学、研、用”多角度考虑,是需要跟上下游生态形成合力,才能为客户提供更为完整的服务。相较于之前国外商业数据库或开源产品,国内数据库生态还需要加大投入,培育自有生态。在这其中,可以通过生态兼容加速这一过程,如何有效利用之前成熟生态值得考虑。此外,开源作为一种有效的生态构建手段,也是生态化建设的利器。(2)共建行业标准作为一种新型数据库,分布式架构尚未形成统一的行业标准或者事实标准。从最终用户角度来看,不得不面对纷繁复杂的产品细节,这也阻碍了分布式数据库的大范围推广。从行业整体发展角度来看,一方面可通过行业指导单位牵头,由行业内众多企业参与形成标准规范;一方面可通过构建标准化评测体系,建立可衡量标准。希望通过标准的建立,能探索出特色发展路径,抓住机遇尽早实现分布式数据库领域的换道超车。(3)树立专有评测分布式数据库,作为新产品有其架构特色。对于这一新产品的理解,各家各有不同。行业内急需统一的评测标准,从多维度评估这一新类型产品。这其中既包括传统数据库的基本能力,也需要包含分布式自有特点,如在高可用、备份恢复等。逐步建立其以功能测试、非功能测试及场景化测试相结合,形成完备的评测体系。(4)填补最后路径企业更换底层数据库,是一个颇为痛苦的过程,更换为一种全新架构产品更是如此。大量用户使用分布式架构的担忧是来自于对新架构、新产品的未知及对实施路径的陌生。前者我们可通过专有评测标准逐步熟悉,后者则需要通过最后路径的填补做好“最后一公里”。从选型评估、工作量评估、结构数据迁移、流量切换、上线保障等多角度,形成标准的实施路径将大大加速这一过程。写在最后:分布式数据库,尚处于发展早期,但已呈现蓬勃之势。虽然仍有很多不足,但发展空间巨大。这里送给分布式数据库从业者一句话:道阻且长,行则将至;行而不辍,未来可期!作者介绍韩锋,51CTO社区编辑,CCIA(中国计算机协会)常务理事,前Oracle ACE,腾讯TVP,阿里云MVP,dbaplus等多家社群创始人或专家团成员。有着丰富的一线数据库架构、软件研发、产品设计、团队管理经验。曾担任多家公司首席DBA、数据库架构师等职。在云、电商、金融、互联网等行业均有涉猎,精通多种关系型数据库,对NoSQL及大数据相关技术也有涉足,实践经验丰富。曾著有数据库相关著作《SQL优化最佳实践》、《数据库高效优化》。来源:51CTO博客
  • [热门活动] 【我和openGauss的故事】第四届openGauss技术文章征集活动来啦!
    亲爱的伙伴们,Gauss松鼠会联合openGauss社区、鲲鹏社区、墨天轮共同共同举办【我和openGauss的故事】第四届openGauss技术文章征集活动踏秋而来~各位爱技术、爱思考、爱总结的小伙伴,你们施展才华的舞台已经搭建好~期待你的投稿作品!参与方式投稿:9月10日至10月15日,完成下面2步即投稿完成。在墨天轮社区或openGauss社区提交技术文章,并将链接发给Gauss松鼠会小助手(Gauss_Asst666)二维码:墨天轮社区:(cid:link_0),提交时需带有opengauss的标签。openGauss社区:https://opengauss.org   在墨天轮社区“我和openGauss的故事”有奖征文活动,将您发布的文章标题及链接复制黏贴到本活动宣传贴的评论区。参与评优:初审合格的文章将会同步发布在鲲鹏论坛-Database专区,我们会根据墨天轮社区&鲲鹏论坛的总点赞量进行片名,请关注文章的点赞量。活动推广:您还可以推荐好友参与投稿,每邀请2位好友完成openGauss社区投稿,可以获得“活动推广奖”。活动日程活动规则1、投稿:内容要求为openGauss相关技术文章,包含但不限于以下内容:系统技术解析、案例分享、实践总结、开发心得、客户案例、故障调试、测试比对、使用技巧、学习笔记等。文章要求原创且在墨天轮社区或openGauss社区首发,并且在墨天轮社区发布时需要加“openGauss”标签,在openGauss社区发布时需要加“openGauss技术文章征集”标签。若投稿文章为复制或抄袭他人文章,均视为无效。每篇文章要求不少于500字(可含代码),图文并茂,排版工整。2、初审:作者在投稿后,专家组将进行初审,通过初审的文章将参与评优活动;未通过初审的文章,专家组将给出修改建议,修改后可再次提交报名。3、评优通过初审的文章,将发布在鲲鹏论坛-DataBase专区,按墨天轮社区、鲲鹏论坛社区点赞的总赞数进行排名并给予奖励。奖品设置墨天轮投稿奖:根据文章价值和借鉴意义给与50~100元的激励。openGauss社区投稿奖:凡是在openGauss社区blog仓投稿并成功合入的,可以获得京东购物卡50/Gauss松鼠会保温杯/筋膜枪。​活动推广奖:(20个)每邀请2位好友参与活动,完成openGauss社区投稿,即可获得一份“活动推广奖”,多邀多得,数量有限,先到先得。优秀奖:通过初审的文章,将发布在鲲鹏论坛-DataBase专区,按墨天轮社区、鲲鹏论坛社区点赞的总赞数进行排名并给予奖励。其他奖项:在墨天轮社区发布的文章,同时还可以参加墨力计划,详细内容请参见:cid:link_2注:如参与者投稿多篇作品,仅按文章最好成绩参与优秀奖排名。墨天轮投稿奖,openGauss社区投稿奖,活动推广奖,优秀奖相互独立,每位参与活动者获奖可叠加。openGauss社区投稿奖、推广奖和优秀奖获奖者需在鲲鹏社区完成实名认证后方可领取奖品。优秀奖得主需在openGauss社区同步发布获奖文章后方可领取奖品。奖品种类数量有限,先到先得。奖励发放墨天轮投稿奖每满20篇,将在墨天轮编辑部账号上及时公布获奖名单,大家请多多关注!中奖用户可以联络墨天轮小助手(微信:modb666)领取墨天轮投稿奖。openGauss社区投稿奖、推广奖与优秀奖将于10月16日整理后发布,届时请联络Gauss松鼠会小助手领取。
总条数:553 到第
上滑加载中