开发者学堂课程【PostgreSQL 快速入门:14 PostgreSQL 表级复制-Londiste3哈希数据分区复制】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/16/detail/73
14 PostgreSQL 表级复制-Londiste3哈希数据分区复制
目录
一、知识讲解
二、重点回顾
本节课讲 Londiste3的数据分区。
一、知识讲解
数据分区指的是把一个数据库的数据复制,分发给下面多个子节点,相当于在一个表上的消息,比如说是被三个或者四个,两个或者四个 consumer 去消费,但是每一个节点,每一个 consumer 消费只是这个队列里面的部分数据,并且没有任何的重叠,然后这四个或者两个这样的消费者,他们所有的消费的消息加起来就是完整的消息队列,也就是要把一个表通过 Londiste 哈希成多个表,然后分发到不同的 consumer 里面去。
Londiste 里面,有一个这样的例子,可以从 doc/howto 里面找到,叫做 partitioning howto,意思就是如何来做分区。
这里实际上相当于用到了 Londiste 提供的 handler。
这个 handler 相当于在数据库里面要创建叫 partconf 的 schema,schema 里面需要用到一些配置文件,这些配置文件里面会表明里面有几个分区节点等,这些配置在 partconf 里面,比如在1号节点,它的配置的表结构如下。
CREATE SCHEMA partconf;
CREATE TABLE partconf.conf(
part_nr integer,
max _part integer, //
总共有多少个部分要去从消息
队列
里面取数据
db_code bigint,
is _primary boolean,
max_slot integer,
cluster_name text
);
还要用到哈希函数以及 handler,handler 也是 python 写的对数据做分区的 handler。handler 里面要用到的是哈希函数和关键字段。也就是这个表通过什么字段来做哈希。
p
sql
rootdb
<
/usr/share/postzresq
l
/8.4/contrib/hashlib.sql
ps
q
l sharddb 0</usr/share/postgresq1/8.4/contrib/hashlib.sql
psql sharddb_1</usr/share/postgresq1/8.4/contrib/hashlib.sol
接下来看一个例子:
Run command the following commands :
---
londiste3
st3partsplit/st3
_
rootdb
.
ini add-table
p
g
bench
_
accounts
--
handler=part-handler-arg=key=aid
londiste3 st3partsplit/st3_sharddb
_0
.ini add-table
pgbench
_
accounts--create --handler=part --handler-arg=key=aid
londiste3
st3partsplit/st3
_
sharddb
_
1
.
ini
add
--
table
Pgbench
_
accounts
--create
--handler=part
--handler-arg=key=aid
这个例子里面,相当于对 pgbench_accounts 这个表,它选择了 handler 叫 part,也就说这个表相当于是一对多的就是数据拆分的复制方法,就用到了 handler=part,handler的参数就是可以指定是根据哪个 key 值来做哈希分区的,对于两个订阅节点,它的参数也是一样的,create 表示在这个节点上面有没有创建表,通过主节点直接把它的ddl 复制过来,如果创建好了,就不需要加--create 这样的参数。arg可以指定多个,数据分区的 shaving 对应的python 的源代码是在这个地方(如下图)。
这里面会有介绍,比如参数有 key 参数或者 hash_key,hash_key 会覆盖 key 的参数,还有哈希函数,默认哈希函数是放在 partconf 里面的。同时 partconf 里面还有一个 conf 表,也就是说本地的哈希值以及 mask 都是从这个表里面加载的。
这个表也就是我们创建好的那个表,它是要查询 part_nr 和 max_part。
往 load_shard_info 里面加数据的时候需要用到这两个字段的内容。
下面来举一个例子,做一下测试。现在相当于已经有四个挂载的节点,其中 schema2用的名字不一样,要把它改一下,先把这个 schema2这个节点删掉,使用 drop_node 把它删掉,然后要调整它们的顺序,所有的节点都要从主节点取出,现在的主节点是 dst4_digoa1_04,先切换一下(不切换也可以),这样做的目的是使整个的环境更清晰一些。
切换之后的状态:
现在主节点是 src_digoal_01,剩下几个相当于是一级级下来的。如果做数据分区,总表放在这里,然后有四个分区表放在另外的地方。
这里相当于只做三个分区,当然做2的n次方的分区是最好的,所以之后还会加dst2进来。
先把环境部署好,dst3也要直接挂到下面,
首先把dst3挂过来,把 provider 改掉,先找到 change-provider,
然后再看一下它的状态,
然后还要把dst1的 provider 改掉。
改完之后的状态:
现在所有节点都从可以从主节点中取出,然后要加 dst2,要把配置文件改一下,因为 schema 名用得不对,schema名不对,后面会带来一些不必要的麻烦。
dst2放在3上,注意我们用到的 schema 都不需要废弃了,
直接通过用户区链,在这里创建 schema 就可以了。
现在 schema 已经存在了,
现在在这个 schema 里面去创建就可以了。
通过 digoal_01去创建,接下来要把表创建起来,字段都弄成一样的,表之前创建过,直接复制过来,初始化不用讲。
索引用户表和绘画表两个主键一定要加上。
alter table user_info add constraint pk_user_info primary key
(userid);
alter table user_session add constraint pk_user_session
primary key (userid);
函数可加可不加。
然后把这个加进来,
现在就可以了,
可以看到 dst2的 worker 已经不在了,因为已经把它 drop 掉了,所以现在要重新来创建它,create 叶子节点或者是branch 节点都可以,就创建一个 branch 节点,
pg93Cdb-172-16-3-150->londiste3-v/home/pg93/londiste3/dst2_
digoal_02.ini create-branch dst2_digoal_02 "host=172.16.3.39
port-5432
user=postgres dbname=digoal_02 password-postgres"
--provider-" host=172.16.3.150 port=1921 user=postgres
dbname=digoal_01 password=postgres"
加进来之后看一下它的状态,可以看到 dst2已经加进来了。
现在里面还没有加任何的表,但是之后会用新的表。这个新的表会作为一个总表去做数据的分区,分发给四个节点,就相当于总表在主节点,下面跟这个表是一样的,但是相当于用的都只有部分数据。
接下来还要把 worker 建成。
接下来就可以在主库里面添加一个测试表,把它改名为 user account,
digoal 01=> create table user
_
account
<
id int primary key, info
text,crt_tine timestamp>;
所有的节点里面都把这个表创建在 schema 下面。
在01和02上创建,
在03和04上创建,
所有表创建好之后,看一下 howto 里面是怎么做的。
这里面要用到哈希函数,在 postgres 里面本身是带一个哈希函数的,比如hashtext就是传入一个 text,输出一个integer。但是函数在不同的 postgres 版本里面可能算法会不一样,也就是说同一个 text 得到的 hashtext 可能不一样,得到的 integer 结果可能不一样,这样就会带来问题。比如储存环境可能不是同一版本或者说未来迁移了,做了数据迁移或者版本升级,哈希函数就会变。比如说原来出的值是1,但是更新版本或者另外的版本出的值是2或者3,,这样会导致分区产生问题。所以这里就不能用postgres自带的哈希函数,它自带了很多哈希函数,比如hashtext,很多从里面取的哈希值就不能用了,要用不会变的,所有的数据库的哈希算法,都要让它一样。所以,就要用到这个 hashlib 这个哈希库。
下载地址:https://github.com/markikr/pghashlib
这个哈希库其实就是 Londiste 的作者 github 里面的哈希库。
把它通过 git 下载过来,
还有33上也把它克隆过来。
3-150上也要把它克隆过来。
然后就是安装,分别把它安装到对应的数据库的目录里面。
比如这里的软件是安装在 opt/pgsql9.3.2下面的,3.150安装在另外一个地方。
然后看一下 pghashlib 的 Makefile,看一下它的 README。
之后 make,
make install 就可以了,
这里显示 command not found 说明要生成的帮助文件没有生成,但是没有关系,可以不用管它。
so 文件在就可以了。
报错之后命令没有执行下去。
谷歌一下 rst2html 这个命令在哪里使用,它用到了python 里面的东西,要去 python 里面下载它。
因为它提供的函数,我们在 make 它的时候,会有很多错误。”le32toh”
比如这两个函数没有定义,在创建它的时候,即使把控制文件以及 SQL 里面的文件拷贝进去,然后去创建 extension的时候,它也会报未定义符号的错误。Le64toh
就相当于这个函数没有定义,没有把头文件进来。这个函数在 endian.h这个头文件里有定义,但是在5.6的 redhat 这个版本里面是没有的。但是其他的一些文件里面可能会包含这个,比如 src 里面,需要修改一下对应的源码。
#include "/opt/soft_bak/skytools/lib/usual/endian.h"
把头文件包进来。Usual/base
这里又包了 usual/base.h,加个东西把它给包进去。
去掉在这里的注释。
这里的 include 修改一下。
再把这些拷贝过去。
拷贝到 extension 的目录。
发现 li64th 还是显示未定义,没有办法就不用 hashlib 了,它目前还存在一些问题。
这个是6.4的节点,按照 hashlib 看一下在这上面能不能用。
这里没有报错,只是报缺少 rst2html,这个没关系。
在6的版本里面可以创建 hashlib,但在5的里面,因为缺少很多东西,最后没有创建成功,包括缺少 endian.h的头文件。
比如在这里 endian.h里面就包含了它用到的那些函数的定义。如果把它拷过来试一下行不行。
然后把需要改动的地方都改掉。
因为它涉及的东西太多了,相当于整个5的里面的一些头文件就不对了,有可能它的开发环境就是在6或者是以上版本里面去做的,所以我们也就不用它这个 extension 了。
但是大家也要注意到我们为什么要去使用这种统一的哈希库,就是因为 Postgre 本身的哈希函数可能会变更。现在的环境都是在9.3里面的,所以这里就不涉及可以使用 Postgre 本身的哈希函数也没有问题。现在就放弃使用它这里提供的额外的哈希库,但是如果是16的环境,就可以去用它。因为这里不适用这个环境,所以就不用它了。这里照样使用Postgre 统一的哈希函数。原因就是 Postgre 可能跨版本会做一个变更,比如说8.3到8.4,已经把哈希的算法改掉了,那得到的值可能不一样,比如 hashtext 这个函数。
如果要使用额外的函数,就需要去创建这些哈希库。因为这里不用,所以就不需要去创建了。
接下来就要创建这些 schema 和这个裸函数。这个裸函数就是要用到的算法的函数,当然不创建也可以,在加表的时候指定它。我们只需要创建这个 schema 和对应的表就可以。
CREATE SCHEMA partconf;
CREATE TABLE partconf.conf
<
part_nr integer,
max_part integer,
db_code bigint.
is_primary boolean, max_slot integer,
cluster_name text
>;
在所有的库里面去执行。然后在 shard 库里面插入一条数据就指定这个 shard 对应的 part number,以及它的 max的 part number。
先到主节点,去创建这个 schema 和对应的表,然后还有其他的节点。
接下来就要往节点中插一些数据,在主节点里面不用插,只要在子节点里插。
比如2号节点插1,1号节点插0,3号节点插2,4号节点插3。配置文件没有变化,跟原来的都是一样的,只是在添加表的时候有一些变化。
包括这个创建根节点跟原来的创建根节点的方式也是一样的,其中 worker 进程也是一样的。唯一不同的是在添加表的时候也不同。
这些是有不同的,这些相当于它后面指定了 handler 是 part,handler 的 argument。argument 在源代码里面也已经看过了,回到 python 里看一下有哪些。
[rootedb-172-16-3-150 skytools]# cd python/
下面都是 shard 的参数,下面两个都可以不用指定,只需要指定上面,其实只需要指定两个,一个是 hash_key,一个是 hash 函数,hash 函数默认是 partconf.get_hash_raw,也就是在这里创建的一个函数,当然可以不用这个默认的,自定义也可以。
还有就是 hash_key,key 相当于就是传给这个函数,然后返回 hash 值,这里如果要用 Postgre 自身的函数就跟这个有关了,因为这是 int 类型的,根据这个 user_a 这个表的 id 去做一个 hash,它是 int 类型,所以要用到的是hashint,返回一个int。
这个 hashint4就可以。
这个函数是 pg_catalog 里面的。
接下来相当于是要加表了。
digoal_01=> \q
pg93Cdb-172-16-3-150-> londiste3 -v
/home/pg93/londiste3/src_di goal_01.ini add-table
digoal_01.user_account --handler-part --handler
-arg="hash_key=id hashfunc=hashint4
”
要指定 account,还要指定 hash_key,key=id,然后再指定
hashfunc=hashint4。这样表就算加进去了。
先把所有的都加上去,包括几个节点的也加上去。
都加完之后,来看一下 tables。
Dst2相当于加了这样的一个 tables,在这里指定了哈希函数,现在状态 none,接下来往里面添加一些数据。
添加一万条数据。但是后面有报错。
也就是说刚刚在添加的时候,其实这个语法解出来就有问题,这里相就要改一下,改之前先看一下怎么去改 handler,如果没有就只能把它删掉重新添加。使用 change handler 尝试修改一下。
修改后有 public,还是不行,因为它的函数是 pg_catalog。加上 pg_catalog 就对了。然后把所有的都改掉。
现在 tables 就对了,看一下 status 也正常了。
但是后面又报错,跟之前是同一个地方,需要看一下源库的日志。发现刚才修改的时候漏掉了源库。改完之后看一下dst2的 tables的状态。总共插入了一万条记录,到各个节点去看一下有多少记录。
01下面有2525条,
02下面有2441条。
04下面有2520条。
03下面有2514条。
总共加起来等于一万条。也就是说这次的分区成功了,就相当于是把一个大的数据表拆分成了多个的数据表,拆分到了四个库里面去。这种场景一般可以用于比如原来的数据库是集中管理的,现在要做成分布式数据库的情况下,可以用这种方法去做数据的拆分,这个对于业务的影响是很小的,几乎没有影响,并且这个也是实时的增量在做的,当我们在做割接的时候,就可以使业务中断时间很短,不需要中断太长的时间就可以把这个事情干了。然后它还可以做扩容,比如原来是四个节点,要扩成八个节点,可以在这四个节点上面去做这个 partition 的 Londiste 的复制,复制到下面两个子节点上面。
再举个例子,先来 compare 一下数据是不是完整的,连到圆上面去做 compare。
因为圆这里现在相当于有五个表,其中有四个表是前面讲集联复制以及简单复制里面的。user_info、user_session、user_login_rec、user_logout_rec,后来讲这个数据 split 的时候,又加了一个表,叫 user_account,来看一下它的数据。check table 这里报了一个错误,
这可能是 syncing 本身的 bug,如果遇到这种情况,就没法去做 compare。例子里面它是相当于是连到 shard 里面去做的,我们也按照例子去做。但是因为本来就要简单表,所以要看一下简单表和 shard 表同时存在的情况下会不会报错。
这里是因为每个节点上面有 hash 函数,组里面也有 hash 函数,它们作比较的时候。相当于在组的里面,它也会去调这个 hash 函数。也就是源节点也去调这个 hash 函数,它取出来也只有2525条结果,这样相当于是可以比较出来,每一个 shard 节点和主节点的部分数据是不是完整的。也就是说 compare 的时候,是要按照 shard 的节点去做的,对于这种情况,key 的选择是比较重要的问题。
比如这里的 key 用到的是用户 ID,user_account 的 ID,这个 ID 是不会变的。如果这个 I D 发生变化,会怎么样?
可以看出 ID 变更的时候,会被告知 pk 是不允许变更的。这是因为它上面有一个触发器,它不允许你对这个 key 去做变更,否则分区就会出现异常,相当于原来这个 I D 的哈希值,比如说分到 partition-1。然后你变更成这个负一之后,它的分区变成了 partition-2,去执行的时候不知道到底是哪个节点执行,这个就比较复杂了。因为它这里没有做跨库的事务,相当于要把 partition-1里面的数据 delete 掉,同时,又要把这个变更后的数据插入到这个 partition-2里面去。
在 Londiste 里面目前没有实现跨库的这一块功能,所以它干脆就直接把分区件的 update 干掉,不允许装 update。
比如说这里,触发器是来自 logutriga.c,这个就是 Londiste 里面的触发器。
二、重点回顾
1、Hashlib:postgre 的哈希函数,比如之前用到的 hashint4,这个函数可能在不同的版本会发生一些改变,比如说传入同一个数字,可能出来的哈希值不一样,这样会导致整个 partition 的环境出现问题,所以 partition 要所有的节点,包括主节点,也就 src 这个节点,以及所有的复制节点,它们的哈希函数算法一定要一样,就传入同一个词,出来的结果一定要一样,如果不一样,就会出现问题。所以尽量不要去使用 postgre 本身提供的哈希函数,除非所有的环境里面,你能够确保它的函数是没有变化的,比如说你用的都是同一个数据库版本,那么每个哈希函数出来的结果值肯定都是一样的。如果有跨版本,比如说从8.3或者8.4复制到9.3,像这种环境当中,可能主库用的是8.3的版本,几个分区库用的是9.4或者9.3的版本,那这里面的一些哈希函数就不能保证它的算法完全一致,像这种情况可能就要用到第三方的哈希库,我们可以自己去写这个哈希库,或者是用这个网上提供哈希库hashlib,但是这个hashlib前面我们在安装的时候也发现了在5.X的那个版本里面安装的会有问题,就是会报缺少一些头文件,这些头文件在5.X的版本里面是没有的,但是在6的版本里面是有的,就是说在6点几的版本里面是可以安装它的,但是如果你的环境当中有5,就不要使用这个了。要自己去想办法来弥补这个问题。
2、统一所有节点的哈希算法:
3、Schema:这个 partition 的 Schema 相当于是 Schema 写死的,一定要用这个 Schema,它的配置文件是放在conf 表里。
可以在 conf 表里面写一些东西,比如节点的含义等,这个表里面相当于是每个 shard 节点,也就是分区节点,它存储的两个字段是必须要有的,一个是 part_number,另一个就是 max_part ,总共的part数量。
也就说通过这个哈希函数,相当于是得到了总共两个节点。通过哈希函数算出来的得到的这个值就不会有问题。
4、配置都是一样的,只有在添加表的时候不一样,添加表的时候要指定是指定 handler 是用 part(这个是写死的),然后还要指定它的 key,hashkey 就是添加的这个表,用哪个字段来做分区,比如个例子里面用的是 user ID,就是这个表 user_account 的 ID 字段去做分区的,这里用到的哈希函数是我们自己指定的,不是用默认的,如果是默认的,它用的是 schema下面的 get_hash_raw 这个函数。当我们自己去指定函数的时候,需要写多个 handler arg。
5、再回顾一下添加表的命令,因为之前添加错了,所以后面可以通过 change_handler 去改它。
比如说这里指定了 hash_key 是 id,然后再加 arg,它的哈希函数是 pg_catalog 下面的 hashint4这个函数。这个 key 相当于作为这个函数的参数传给它,所以说这个 id 如果 int4,那这个函数的参数也是 int4。弄浩之后,就可以做一些复制了,复制相当于是回落到各自的节点里面去,就相当于数据会帮忙通过哈希函数去做分区,分区到不同的节点里面去,哈希的节点的个数尽量保持2的n次方,这样对以后扩展是比较方便的。