Database 及其 schema
在昆仑数据库中,数据的分布有‘database’, ‘schema’ 和’table’ 、索引、存储过程等 3 个层级。一个 database 里面可以包含任意数量的 schema。
schema 是对表以及其他数据库对象的逻辑分组,每个 database 在创建后默认的 schma 是 public,用户还可以随时按需增加更多 schema。
每个 schema 里面可以创建任意数量的 table。所有对数据库对象比如表、索引、存储过程等的引用,如果没有指定 schema 那么总是使用 public;显式指定(引用)schema 的方法是在各类数据库对象名前面前缀 schema 的名称,例如 schemaName.tableName。连接到昆仑数据库的任何一个计算节点时需要指定 database,该连接只能访问该 database 里面的 schema、表和其他数据库对象,不可以访问其它 database 里面的 schema 和表和其他数据库对象。系统元数据连接到任何 database 都可以访问。
Schema 在 PostgreSQL 和昆仑数据库中也成为 namespace,因为它是对数据库对象的逻辑分组,也就是名字空间。存储 schema 元数据的表叫做 pg_nanamespace. 存储数据库元数据的元数据表叫做 pg_database.
昆仑的每个 database 里面的每个 schema 都在后端所有的存储集群都对应有一个 mysql 的 database 来存储其中所有的表分片。命名的对应规则是计算节点中名为 myDB 的 database 里面名为 mySchema 的 schema,在每一个 mysql 存储集群中的 database 名为 myDB_$$mySchema,这个 myDB.mySchema 里面的任何一个表或者表分区 对应于存储集群的 数据表就存储在某一个存储集群的 myDB$$_mySchema 这个 database 中。每次新建一个 schema 都会自动在当前所有存储集群新建这个 database 和 schema 对应的 database。
用户通过计算节点创建一个不分区的表 myDB.mySchema.t1,那么计算节点自动分配某个存储集群来存储 t1,昆仑会在该存储集群的 myDB_$$mySchema database 中创建同名的表 myDB$$_mySchema.t1 来存储 myDB.mySchema.t1 的数据, 并且昆仑会在计算节点的元数据表中记录下来 t1 的数据存储在 SS1。
这样,读写数据时候,计算节点就可以自动找到 t1 的存储节点,然后与目标节点交互来读写 myDB.mySchema.t1 表的数据。
如果用户创建分区表 t2,那么用户最终还需要创建分区表 t2 的若干个表分区(即分区树的叶子结点。分区表可以多级分区)来存储数据,比如用户需要创建表分区 t20,t21,t22…等,然后这些表分区也会被自动分配到某个后端存储集群。每个叶子结点的处理方式也也类似于单表,计算节点会自动分配存储集群给它来创建同名表。
昆仑的所有 database 的所有 schema 里面的单表或者表分区被自动均匀分布到后端所有存储集群中。 如果需要增加存储集群,那么昆仑会自动搬迁一部分表分片到新的存储集群以便达到均匀分布。
所以,如果用户需要把数据按照业务逻辑分组,并且分组之间的数据还需要做表连接,那么这些分组应该是同一个 database 里面的若干个 schema。同时,用户需要根据其业务和数据的特点,来决定一个表是否需要分区(例如,数据量不大时,比如 10 万行以内,100MB 以内,可以不分区),决定分区的话,选择哪些列作为分区列以及如何分区(有 hash,range,list 三种选项)。并且,决定分区方式以后,每个具体的分区也需要 DBA 去创建好。
由于昆仑数据库不同的 database 之间的数据完全不可以在同一个数据库连接中关联使用,而是完全独立使用,所以我们不建议在一个昆仑数据库集群中创建很多个 database。需要对数据表按业务分隔的话,可以针对每个业务创建若干个 schema,然后在其中创建表。
这样同一个数据库连接中可以同时操作其 database 里面的所有 schema 中的所有的表。 不同的 database 使用不同的昆仑数据库集群,这样做的好处是可以按 database 做计算/存储资源分配,以及按 database 来做数据备份/恢复/回档等管理工作。
存储集群元数据
昆仑数据库集群的每个计算节点的元数据表含有该集群的所有存储集群的元数据信息。
在 pg_shard 表存储这每个存储集群的信息,包括 ID,名称,数据量,分片数量等;而在 pg_shard_node 表则存储这每个存储集群的每个存储节点的元数据信息,包括其 IP,端口,用户名和密码等。
在 pg_class 表中,我们新增了 relshardid 字段,它存储的是这个表/索引/sequence 等对应的数据表所在的存储集群的 ID,这个 ID 就是 pg_shard 表的 ID 列。
计算节点使用这些信息找到每个表和其他数据库对象所在的存储集群和节点的信息,然后连接其节点并与之交互完成数据读写和事务处理。
示例说明
我们使用的昆仑数据库集群由两个存储集群,其详细信息如下:
postgres=# select t1.name, t2.shard_id, t2.ip, t2.port, t2.user_name, t2.passwd from pg_shard t1, pg_shard_node t2 where t2.shard_id=t1.id;
name | shard_id | ip | port | user_name | passwd
--------+----------+-----------+------+-----------+---------
shard1 | 3 | 127.0.0.1 | 4001 | pgx | pgx_pwd
shard2 | 4 | 127.0.0.1 | 4002 | pgx | pgx_pwd
复制代码
在 postgres 这个数据库中有 public 和 benchmarksql 两个 schema,那么 shard1 和 shard2 存储集群上就有 postgres_$$public 和 postgres$$_benchmarksql 两个 database。
分别连接其主节点可以看到:
mysql> select @@port;
+--------+
| @@port |
+--------+
| 4001 |
+--------+
1 row in set (0.00 sec)
mysql> show databases;
+--------------------------+
| Database |
+--------------------------+
| information_schema |
| mysql |
| performance_schema |
| postgres_$$_benchmarksql |
| postgres_$$_public |
| regression_$$_public |
| sys |
+--------------------------+
7 rows in set (0.00 sec)
mysql> select @@port;
+--------+
| @@port |
+--------+
| 4002 |
+--------+
1 row in set (0.00 sec)
mysql> show databases;
+--------------------------+
| Database |
+--------------------------+
| information_schema |
| mysql |
| performance_schema |
| postgres_$$_benchmarksql |
| postgres_$$_public |
| regression_$$_public |
| sys |
+--------------------------+
7 rows in set (0.02 sec)
复制代码
使用如下查询获得每个表所属的 schema(即 nspname 列),其表文件在哪个存储集群(即 relshardid)等信息。然后分别在两个存储集群上看一下这些表或者表分片在存储集群上面对应的表。
select t1.nspname, t2.relname, t2.relshardid, t2.relkind from pg_namespace t1 join pg_class t2 on t1.oid=t2.relnamespace where t2.relshardid != 0 order by t1.nspname;
postgres=# select t1.nspname, t2.relname, t2.relshardid, t2.relkind from pg_namespace t1 join pg_class t2 on t1.oid=t2.relnamespace where t2.relshardid != 0 order by t1.nspname;
nspname | relname | relshardid | relkind
--------------+--------------------+------------+---------
benchmarksql | customer | 4 | r
benchmarksql | hist_id_seq | 3 | S
benchmarksql | history | 4 | r
benchmarksql | oorder | 3 | r
benchmarksql | new_order | 4 | r
benchmarksql | order_line | 3 | r
benchmarksql | stock | 4 | r
benchmarksql | item | 3 | r
benchmarksql | warehouse | 4 | r
benchmarksql | district | 3 | r
public | t101_pkey | 4 | i
public | t102 | 3 | r
public | t102_pkey | 3 | i
public | tt14t | 4 | r
public | scores1 | 4 | r
public | scores1_pkey | 4 | i
public | t1 | 4 | r
public | uv_iocu_tab_a_seq | 4 | S
public | uv_iocu_tab | 4 | r
public | uv_iocu_tab_pkey | 4 | i
public | warehouse2 | 4 | r
public | warehouse2_pkey | 4 | i
public | district2 | 3 | r
public | warehouse1 | 4 | r
public | warehouse1_pkey | 4 | i
public | district1 | 3 | r
public | district1_pkey | 3 | i
public | customer1 | 4 | r
public | customer1_pkey | 4 | i
public | history1 | 3 | r
public | orders1 | 4 | r
public | orders1_pkey | 4 | i
public | new_orders1 | 3 | r
复制代码
。。。。。。由于行数较多,此处省略若干行。。。。。
连接到端口号为 4001 的存储集群主节点,可以看到对应于计算节点的每个 database 和 schema 组合的数据库和其中位于 shard1 的数据表。
mysql> use postgres_$$_benchmarksql
Database changed
mysql> show tables;
+------------------------------------+
| Tables_in_postgres_$$_benchmarksql |
+------------------------------------+
| district |
| item |
| oorder |
| order_line |
+------------------------------------+
4 rows in set (0.01 sec)
mysql> use postgres_$$_public
Database changed
mysql> show tables;
+------------------------------+
| Tables_in_postgres_$$_public |
+------------------------------+
| district1 |
| district2 |
| history1 |
| history2 |
| new_orders1 |
| new_orders2 |
| scores |
| stock1 |
| stock2 |
| t100 |
| t102 |
+------------------------------+
11 rows in set (0.01 sec)
复制代码
连接到端口号为 4002 的存储集群主节点,可以看到对应于计算节点的所有非系统内部使用的 database 和 schema 的组合的数据库和其中位于 shard2 的数据表。
mysql> use postgres_$$_benchmarksql
Database changed
mysql> show tables;
+------------------------------------+
| Tables_in_postgres_$$_benchmarksql |
+------------------------------------+
| customer |
| history |
| new_order |
| stock |
| warehouse |
+------------------------------------+
5 rows in set (0.02 sec)
mysql> use postgres_$$_public
Database changed
mysql> show tables;
+------------------------------+
| Tables_in_postgres_$$_public |
+------------------------------+
| customer1 |
| customer2 |
| item1 |
| item2 |
| order_line1 |
| order_line2 |
| orders1 |
| orders2 |
| scores1 |
| students |
| t1 |
| t101 |
| tt14t |
| uv_iocu_tab |
| warehouse1 |
| warehouse2 |
+------------------------------+
16 rows in set (0.02 sec)
复制代码
表存储机制
分区表的每一个表分区存储在某一个后端存储集群;每个非分区表也存储在某个后端存储集群。这些表在计算节点中有全部元数据以便完成查询优化和执行,不过其数据却存储在存储集群中对应的表中。例如:
t10 是一个分区表,其 3 个分区 t100, t101, t102 分别在 id 为 3,4,3 的分区:
postgres=# \d+ t10;
Table "public.t10"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+----------------------------------+---------+--------------+-------------
a | integer | | not null | "nextval"('t10_a_seq'::regclass) | plain | |
b | integer | | | | plain | |
Partition key: HASH (a)
Indexes:
"t10_pkey" PRIMARY KEY, btree (a)
Partitions: t100 FOR VALUES WITH (modulus 3, remainder 0),
t101 FOR VALUES WITH (modulus 3, remainder 1),
t102 FOR VALUES WITH (modulus 3, remainder 2)
postgres=# select relname, relshardid from pg_class where relname like 't10_' and relkind='r';
relname | relshardid
---------+------------
t100 | 3
t101 | 4
t102 | 3
复制代码
从计算节点查询 t10 可以看到其全部数据,这些行位于其 3 个分区中:
postgres=# select*from t10;
a | b
----+----
2 | 2
4 | 4
6 | 6
8 | 8
15 | 13
3 | 3
7 | 7
10 | 10
13 | 11
14 |
1 | 1
5 | 5
9 | 9
11 |
12 |
(15 rows)
复制代码
依次在每个存储集群中查看 t10 的分区对应的表的定义并查看每个表分片中的数据。可以看到这些表分片中的数据的并集就是在计算节点上通过查询 t10 得到的所有数据行。
mysql> show create table t100;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
| t100 | CREATE TABLE `t100` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> show create table t102;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
| t102 | CREATE TABLE `t102` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select*from t100;
+----+------+
| a | b |
+----+------+
| 2 | 2 |
| 4 | 4 |
| 6 | 6 |
| 8 | 8 |
| 15 | 13 |
+----+------+
5 rows in set (0.00 sec)
mysql> select*from t102;
+----+------+
| a | b |
+----+------+
| 1 | 1 |
| 5 | 5 |
| 9 | 9 |
| 11 | NULL |
| 12 | NULL |
+----+------+
5 rows in set (0.00 sec)
mysql> show create table t101;
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
| t101 | CREATE TABLE `t101` (
`a` int(11) NOT NULL,
`b` int(11) DEFAULT NULL,
PRIMARY KEY (`a`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+-------+------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select*from t101;
+----+------+
| a | b |
+----+------+
| 3 | 3 |
| 7 | 7 |
| 10 | 10 |
| 13 | 11 |
| 14 | NULL |
+----+------+
5 rows in set (0.00 sec)
复制代码
END
昆仑数据库是一个 HTAP NewSQL 分布式数据库管理系统,可以满足用户对海量关系数据的存储管理和利用的全方位需求。应用开发者和 DBA 的使用昆仑数据库的体验与单机 MySQL 和单机 PostgreSQL 几乎完全相同,因为首先昆仑数据库支持 PostgreSQL 和 MySQL 双协议,支持标准 SQL:2011 的 DML 语法和功能以及 PostgreSQL 和 MySQL 对标准 SQL 的扩展。同时,昆仑数据库集群支持水平弹性扩容,数据自动拆分,分布式事务处理和分布式查询处理,健壮的容错容灾能力,完善直观的监测分析告警能力,集群数据备份和恢复等 常用的 DBA 数据管理和操作。所有这些功能无需任何应用系统侧的编码工作,也无需 DBA 人工介入,不停服不影响业务正常运行。昆仑数据库具备全面的 OLAP 数据分析能力,通过了 TPC-H 和 TPC-DS 标准测试集,可以实时分析最新的业务数据,帮助用户发掘出数据的价值。昆仑数据库支持公有云和私有云环境的部署,可以与 docker,k8s 等云基础设施无缝协作,可以轻松搭建云数据库服务。请访问 http://www.zettadb.com/ 获取更多信息并且下载昆仑数据库软件、文档和资料。
KunlunDB 项目已开源
【GitHub:】https://github.com/zettadb
【Gitee:】https://gitee.com/zettadb
评论