技术开发 频道

主流列式数据库评测:南大通用GBase 8a

  二、数据库的功能

  GBase是一个服务器/客户机模式的数据库,服务器端其实就是一个后台运行的GBase服务。我们可以在Windows的管理工具/服务中看到它已经被设置为默认自动随操作系统启动,这也意味着,我们基本上不需要人工地干预。在需要的时候可以用Windows管理工具启动和关闭它。GBase还有一种启动方式,在命令行输入GBased -- console启动,但这不是推荐的方式。

  数据库的基本功能有CRUD(表的创建、插入、更新、删除)等方面,下面我们逐个测试。

  GBase 8a提供的集成管理工具包括一个适合于初学者和非专业用户的交互式的表设计工具,也包括一个SQL文本编辑器,使用后者需要用户有一定的SQL基础,但只有这点基础也不够,还需要对GBase 8a的语法规范和数据类型等有足够的了解。比如: Decimal(30)是非法的,不是因为Decimal关键字非法,而是定义的数据长度超出相应类型的限制。因为GBase中Decimal最大长度是18,也就是8字节长整型数的范围,与Firebird等数据库一致。(参考文献1中提到gbase支持更长的数字类型,也许要下一个版本才会支持)

  除了图形化工具,GBase还提供一个本身自带的命令行工具,界面和mysql也神似。

F:\GBase\Server\bin>gbase -uroot -proot test
Enter password:
****
Welcome
to the GBase monitor.  Commands end with ; or \g.
Your GBase connection id
is 39
Server version:
8.3.31.100

Type
'help;' or '\h' for help. Type '\c' to clear the current input statement.

   黑体部分可省略,工具会提示用户输入口令,口令字符以*号代替,这样具有较好的安全性。

  有趣的是我们也可以用mysql客户端可以访问GBase服务端,但需要指定5029端口。

D:\app\mysql54\bin>mysql -h 10.x.x.23 -P 5029 -uroot -proot
Welcome
to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id
is 35
Server version:
8.3.31.100 100.1

Type
'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql
> use test;
Database changed
mysql
> select count(*) from test.t;
+----------+
| count(
*) |
+----------+
|    
1000 |
+----------+
1 row in set (0.00 sec)

   成功地创建了表。下一步是插入数据,按以往的经验,用insert语句逐行插入涉及客户端和数据库服务器的多次解析和交互,一般是较慢的,能否把外部文本(csv)文件导入?还用mysql的方法在这里行不通了,load data infile命令报错。

mysql> load data infile 'f:/soft/ren41_10.csv' into table ren41 fields terminated by ',' ;
ERROR 3 (HY000): Unsupport commond, please use gbloader.

  而一时也猜不透gbloader的语法规格,只能退而求其次,用存储过程,不懂语法规则没关系,再用mysql的试一把,这下顺利执行成功了,只不过速度大大低于预期。

DELIMITER |
CREATE PROCEDURE `test`.`insert_1K`
( )
BEGIN
  DECLARE v
INT;  
  
SET v = 1;  
  loop_label:
LOOP  
    INSERT INTO t VALUES (
mod(v,4),mod(v,5),mod(v,67),mod(case when mod(v,13)>0 then v end ,113),v);  
    
SET v = v + 1;  
    
IF v > 1000 THEN  
      LEAVE loop_label;  
    
END IF;  
  
END LOOP;  
  
END |
call test.insert_1K()
执行时间 21479ms, 更新行数:
1

  再查阅mysql文档,了解到有个自动提交的开关,用set autocommit=off把它关闭,再次执行刚才的存储过程,速度就合乎常情了。

mysql> Set AUTOCOMMIT=OFF;
Query OK,
0 rows affected (0.00 sec)

mysql
> call insert_1K();
Query OK,
1 row affected (0.05 sec)

mysql
> commit;
Query OK,
0 rows affected (0.04 sec)

mysql
> select count(*) from t;
+----------+
| count(
*) |
+----------+
|    
2000 |
+----------+
1 row in set (0.00 sec)

  GBase毕竟不是Mysql,有一些地方是独特的,需注意。

  GBase默认创建的表存储引擎是express,可以用show create table命令获得,并且不支持其他的存储引擎,虽然不报错,但实际上仍保持gbase引擎:

