业务系统从Oracle迁移到openGauss数据库的简单记录

文章目录

  • (一)为啥要动数据库
    • (1.1)关于openGauss
    • (1.2)数据库对比和迁移
  • (二)学习并安装使用openGauss
    • (2.1)官网文档资料
    • (2.2)下载安装包并安装
      • (2.2.1)准备安装
      • (2.2.2)准备环境
      • (2.2.3)安装
    • (2.3)客户端和连接数据库
      • (2.3.1)使用gsql命令行连接
      • (2.3.2)Navicat
      • (2.3.3)Data Studio
      • (2.3.3)Toad Edge
  • (三)业务数据库结构和元数据迁移
    • (3.1)用工具迁移数据
    • (3.2)注意建表和导数语法
    • (3.3)表和字段的注释写法
    • (3.4)编码导致长度差异
  • (四)码农的工作(monkey is coding)
    • (4.1)Java
      • (4.1.1)PostgreSQL JDBC Driver
      • (4.1.2)OpenGauss JDBC Driver
      • (4.1.3)MySQL JDBC Driver
      • (4.1.4)SQL Loader & COPY FROM STDIN & ...
        • >>Oracle
        • >>PostgreSQL(openGauss)
        • >>MySQL
    • (4.2)C & C++
      • (4.2.1)头文件
      • (4.2.2)PostgreSQL(libpq)
      • (4.2.3)MySQL
      • (4.2.4)客户端环境(Ubuntu)
    • (4.3)Object Pascal
    • (4.4)大小写问题
    • (4.5)特有语法与高亮
  • (五)未完待续

(一)为啥要动数据库

由于世界风云变幻,无论是国家、客户、还是公司老板。
都希望数据库能够自主可控,不是米国的,自研的,最好还是开源的。
所以用了20年的Oracle数据库也准备更换了(省略吐槽,不考虑工作量?)……

(1.1)关于openGauss

在这里插入图片描述
官网

源于PostgreSQL-XC,由华为主导、各个生态合作伙伴共同建设的。开源发行协议遵从木兰宽松许可证v2。
目前版本3.0.0,介绍分为【企业版】和【轻量版】,不过这次我们用到的却是【极简版】……
周边工具和生态如下(省略吐槽,Spark咋办?):
业务系统从Oracle迁移到openGauss数据库的简单记录_第1张图片

(1.2)数据库对比和迁移

在这里插入图片描述
下面是这个网站关于openGauss和目前最流行关系型数据库的对比(可以自己加上SQL Server 和 PostgreSQL)
MySQL vs. OpenGauss vs. Oracle

因为工作都是用的Oracle(少量MySQL,SQL Server)。
完全没有接触过openGauss(甚至没用过PostgreSQL)也没时间慢慢研究了,
所以大概拟定步骤如下:

  • 1. 学习并安装使用openGauss数据库。
  • 2. 业务数据库结构和元数据的迁移。
  • 3. 涉及数据库软件(Java)的开发。
  • 4. 涉及数据库软件(Delphi/C++)的开发。
  • 5. 涉及数据库软件(Go/Python)的开发。

(二)学习并安装使用openGauss

(2.1)官网文档资料

为啥以前连PostgreSQL都没用(省略吐槽)……
对比Oracle多了非常多的数据类型。
官网好些文档并没有随着版本更新。
但详细的步骤和各种资料都是中文的!

  • https://blog.csdn.net/GaussDB
  • https://www.modb.pro/openGauss
  • bilibili

(2.2)下载安装包并安装

(2.2.1)准备安装

  • 获取安装包:openGauss-3.0.0-CentOS-64bit.tar.bz2 (极简版)
  • 解压到目录:tar -zxf /xxxx/openGauss-Lite-3.0.0-CentOS-x86_64.tar.gz -C ~/openGauss
  • 检查目录结构:ll ~/openGauss/
