在GBase 8a数据库集群的V95版本里,支持虚拟集群来物理隔离计算节点资源,实现多租户。本文介绍该版本实现在虚拟集间创建按镜像表的功能,通过镜像功能是双向的的特性,可以实现实时的平等双活,也可以实现读写分离。
目录导航
语法汇总
库级
ALTER DATABASE VC1.DB CREATE MIRROR TO VC2;
ALTER DATABASE VC1.DB CREATE MIRROR TO VC2 force;
ALTER DATABASE VC1.DB DELETE MIRROR;
ALTER DATABASE VC1.DB SET DEFAULT MIRROR = VC2;
ALTER DATABASE VC1.DB SET DEFAULT MIRROR = NULL;
show mirror databases;
表级
CREATE TABLE VC1.DB.T1(A INT, B VARCHAR(10)) MIRROR TO VC2;
create table tlike2 mirror to vc2 as select * from t1;
ALTER TABLE VC1.DB.T1 CREATE MIRROR TO VC2;
ALTER TABLE VC1.DB.T1 CREATE MIRROR TO VC2 FORCE;
ALTER TABLE VC1.DB.T1 DELETE MIRROR;
测试版本
gbase> select @@version;
+-----------------+
| @@version |
+-----------------+
| 9.5.2.26.121440 |
+-----------------+
1 row in set (Elapsed: 00:00:00.00)
要求
镜像集群的主分片数量必须相同。 和节点数无关,和备份数量无关。
镜像集群的相关表的nodedatamap必须相同。建议新建的VC2,可以用如下命令初始化
INITNODEDATAMAP FROM VC1;
镜像库
以数据库为单位做整体镜像。包括两类操作
- 已经存在的对象的镜像
- 后续新建对象的镜像
语法
库内已有对象的镜像创建
新版本可以自动创建表,存储过程和函数,但当前版本尚不包括视图(猜测是涉及到嵌套使用关系)。
如果目标对象已经存在,则报warning。
ALTER DATABASE VC1.DB CREATE MIRROR TO VC2;
检查一致性。如果不同则强制重建
注意,force是单向的。如果vc2里有,但vc1不存在的表,存储过程和函数,并不会强行删除vc2里的。
ALTER DATABASE VC1.DB CREATE MIRROR TO VC2 force;
库内已创建镜像的镜像删除
ALTER DATABASE VC1.DB DELETE MIRROR;
默认镜像,库内新对象的自动镜像
同步的范围包括【新建】的Table 、 view 、 procedure。不影响已经存在的对象。新建表自动为镜像表。后续所有DDL自动同步。
ALTER DATABASE VC1.DB SET DEFAULT MIRROR = VC2;
关闭默认镜像
后续所有的对象类变动(Table 、 view 、 procedure, DDL、DML)不再自动建立镜像,但不影响已经创建镜像的表。
ALTER DATABASE VC1.DB SET DEFAULT MIRROR = NULL;
查看库的镜像关系
show mirror databases;
说明
要求 VC1.DB 库和 VC2.DB 库必须存在。创建镜像库,除了给库下的表创建镜像表之外,还会把 VC1.DB下已存在的存储过程、函数在 VC2.DB 下创建。
如果VC2下没有同名的表,则创建新表,否则只创建镜像。
如果已经存在的表结构不一致,请先行调整。或者用 force参数强行检查,如不同会重建,所以请注意数据的安全性。
目标库存在,而源库不存在的表,不会自动同步,也就是镜像操作本身是单向的。
使用方法
建立库镜像,要先创建默认镜像,保证新的变动能自动同步,再创建库镜像,把已有的对象镜像过去。注意顺序。
ALTER DATABASE VC1.DB SET DEFAULT MIRROR = VC2;
ALTER DATABASE VC1.DB CREATE MIRROR TO VC2;
如果要完全去掉库级镜像,需要先关闭默认镜像,再删除已经建立的镜像表。注意顺序
ALTER DATABASE VC1.DB SET DEFAULT MIRROR = NULL;
ALTER DATABASE VC1.DB DELETE MIRROR;
用例
创建数据库
gbase> use vc vc01;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> create database testmirror;
Query OK, 1 row affected (Elapsed: 00:00:00.00)
gbase> create database vc02.testmirror;
Query OK, 1 row affected (Elapsed: 00:00:00.01)
创建测试用的表
我们在源库vc01创建一个int字段的t1表,在vc02创建一个name类型的表结构不同的t1表,以及一个vc01不存在的t2表。
gbase> create table vc01.testmirror.t1(id int);
Query OK, 0 rows affected (Elapsed: 00:00:00.08)
-- 表名字相同但表结构不同
gbase> create table vc02.testmirror.t1(name varchar(100));
Query OK, 0 rows affected (Elapsed: 00:00:00.09)
-- 多了一个表
gbase> create table vc02.testmirror.t2(id int);
Query OK, 0 rows affected (Elapsed: 00:00:00.09)
gbase> show tables from vc01.testmirror;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
+----------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> show tables from vc02.testmirror;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
| t2 |
+----------------------+
2 rows in set (Elapsed: 00:00:00.00)
创建镜像库
如下的镜像库,会看到vc02的t2表并没有镜像到vc01,因为目标表存在了。
gbase> alter database testmirror create mirror to vc02;
Query OK, 0 rows affected, 1 warning (Elapsed: 00:00:00.01)
gbase> show warnings;
+---------+------------+-------------------------------+
| Level | Code | Message |
+---------+------------+-------------------------------+
| Warning | 4294967295 | vc00002.testmirror.t1 exists. |
+---------+------------+-------------------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> use testmirror;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> show tables from vc01.testmirror;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
+----------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> show tables from vc02.testmirror;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
| t2 |
+----------------------+
2 rows in set (Elapsed: 00:00:00.00)
查看同名的t1表结构,发现保持原样,并没有和vc01保持一致。
gbase> show create table vc02.testmirror.t1;
+-------+--------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+--------------------------------------------------------------------------------------------------------------------------+
| t1 | CREATE TABLE "t1" (
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+--------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
强制镜像库
增加force参数,进行强制镜像库
gbase> alter database testmirror delete mirror;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase>
gbase> alter database testmirror create mirror to vc02 force;
Query OK, 0 rows affected (Elapsed: 00:00:00.31)
查看信息,发现vc02的t1表的结构和vc01的一致了。
gbase> show tables from vc01.testmirror;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
+----------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> show tables from vc02.testmirror;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
| t2 |
+----------------------+
2 rows in set (Elapsed: 00:00:00.00)
gbase> show create table vc02.testmirror.t1;
+-------+-------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------+
| t1 | CREATE TABLE "t1" (
"id" int(11) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
测试存储过程、函数和视图
gbase> create view v_t1 as select * from t1;
Query OK, 0 rows affected (Elapsed: 00:00:00.02)
gbase> delimiter //
gbase> create function f_1() returns datetime
-> begin
-> return now();
-> end//
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> select vc01.testmirror.f_1();
+-----------------------+
| vc01.testmirror.f_1() |
+-----------------------+
| 2021-01-12 18:56:31 |
+-----------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> select * from vc01.testmirror.v_t1;
Empty set (Elapsed: 00:00:00.01)
强制同步后,发现函数自动镜像过去了,视图没有。
gbase> alter database testmirror create mirror to vc02 force;
Query OK, 0 rows affected (Elapsed: 00:00:00.34)
gbase> use vc02.testmirror;
Query OK, 0 rows affected (Elapsed: 00:00:00.00)
gbase> show tables;
+----------------------+
| Tables_in_testmirror |
+----------------------+
| t1 |
| t2 |
+----------------------+
2 rows in set (Elapsed: 00:00:00.00)
gbase> select vc01.testmirror.f_1();
+-----------------------+
| vc01.testmirror.f_1() |
+-----------------------+
| 2021-01-12 18:56:31 |
+-----------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> select * from vc01.testmirror.v_t1;
Empty set (Elapsed: 00:00:00.01)
gbase> select * from vc02.testmirror.v_t1;
ERROR 1146 (42S02): Table 'vc02.testmirror.v_t1' doesn't exist
gbase>
库的默认镜像
如果要将视图等都同步过去,需要设置库的默认镜像
alter database vc1.testmirror set default mirror=vc2;
之后,在任何一面的DDL操作,包括表,视图,存储过程等,都会自动在镜像库进行。且新建的表自动为镜像表。
镜像库总结
镜像库适合全新的库,否则一定要注意原有数据的安全。
镜像表
语法
包括2种情况,一,全新的表创建镜像;二、已经存在的表创建镜像。
新建表直接指定镜像
通过在建表语句后面,增加mirror to VC2来创建。
CREATE TABLE VC1.DB.T1(A INT, B VARCHAR(10)) MIRROR TO VC2;
或者
gbase> create table tlike2 mirror to vc2 as select * from t1;
Query OK, 1 row affected (Elapsed: 00:00:00.37)
已存在的表创建镜像
通过Alter语句,在后面增加 create mirror to vc2的方式创建。
ALTER TABLE VC1.DB.T1 CREATE MIRROR TO VC2;
强制创建镜像表并同步结构
ALTER TABLE VC1.DB.T1 CREATE MIRROR TO VC2 FORCE;
删除表镜像关系
ALTER TABLE VC1.DB.T1 DELETE MIRROR;
说明
如果目标表已经存在,或者删除,或者确认和原始表结构一致,否则有数据丢失风险。
查看表的镜像关系
select vc_id,index_name,mirror_vc_id from gbase.table_distribution
where dbname='testdb' order by vc_id;
样例
新表全新建立镜像表
在vc01创建的表,会在vc02直接建立。
gbase> show tables from vc02.testdb;
+------------------+
| Tables_in_testdb |
+------------------+
| t1 |
+------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> create table t2(id int) mirror to vc02;
Query OK, 0 rows affected (Elapsed: 00:00:00.18)
gbase> show tables from vc01.testdb;
+------------------+
| Tables_in_testdb |
+------------------+
| t1 |
| t2 |
+------------------+
2 rows in set (Elapsed: 00:00:00.00)
gbase> show tables from vc02.testdb;
+------------------+
| Tables_in_testdb |
+------------------+
| t1 |
| t2 |
+------------------+
2 rows in set (Elapsed: 00:00:00.00)
gbase>
建新表时,目标镜像表已存在
报表已经存在错误,执行失败。
gbase> create table t3(id int) mirror to vc02;
ERROR 1702 (HY000): gcluster table error: mirror vc vc00002 already exists table t3
gbase>
目标镜像表已存在,新建表
现有版本,不支持在create table时指定force参数。
gbase> create table t3(id int) mirror to vc02 force;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your GBase server version for the right syntax to use near 'force' at line 1
gbase>
需要先创建本地表,然后使用alter进行镜像。参看下一节
目标镜像表已存在,已存在表
通过force参数,强行将目标表重建。如果已经存在数据会造成丢失。
gbase> create table t3(id int, name varchar(100));
Query OK, 0 rows affected (Elapsed: 00:00:00.09)
gbase> show create table vc01.testdb.t3;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t3 | CREATE TABLE "t3" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> show create table vc02.testdb.t3;
+-------+-------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------+
| t3 | CREATE TABLE "t3" (
"id" int(11) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> alter table t3 create mirror to vc02 force;
Query OK, 0 rows affected (Elapsed: 00:00:00.33)
gbase> show create table vc01.testdb.t3;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t3 | CREATE TABLE "t3" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
gbase> show create table vc02.testdb.t3;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
| t3 | CREATE TABLE "t3" (
"id" int(11) DEFAULT NULL,
"name" varchar(100) DEFAULT NULL
) ENGINE=EXPRESS DEFAULT CHARSET=utf8 TABLESPACE='sys_tablespace' |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (Elapsed: 00:00:00.00)
镜像表总结
镜像表要求目标表不能存在。
如果强行重建(force),请注意数据安全,一旦用了force,有数据丢失风险。
镜像功能测试
通过如下测试,确认镜像功能是双向的。
vc01到vc02
在vc01里数据变动,可以在vc02里看到。
gbase> select * from vc01.testdb.t4;
Empty set (Elapsed: 00:00:00.00)
gbase> select * from vc02.testdb.t4;
Empty set (Elapsed: 00:00:00.01)
gbase> insert into vc01.testdb.t4 values(1,'First');
Query OK, 1 row affected (Elapsed: 00:00:00.11)
gbase> select * from vc01.testdb.t4;
+------+-------+
| id | name |
+------+-------+
| 1 | First |
+------+-------+
1 row in set (Elapsed: 00:00:00.01)
gbase> select * from vc02.testdb.t4;
+------+-------+
| id | name |
+------+-------+
| 1 | First |
+------+-------+
1 row in set (Elapsed: 00:00:00.00)
vc02到vc01
在vc02里数据变动,可以立即在vc01里看到。
gbase> insert into vc02.testdb.t4 values(2,'vc02.Second');
Query OK, 1 row affected (Elapsed: 00:00:00.11)
gbase> select * from vc01.testdb.t4;
+------+-------------+
| id | name |
+------+-------------+
| 1 | First |
| 2 | vc02.Second |
+------+-------------+
2 rows in set (Elapsed: 00:00:00.01)
gbase> select * from vc02.testdb.t4;
+------+-------------+
| id | name |
+------+-------------+
| 1 | First |
| 2 | vc02.Second |
+------+-------------+
2 rows in set (Elapsed: 00:00:00.00)
业务方案
通过镜像功能,可以实现一些业务上的优化,满足特定的业务需求。
平等双活
由于镜像功能保持数据实时同步,可以做到实时的主备双活。
在考虑到数据同步是双向的,而不是常见主备集群的单向,可以实行平等双活。也就是业务可以将负载均衡到任意一个镜像集群上,可以实现更高的高可用。
读写分离方案
利用主备同步机制,将复杂、耗时的计算放在vc01,然后集群通过内部机制,只将处理的结果同步到镜像集群vc02。这样在vc02专注于提供更高的并发查询性能,实现读写分离。
快速集群迁移或搬迁
集群要迁移到其它地方,比如搬迁机房。如果直接搬迁硬件服务器,会导致期间服务中断。此时可以在新机房扩容一个VC,然后扩容那个,将管理、调度节点扩容到新机房,再通过缩容的方式,将老机房的vc和管理调度节点下线。
该方案的管理调度服务扩容缩容时会有相对短暂的时间停服务,相比于搬迁服务器,上架,调试的时间,可以极大缩短迁移耗时。
总结
GBase 8a通过镜像表功能,实现数据的实时同步传输,是实时双活和读写分离的最佳方案。
如对实时性要求不高,比如每天同步一次即可,或者2套集群不在一个机房或网络带宽不足,可以考虑跨集群的同步方案。