gbase> create table tisam (a varchar(10))engine=myisam;
Query OK,
0 rows affected, 2 warnings (0.06 sec)

gbase
> show create table tisam;
-----------------------+
| tisam | CREATE TABLE `tisam` (
  `a` varchar(
10) DEFAULT NULL
) ENGINE
=EXPRESS DEFAULT CHARSET=utf8 |
-----------------------+

   从前面对GBase数据目录的分析可以了解,实际上GBase完全能够读取myisam存储引擎格式的表,只是不支持用户通过命令来创建其他的存储引擎的表。

  分区是数据仓库的优化手段之一GBase 8a支持范围分区,可能也支持其他分区方式,就不一一试验了。

gbase>  CREATE TABLE foo (created DATETIME)PARTITION BY RANGE (TO_DAYS(created))(
    
-> PARTITION foo_1 VALUES LESS THAN (TO_DAYS('2009-01-01')),
    -> PARTITION foo_2 VALUES LESS THAN (TO_DAYS('2012-01-01'))
    -> );
Query OK,
0 rows affected (0.08 sec)
gbase
> insert into foo values(now());
Query OK,
1 row affected (0.07 sec)
gbase
> select * from foo;
+---------------------+
| created             |
+---------------------+
|
2011-01-10 16:45:14 |
+---------------------+
1 row in set (0.01 sec)

   另外,GBase 8a不支持表的主键,也不支持用户自定义索引。

gbase> alter table test.b add primary key (a);
ERROR 1031 (HY000): Table storage engine for 'b' doesn't have this option
gbase> create index a on test.b(a);
ERROR 1031 (HY000): Table storage engine for 'b' doesn't have this option

   祭出我用来测试的中等规模数据集,100万行记录,一个多列分组聚合查询。更新操作对支持事务的数据库是代价较大的操作,Gbase的删除支持delete和truncate table二种方式。

  GBase的事务处理实现与Oracle等其他数据库有多处差别,在开发时需要引起重视。请看下面例子:

/*会话   1 */                                  │ /*会话   2*/
gbase
> set autocommit =off;                    │
Query OK,
0 rows affected (0.00 sec)           │
                                               │
gbase
> insert into test.t2 values('12');       │ /*会话1未提交,insert更改对会话2不可见*/
Query OK, 1 row affected (0.03 sec)            │ gbase> select * from test.t2;
                                               │
Empty set (0.02 sec)
gbase
> commit;                                 │
Query OK,
0 rows affected (0.11 sec)           │ /*会话1提交,insert更改对会话2可见*/
                                               │ gbase
> select * from test.t2;
                                               │
+------+
                                               │ | a    |
                                               │
+------+
                                               │ |
12   |
                                               │
+------+
                                               │
1 row in set (0.02 sec)
gbase
> delete from test.t2 where a='12';       │
Query OK, 1 row affected (0.02 sec)            │ /*会话1未显式提交,但delete的更改对会话2可见*/
                                               │ gbase
> select * from test.t2;
gbase
> rollback;                               │ Empty set (0.00 sec)
Query OK,
0 rows affected (0.00 sec)           │ /*会话1回滚,但delete的更改已提交,无法恢复*/
                                               │ gbase
> select * from test.t2;
gbase
> insert into test.t2 values('12');       │ Empty set (0.00 sec)
Query OK, 1 row affected (0.00 sec)            │
/*会话1未提交,insert的更改对会话1也不可见*/  │ gbase> select * from test.t2;
gbase
> update test.t2 set a='13' where a='12'; │ Empty set (0.00 sec)
ERROR 1015 (HY000): Can't lock file (errno: 1) │
gbase> commit;                                 │
Query OK,
0 rows affected (0.06 sec)           │
                                               │
gbase
> update test.t2 set a='13' where a='12'; │
Query OK, 1 row affected (0.06 sec)            │/*会话1未显式提交,但update的更改对会话2可见*/
Rows matched:
1  Changed: 1  Warnings: 0       │ gbase> select * from test.t2;
                                               │
+------+
gbase
> insert into test.t2 values('14');       │ | a    |
Query OK, 1 row affected (0.00 sec)            │ +------+
/*会话1未提交,insert的更改对会话1也不可见*/  │ | 12   |
gbase
> select * from t2;                       │ +------+
+------+                                       │ 1 row in set (0.00 sec)
| a    |                                       │
+------+                                       │
|
13   |                                       │
+------+                                       │
1 row in set (0.00 sec)                        │
                                               │ gbase