总用量 16
drwxr-xr-x. 3 Shion Shion 4096 41 18:24 bin
drwx------. 3 Shion Shion   25 56 10:18 data
drwxr-xr-x. 3 Shion Shion   22 41 18:24 etc
drwxr-xr-x. 3 Shion Shion   24 41 18:24 include
drwxr-xr-x. 4 Shion Shion   95 41 18:24 jre
drwxr-xr-x. 5 Shion Shion 4096 41 18:24 lib
drwx------. 2 Shion Shion  126 56 10:18 logs
drwxr-xr-x. 5 Shion Shion   53 41 18:24 share
drwxr-xr-x. 2 Shion Shion   94 56 10:19 simpleInstall
-rw-r--r--. 1 Shion Shion   32 41 18:24 version.cfg

(2.2.2)准备环境

官网都有,不详细写了,CentOS7:

  • 依赖检查一遍:
  • libaio-devel 建议版本:0.3.109-13
  • flex 要求版本:2.5.31 以上
  • bison 建议版本:2.7-4
  • ncurses-devel 建议版本:5.9-13.20130511
  • glibc-devel 建议版本:2.17-111
  • patch 建议版本:2.7.1-10
  • redhat-lsb-core 建议版本:4.1
  • readline-devel 建议版本:7.0-13
    ——
  • 关闭防火墙
  • 设置字符集参数
  • 设置时区和时间
  • 关闭swap交换内存(可选)
  • 关闭RemoveIPC

(2.2.3)安装

官方的例子是【极简版】不是【轻量版】,那么就按教程来吧。
也不准备弄主备,快点搭建好才行。

simpleInstall目录。
$ sh install.sh -w "你的初始用户密码"

......
Would you like to create a demo database (yes/no)? yes   #这里不yes也行,不生成样例数据库而已。
Load demoDB [school,finance] success. 
...... 
[complete successfully]: You can start or stop the database server using:   
gs_ctl start|stop|restart -D $GAUSSHOME/data/single_node -Z single_node
......

检查:
$ ps -ef |grep -v grep |grep gaussdb
$ gs_ctl query -D ~/openGauss/data/single_node

安装时应该会报错,官网有提到问题和解决方式。

完成后会修改一些环境变量,
但我这安装后它改的.bashrc里面limit -n 1000000会报错。

(2.3)客户端和连接数据库

数据库安装完成后,默认生成名称为postgres的数据库。

(2.3.1)使用gsql命令行连接

$ gsql -d postgres (-p 5432)

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=#

(2.3.2)Navicat

平时可没法全用命令行,得有个正常的图形界面管理工具。
Oracle都是用的Toad……这次准备试试Navicat。

但新安装好的openGauss默认不能从别的主机连接数据库,需要:

>>配置pg_hba.conf允许主机连接:

host    all             all             0.0.0.0/0                md5   #使任何地址均可使用md5加密认证方式

>>配置postgresql.conf监听/绑定地址,以及密码加密方式:

listen_addresses = '*'                  # what IP address(es) to listen on;
local_bind_address = '0.0.0.0'             
password_encryption_type = 0            #Password storage type, 0 is md5 for PG, 1 is sha256 + md5, 2 is sha256 only

>>新建数据库用户:
配置好上面两项后重启数据库:

$ gs_ctl restart -D $GAUSSHOME/data/single_node -Z single_node

由于安全原因初始用户不能从网络访问,所以新建用户并赋予权限。

$ gsql -d postgres

openGauss=# CREATE USER ac_man_parse PASSWORD '新用户的密码';
NOTICE:  The encrypted password contains MD5 ciphertext, which is not secure.
CREATE ROLE

openGauss=# ALTER ROLE ac_man_parse SYSADMIN;
ALTER ROLE

>>在Navicat中配置PostgreSQL的方式访问:

业务系统从Oracle迁移到openGauss数据库的简单记录_第2张图片
小插曲:

Windows10下的自带防火墙,分配了盘符的磁盘分区上的程序通过没有问题。
但是挂载到空目录的磁盘分区,上面的程序设置规则也没用,自带防火墙依然拦截,原理不明。
所以我收到了很多socket error 10013,害我以为是服务端问题。

