由于世界风云变幻,无论是国家、客户、还是公司老板。
都希望数据库能够自主可控,不是米国的,自研的,最好还是开源的。
所以用了20年的Oracle数据库也准备更换了(省略吐槽,不考虑工作量?)……
源于PostgreSQL-XC,由华为主导、各个生态合作伙伴共同建设的。开源发行协议遵从木兰宽松许可证v2。
目前版本3.0.0,介绍分为【企业版】和【轻量版】,不过这次我们用到的却是【极简版】……
周边工具和生态如下(省略吐槽,Spark咋办?):
下面是这个网站关于openGauss和目前最流行关系型数据库的对比(可以自己加上SQL Server 和 PostgreSQL)
MySQL vs. OpenGauss vs. Oracle
因为工作都是用的Oracle(少量MySQL,SQL Server)。
完全没有接触过openGauss(甚至没用过PostgreSQL)也没时间慢慢研究了,
所以大概拟定步骤如下:
为啥以前连PostgreSQL都没用(省略吐槽)……
对比Oracle多了非常多的数据类型。
官网好些文档并没有随着版本更新。
但详细的步骤和各种资料都是中文的!
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 4月 1 18:24 bin
drwx------. 3 Shion Shion 25 5月 6 10:18 data
drwxr-xr-x. 3 Shion Shion 22 4月 1 18:24 etc
drwxr-xr-x. 3 Shion Shion 24 4月 1 18:24 include
drwxr-xr-x. 4 Shion Shion 95 4月 1 18:24 jre
drwxr-xr-x. 5 Shion Shion 4096 4月 1 18:24 lib
drwx------. 2 Shion Shion 126 5月 6 10:18 logs
drwxr-xr-x. 5 Shion Shion 53 4月 1 18:24 share
drwxr-xr-x. 2 Shion Shion 94 5月 6 10:19 simpleInstall
-rw-r--r--. 1 Shion Shion 32 4月 1 18:24 version.cfg
官网都有,不详细写了,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
官方的例子是【极简版】不是【轻量版】,那么就按教程来吧。
也不准备弄主备,快点搭建好才行。
进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
会报错。
数据库安装完成后,默认生成名称为postgres
的数据库。
$ 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=#
平时可没法全用命令行,得有个正常的图形界面管理工具。
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的方式访问:
Windows10下的自带防火墙,分配了盘符的磁盘分区上的程序通过没有问题。
但是挂载到空目录的磁盘分区,上面的程序设置规则也没用,自带防火墙依然拦截,原理不明。
所以我收到了很多socket error 10013,害我以为是服务端问题。
下载页面
不知道为啥我跳过了官方的工具,刚才试了一下,JDK劝退。
虽然Data Studio的readme.txt中写着:
Software:
----------------------------------------------------------------------
Java 1.8.0_181 or later
正在试用,稍微有点重啊。
反应比较慢,但是操作逻辑和TOAD for Oracle差不多。
呃Freeware版本要求每30天登录一次账号进行授权,未联网机器无解(没有Offline模式)。
所以目前还是放弃,等等看是不是要买商业版。
目前看来能够支持PostgreSQL的客户端工具,都可以连接openGauss呢。
官方提供的工具比如Ora2Pg
还没用过。
但Navicat迁移数据很方便,包括Blob类型字段数据。没有购买也可以先试用来导数据。
1)运行Navicat菜单中的Data Transfer,选源和目标:
2)设置选项:
我测试中Oracle
->PostgreSQL
,下面红框中选项(十六进制格式的BLOB)的勾要取消。
而再倒PostgreSQL
->PostgreSQL
,则要勾上……原理不明……
3)选择对象:
选需要迁移的数据类型和具体对象,如果是同类数据库,则索引、序列等可选。
4)确认和开始:
点开始后,可以看到进度(我已经导过,这里就不贴图了)。
完成后先别关闭。可以返回去再选另一个数据库来同步。
5)检查数据结构:
迁移成功后需要检查:各种数据类型长度对不对,大对象(BLOB)内容是否正确。
当然,我们也需要正常的建表导入元数据。
用图形化工具执行脚本很方便,主要问题是Oracle的建表SQL语句需要修改,导入数据语句也需要修改。类似:
SET DEFINE OFF
。NOCOMPRESS
,NOCACHE
VARCHAR2(100 BYTE)
-> VARCHAR2(100)
ADD (CONSTRAINT XXXXX PRIMARY KEY
-> ADD PRIMARY KEY
不是很明白:PK和Unique的索引,到底要不要自己建?(Oracle不需要)
网上查了一下说PostgreSQL也会自动建,但是看不到啊?
表和字段的注释,不能和Oracle&MySQL一样直接写到建表语句中,
当然Oracle也支持这种先建表再注释的方式,所以脚本还是按照通用的方式写比较方便。如下:
CREATE TABLE SampleTable ...
COMMENT ON TABLE SampleTable IS '表的注释说明'
COMMENT ON COLUMN SampleTable.Field1 IS '字段的注释说明'
从上面步骤创建的openGauss数据库字符编码是UTF8
,
而原Oracle数据库是ZHS16GBK
。
导入数据时,如果字段内容是中文(多字节)的,则可能原来够用而现在超长。
⚠️注意:并不准确 --> 字段大概估算GBK
汉字长度x2,而UTF8
汉字长度x3。
具体看这篇《【锟斤拷】的故事:谈谈汉字编码和常用字符集》吧,
这里不再赘述了。
肯定用JDBC了,
只需要改一点点,其它的用法和Oracle一样(当然得注意SQL语法,别再用Oracle专用函数,省略吐槽)……
使用PostgreSQL包的方式也可以访问openGauss:
<dependency>
<groupId>org.postgresqlgroupId>
<artifactId>postgresqlartifactId>
<version>42.3.5version>
dependency>
JDBCUrl = "jdbc:postgresql://host:port/db";
driverClass = "org.postgresql.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)) {
...
}
<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】,不要加载旧驱动了。
大量数据入库的时候,Oracle可以用SQL Loader:
类似下面的命令行(不是Java也能调用)。
参数direct
很重要高速导入都靠它。
文件尽量完整一点,遇到错误可以看log
和bad
更方便。
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, ......)
它的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
20
秒240
秒试了可以报错,比如:extra data after last expected column
。
并不会看不出问题在哪,这个就是文本字段多了。
LOAD DATA LOCAL INFILE ‘xxxx’
参考:这里
……
头文件来自分别安装的客户端。
extern "C" {
#include "mysql.h"
#include "libpq-fe.h"
}
其实都是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);
和上面几乎是一样的流程,
呃,但没懂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);
用C可没有Java那么方便,运行也需要客户端的库。
所以需要一些准备:
$ sudo apt install libpq-dev
$ sudo apt install libmysqlclient-dev
PS:开发方面参考:《Linux编译C++项目找到.so库后找不到里面的函数》
哎……
单身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的锅。
目前不知道怎么解决,暂时改程序,绕过不要直接取日期。
Oracle默认所有对象名称大写(除非你加双引号强行小写)。
PostgreSQL则都是小写。
在一般的SQL中不会有什么问题,但是牵涉到选出表名做比较时,两个数据库就不一样了。
最好用Upper(字段)
来进行统一,否则只能程序里if else
判断不同的库写不同的SQL了。
还有,登录的用户名Oracle随意大小写,
但是PostgreSQL需要严格区分。
不仅处理部分需要考虑不同的数据库语法。
有显示或配置界面的地方,得注意不同数据库特有的关键字,加以高亮。
比如Oracle
的`
select sysdate from dual;
比如PostgreSQL
的
select now();
【2022-05-07】弄了一整天总算可以简单的访问openGauss了,以后的工作和遇到的问题,再继续记录吧。
【2022-05-10】增加了表和字段注释脚本问题,以及sqlldr
和COPY FROM STDIN
导入文件内容。
【2022-05-13】对比sqlldr
和COPY FROM STDIN
效率,报错。语法和高亮。神秘bug。客户端工具。
【2022-07-28】一些C++的代码例子。
实际做下来SQL改动不少……
具体请参考下一篇:《业务系统兼容数据库Oracle/PostgreSQL(openGauss)/MySQL的琐事》