> select * from test.t2;
gbase
> commit;                                 │ +------+
Query OK,
0 rows affected (0.01 sec)           │ | a    |
                                               │
+------+
gbase
> select * from t2;                       │ | 13   |
+------+                                       │ +------+
| a    |                                       │
1 row in set (0.00 sec)
+------+                                       │
|
13   |                                       │
|
14   |                                       │
+------+                                       │
2 rows in set (0.00 sec)

   如图所示,GBase可设置不自动提交事务,默认是在会话级的。但这个不提交仅对insert操作生效,其余DML,如delete和update,仍然自动提交,而且在不自动提交的时候,insert操作以后,新记录在发起命令的事务本身都是不可见的,如果此刻发出更新命令,则报错。

  测试到这似乎也没多少可干的了,少量数据体现不出列式的优势,常用的CRUD别的数据库都支持。用简单存储过程模拟出的数据离现实太远,测试了也不代表实际生产环境,而已有的外部真实数据又难以加载。由于GBase 8a定位于数据仓库应用,批量数据加载的功能也是不可缺少的,这也怪不得GBase,谁让我是在没有技术支持的情况下自己摸索的。直到偶然用GBase 组合8.3版本号在网上搜到了《gbase ctl文件格式与gbloader方法》,一切都迎刃而解了。

  还有一个工具值得一提,尽管它不是gbase数据库安装的一部分,却是gbase工程师在做技术工作中遗留下来的好东西,orato8a,顾名思义是一个从Oracle到8a的迁移工具,这个工具的提示信息也是令我眼前一亮,有种久别重逢的感觉。

  这不就是楼方鑫SQLULDR2工具的命令行参数吗,它的前身ociuldr在网上有公开的源码,gbase将这个工具在64位Windows上编译了,更加符合普通非专业用户的需求。这个工具能产生csv数据文件对应的gbloader控制文件,如果用脚本适当编程就可以实现从Oracle到8a批量的数据迁移。

orato8a产生的控制文件
#
# Generated by SQLULDR
#
BASE_PATH
=
TABLE_NAME
=/ren
LOAD_DATA_INFILE
=f:\soft\ren.csv
FORMAT
= 0

   gbloader命令行语法举例如下:

F:\GBase\Server\bin>gbloader -uroot -proot f:\soft\lineitem.ctl
Load Records:
6001215
1 min 0.09 sec

  指定用户名、口令和上面提到的控制文件路径就可实现数据批量导入。

  一个令人疑惑的问题,就是我通过gbloader加载的数据,数据目录和原始文件排除分隔符占用的空间后大小几乎没有差别,也就是说压缩没有起作用。

F:\Calpont\bulk\data\import 的目录

2010/12/20  12:27        24,496,144 customer.tbl
2010/12/20  12:27       765,864,502 lineitem.tbl
2010/12/20  12:27             2,249 nation.tbl
2010/12/20  12:27       173,452,161 orders.tbl
2010/12/20  12:27        24,407,240 part.tbl
2010/12/20  12:27       119,784,616 partsupp.tbl
2010/12/20  12:27               394 region.tbl
2010/12/20  12:27         1,419,184 supplier.tbl
              
8 个文件  1,109,426,490 字节
              
F:
\GBase\Server\data\tpch的大小920 MB (965,712,866 字节)  

   而用存储过程插入的数据,压缩率却比较高:

F:\GBase\Server\data\test\t.GED的大小5.71 MB (5,991,113 字节)
gbase
> select count(*) from test.t;
+----------+
| count(
*) |
+----------+
|  
1000000 |
+----------+
1 row in set (0.00 sec)

gbase
> select * from test.t into outfile 'f:/soft/t1m.csv';
Query OK, 1000000 rows affected, 1 warning (5.70 sec)
F:
\GBase\Server\bin>dir \soft\t1m.csv

2011/01/09  19:04        16,764,132 t1m.csv

   关于gbloader还有一些选项,比如压缩级别和并行度,我在测试中都没有生效,无论是直接在命令行指定还是在配置文件gbase_8a.ini中修改都无效,可能还有一些条件未满足,需要进一步研究。

8
相关文章