(2.3.3)Data Studio

下载页面
不知道为啥我跳过了官方的工具,刚才试了一下,JDK劝退。
虽然Data Studio的readme.txt中写着:

Software:  
----------------------------------------------------------------------
Java 1.8.0_181 or later    

但实际运行则不行,所以没有继续使用。
业务系统从Oracle迁移到openGauss数据库的简单记录_第3张图片

(2.3.3)Toad Edge

正在试用,稍微有点重啊。
反应比较慢,但是操作逻辑和TOAD for Oracle差不多。

呃Freeware版本要求每30天登录一次账号进行授权,未联网机器无解(没有Offline模式)。
所以目前还是放弃,等等看是不是要买商业版。

目前看来能够支持PostgreSQL的客户端工具,都可以连接openGauss呢。

(三)业务数据库结构和元数据迁移

官方提供的工具比如Ora2Pg还没用过。
Navicat迁移数据很方便,包括Blob类型字段数据。没有购买也可以先试用来导数据。

(3.1)用工具迁移数据

1)运行Navicat菜单中的Data Transfer,选源和目标:
业务系统从Oracle迁移到openGauss数据库的简单记录_第4张图片

2)设置选项:
我测试中Oracle->PostgreSQL,下面红框中选项(十六进制格式的BLOB)的勾要取消。
而再倒PostgreSQL->PostgreSQL,则要勾上……原理不明……业务系统从Oracle迁移到openGauss数据库的简单记录_第5张图片

3)选择对象:
选需要迁移的数据类型和具体对象,如果是同类数据库,则索引、序列等可选。
业务系统从Oracle迁移到openGauss数据库的简单记录_第6张图片

4)确认和开始:
点开始后,可以看到进度(我已经导过,这里就不贴图了)。
完成后先别关闭。可以返回去再选另一个数据库来同步。
业务系统从Oracle迁移到openGauss数据库的简单记录_第7张图片

5)检查数据结构:
迁移成功后需要检查:各种数据类型长度对不对,大对象(BLOB)内容是否正确。

(3.2)注意建表和导数语法

当然,我们也需要正常的建表导入元数据。
用图形化工具执行脚本很方便,主要问题是Oracle的建表SQL语句需要修改,导入数据语句也需要修改。类似:

  • 去掉: SET DEFINE OFF
  • 去掉: Create table 的修饰NOCOMPRESS,NOCACHE
  • 修改:VARCHAR2(100 BYTE) -> VARCHAR2(100)
  • 修改:ADD (CONSTRAINT XXXXX PRIMARY KEY -> ADD PRIMARY KEY
  • 等等……

不是很明白:PK和Unique的索引,到底要不要自己建?(Oracle不需要)
网上查了一下说PostgreSQL也会自动建,但是看不到啊?

(3.3)表和字段的注释写法

表和字段的注释,不能和Oracle&MySQL一样直接写到建表语句中,
当然Oracle也支持这种先建表再注释的方式,所以脚本还是按照通用的方式写比较方便。如下:

  1. 先建表:CREATE TABLE SampleTable ...
  2. 注释表:COMMENT ON TABLE SampleTable IS '表的注释说明'
  3. 循环注释字段 COMMENT ON COLUMN SampleTable.Field1 IS '字段的注释说明'

(3.4)编码导致长度差异

从上面步骤创建的openGauss数据库字符编码是UTF8
而原Oracle数据库是ZHS16GBK
导入数据时,如果字段内容是中文(多字节)的,则可能原来够用而现在超长。

⚠️注意:并不准确 --> 字段大概估算GBK汉字长度x2,而UTF8汉字长度x3。
具体看这篇《【锟斤拷】的故事:谈谈汉字编码和常用字符集》吧,
这里不再赘述了。

(四)码农的工作(monkey is coding)

(4.1)Java

肯定用JDBC了,
只需要改一点点,其它的用法和Oracle一样(当然得注意SQL语法,别再用Oracle专用函数,省略吐槽)……

(4.1.1)PostgreSQL JDBC Driver

使用PostgreSQL包的方式也可以访问openGauss:

        
        <dependency>
            <groupId>org.postgresqlgroupId>
            <artifactId>postgresqlartifactId>
            <version>42.3.5version>
        dependency>
	JDBCUrl = "jdbc:postgresql://host:port/db";
	driverClass = "org.postgresql.Driver";

(4.1.2)OpenGauss JDBC Driver

使用openGauss包的方式:
这一段官方的文档似乎忘记更新了,一处写com.huawei.opengauss,一处还写的postgre。

        
        <dependency>
            <groupId>org.opengaussgroupId>
            <artifactId>opengauss-jdbcartifactId>
            <version>3.0.0version>
        dependency>
	JDBCUrl = "jdbc:opengauss://host:port/db";
	driverClass = "org.opengauss.Driver";

这种方式会在console打印一些日志,我们的系统需要去掉。
随手丢了个log4j.properties进去,发现亲不是用log4j输出的日志呢……
于是设置loggerLevel=off

		...
		Properties info = new Properties();
		info.setProperty("loggerLevel","off");
		info.setProperty("user", Username);
		info.setProperty("password", gPass);		
		...
		try (Connection conn = DriverManager.getConnection(jdbcUrl, info)) {
			...
		}

(4.1.3)MySQL JDBC Driver

        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>8.0.29version>
        dependency>
	JDBCUrl = "jdbc:mysql://host:port/db";
	driverClass = "com.mysql.cj.jdbc.Driver"; //请注意字符串中间【cj】,不要加载旧驱动了。

(4.1.4)SQL Loader & COPY FROM STDIN & …

>>Oracle

大量数据入库的时候,Oracle可以用SQL Loader:
类似下面的命令行(不是Java也能调用)。
参数direct很重要高速导入都靠它。
文件尽量完整一点,遇到错误可以看logbad更方便。

sqlldr user/password@Database control=sample.ctl log=sample.log bad=sample.bad discard=sample.dis direct=true parallel=true streamsize=16777216

当然,命令行中的控制文件(sample.ctl)需要提前生成,内容大概如下:
稍微有些复杂,比如长字段需要指定长度。

OPTIONS (skip=0)
LOAD DATA CHARACTERSET ZHS16GBK INFILE 'Sample.txt'
APPEND INTO TABLE SampleTable
FIELDS TERMINATED BY X'09'
OPTIONALLY  ENCLOSED BY "'" 
TRAILING NULLCOLS (field1,field2 char(3000),field3, ......)

>>PostgreSQL(openGauss)

它的JDBC提供了一个很类似的方法,还要简单一些。
从文件中拷贝数据到数据库表,如下。
没太多的参数,返回入库了的条数。

...
import org.opengauss.copy.CopyManager;
import org.opengauss.core.BaseConnection;
...
    public static long copyFromFile(Connection connection, String filePath, String tableName) throws SQLException, IOException {
        try (FileInputStream fileInputStream = new FileInputStream(filePath)){
            CopyManager copyManager = new CopyManager((BaseConnection)connection);
            return copyManager.copyIn("COPY " + tableName + " FROM STDIN with (DELIMITER '\t', ENCODING 'gb18030')", fileInputStream);
        }
    }

分隔符和字符集根据实际情况替换。
比较奇怪的是官网文档的例子居然有语法问题,漏写了逗号,还写得很长……

试了一下主表50万左右的用户量,一共10多张表导入,包括建立索引:
感觉没比insert语句更快啊……
如果正式用起来这个速度……ToT

  • Oracle 12c: 20
  • openGauss 3.0.0: 240

试了可以报错,比如:extra data after last expected column
并不会看不出问题在哪,这个就是文本字段多了。

>>MySQL

LOAD DATA LOCAL INFILE ‘xxxx’
参考:这里
……

(4.2)C & C++

(4.2.1)头文件

头文件来自分别安装的客户端。

extern "C" {
	#include "mysql.h"
	#include "libpq-fe.h"
}

(4.2.2)PostgreSQL(libpq)

其实都是c代码,
真实SQL最好用绑定变量,而不是下面的一堆NULL,SQL全字符串拼接。
这方面资料很多所以不写了,例子:

	//连接
	sprintf(connStr,"user=%s password=%s host=%s port=%s dbname=%s sslmode=disable",xxx,yyy,zzz,...);
	PGconn *z_pgSession = PQconnectdb(connStr);
	if (PQstatus(z_pgSession)!=CONNECTION_OK)
		throw MyException(PQerrorMessage(z_pgSession));
	......
	
	//执行insert,update,...
	PGresult * pres=PQexec(z_pgSession, "update ...");
		if (PGRES_COMMAND_OK!=PQresultStatus(pres))
			throw MyException(PQerrorMessage(z_pgSession));		
		PQclear(pres);	
	......
	
	//执行查询(字符串)
	PGresult * pres=PQexec(z_pgSession, "select ...");
	if (PQresultStatus(pres) != PGRES_TUPLES_OK)
		throw MyException(PQerrorMessage(z_pgSession));
	int RowCount = PQntuples(pres) ;
	for(int RowIndex = 0;RowIndex < RowCount; RowIndex++)
	{
		unsigned int rowcount = PQnfields(pres);
		strcpy(value1 , PQgetvalue(pres,RowIndex,0));
		strcpy(value2 , PQgetvalue(pres,RowIndex,1));
		......
	}
	PQclear(pres);
	......
	
	//执行查询(二进制)
	PGresult * preXs=PQprepare(z_pgSession, "","select blob ...",0,NULL);
	if (preXs== NULL)
		throw MyException(PQerrorMessage(z_pgSession));
	PQclear(preXs);
	
	PGresult * pre3s=PQexecPrepared(z_pgSession, "",0,NULL,NULL,NULL,1);
	if (PQresultStatus(pre3s) != PGRES_TUPLES_OK)
		throw MyException(PQerrorMessage(z_pgSession));
					
	unsigned long obj1size = PQgetlength(pre3s,0,0);
	std::ofstream ofs; //sorry, c++ 更方便
	ofs.open("filename",std::ios::out|std::ios::binary);
	if (obj1size>0) //有内容才写
	{
		char *object = (char*)malloc(obj1size);
		if (object == NULL){
			throw MyException("error alloc mem...");
		}
		memcpy(object, PQgetvalue(pre3s,0,0), obj1size);
		ofs.write(object,obj1size);
		free(object);
	}
	ofs.close();		
	PQclear(pre3s);
	...

	PQfinish(z_pgSession);

(4.2.3)MySQL

和上面几乎是一样的流程,
呃,但没懂MySQL怎么用绑定变量。例子:

	//连接
	MYSQL *z_mySession=mysql_init(NULL);
	if (z_mySession==NULL)
		throw MyException("Error mysql_init");

	if (mysql_real_connect(z_mySession,"Host","User", "Passwd","DBName","Port"),NULL,0)==NULL)
		throw MyException(mysql_error(z_mySession));
	......

	//执行查询
	if(mysql_query(z_mySession,"select ...")!=0)
		throw MyException(mysql_error(z_mySession));	
	MYSQL_RES *result = mysql_store_result(z_mySession);
	if (result==NULL)
    	throw MyException(mysql_error(z_mySession));    		
	while (MYSQL_ROW sql_row = mysql_fetch_row(result))
	{
		strcpy(value1 , sql_row[0]);
		strcpy(value2 , sql_row[1]);
		......
	}
	mysql_free_result(result);
	......

	//执行查询(二进制)
	if(mysql_real_query(z_mySession,"select blob ...",SQLlength)!=0)
		throw MyException(mysql_error(z_mySession));
	MYSQL_RES *result2 = mysql_store_result(z_mySession);
	if (result2==NULL)
		throw MyException(mysql_error(z_mySession));
	if (MYSQL_ROW sql_row2 = mysql_fetch_row(result2))//获取具体的数据
	{
		unsigned long *rowlength =  = mysql_fetch_lengths(result2);
		unsigned int rowcount  = mysql_num_fields(result2);
		unsigned long objsize = rowlength [0];
		std::ofstream ofs;
		ofs.open("filename",std::ios::out|std::ios::binary);
		if (obj1size>0) //其实也可以不判断,直接 malloc(0)。
		{
			char *object = (char*)malloc(obj1size);
			if (object == NULL){
				throw MyException("error alloc mem...");
			}
			memcpy(object, sql_row2[0], objsize);
			ofs.write(object,obj1size);
			free(object);
		}
		ofs.close();
	}
	......

	mysql_close(z_mySession);

(4.2.4)客户端环境(Ubuntu)

用C可没有Java那么方便,运行也需要客户端的库。
所以需要一些准备:

$ sudo apt install libpq-dev
$ sudo apt install libmysqlclient-dev

PS:开发方面参考:《Linux编译C++项目找到.so库后找不到里面的函数》
哎……

(4.3)Object Pascal

单身20年的delphi7老程序本已停止维护,都准备放弃了。
通过Java发现既然PostgreSQL方式可以访问openGauss,那么也试试,结果可以:

	MainSession.Close;
	MainSession.ProviderName:='PostgreSQL';
	MainSession.Server:=theHost;
	MainSession.Port:=StrToIntDef(thePort,-1);
	MainSession.Database:=theDB;
	MainSession.Username:=Username;
	MainSession.Password:=gPass;
	MainSession.Open;

但需要注意,主机、端口、数据库名称。
需要像上面的代码拆开分别赋值。
注意不能和Oracle一样直接MainSession.Server:=//ip:port/databasename
另外似乎也不能乱加数据库不支持的参数比如MainSession.SpecificOptions.Values['xxxxx'] := 'yyyyy';
毕竟不是只支持Oracle一种数据库了。

⚠️ ⚠️ ⚠️ 神秘的bug ⚠️ ⚠️ ⚠️
【delphi+unidac+openGauss】,当SQL选出的字段是日期型(Timestamp)时(包括选now())。
程序.AsDatetime取到的任何日期永远是2000-01-01 08:00:00,用.value也一样,取出来时已经变了。
其它语言和数据库组合未发现这种问题。
不知道是unidac的锅,还是openGauss的锅。
目前不知道怎么解决,暂时改程序,绕过不要直接取日期。

(4.4)大小写问题

Oracle默认所有对象名称大写(除非你加双引号强行小写)。
PostgreSQL则都是小写。

在一般的SQL中不会有什么问题,但是牵涉到选出表名做比较时,两个数据库就不一样了。
最好用Upper(字段)来进行统一,否则只能程序里if else判断不同的库写不同的SQL了。

还有,登录的用户名Oracle随意大小写,
但是PostgreSQL需要严格区分。

(4.5)特有语法与高亮

不仅处理部分需要考虑不同的数据库语法。
有显示或配置界面的地方,得注意不同数据库特有的关键字,加以高亮。
比如Oracle的`

select sysdate from dual;

比如PostgreSQL

select now();

(五)未完待续

【2022-05-07】弄了一整天总算可以简单的访问openGauss了,以后的工作和遇到的问题,再继续记录吧。
【2022-05-10】增加了表和字段注释脚本问题,以及sqlldrCOPY FROM STDIN导入文件内容。
【2022-05-13】对比sqlldrCOPY FROM STDIN效率,报错。语法和高亮。神秘bug。客户端工具。
【2022-07-28】一些C++的代码例子。

实际做下来SQL改动不少……
具体请参考下一篇:《业务系统兼容数据库Oracle/PostgreSQL(openGauss)/MySQL的琐事》


你可能感兴趣的:(Linux,数据库,General,Coding,数据库,oracle,postgresql,openGauss,JDBC)