dblink(Database Link)数据库链接顾名思义就是数据库的链接,就像电话线一样,是一个通道,当我们要跨本地数据库,访问另外一个数据库表中的数据时,本地数据库中就必须要创建远程数据库的dblink,通过dblink本地数据库可以像访问本地数据库一样访问远程数据库表中的数据。
往往随着系统的不断演进,系统往往会根据需要对系统以及数据库做拆分,在数据库层面单体数据库往往在应对“三高”(高并发、高可用、高效率)问题上捉襟见肘,而这个时候往往会对数据进行拆分,而在拆分数据库的过程中,虽然遵循一定的方法论但是最后呈现出来的剖分方式上可以说天马行空,而在这种天马行空的设计后,当需求发生变化或升级中总会有数据库中的交互,比如子库需要在发生变化时,将子库的一些信息推送到主库中。
而笔者在面临的业务场景时分布式平台中,往往分布式节点(下文中均称呼为NodeDB)中的数据库会发生数据变化,而在数据发生变化对于分布式主库(下文中均称呼为XXLJobDB)需要实时更新统计信息。所以笔者对于此种业务场景的解决方法为在NodeDB中创建一个XXLJobDB的数据连接DBlink。每当NodeDB中数据信息发生变化时,我们将变化信息通过数据连接DBlink推送到XXLJobDB数据库中。
SELECT dblink_connect('xxljobDB','hostaddr=200.200.200.200 port=5432 dbname=xxljob user=postgres password=postgres');
但是直接创建时会发生错误。
这个需要开启postgresql数据库的扩展。
create extension dblink;
--查看pgsql数据库已安装的扩展
select * from pg_extension;
开启扩展之后,再创建dblink既可在当前会话中直接使用。
SELECT dblink_connect('xxljobDB','hostaddr=200.200.200.200 port=5432 dbname=xxljob user=postgres password=postgres');
select * from dblink('xxljobDB','select id from tbtest;') as t1(id int);
select * from dblink('xxljobDB','select id,name from tbtest;') as t1(id int,name varchar);
insert into test1 select * from dblink('test','select id tbtest;') as t1(id int);
select dblink_disconnect('test');
CREATE VIEW view_remote_tb1 AS
select * from
dblink('hostaddr=200.200.200.200 port=5432 dbname=xxljob user=postgres password=postgres'::text,'select * from tbtest'::text)t
(id integer,name character varying));
SELECT dblink_exec('mycoon', 'BEGIN');
SELECT dblink_exec('mycoon', 'COMMIT');
SELECT dblink_exec('mycoon', 'ROLLBACK');
SELECT dblink_exec('mycoon', 'insert into tb1 select generate_series(10,20),''hello''');
create or replace function SyncStat2RemoteDB(out RunState bool )
AS
$BODY$
declare
v_task_info_rec record;
xxljob_dblink_conn text;
begin
RunState=True;
for v_task_info_rec in EXECUTE 'select * from check_task_info' loop
xxljob_dblink_conn:=v_task_info_rec.xxljob_dblink_conn;
EXECUTE 'select dblink_connect(''xxljobDB'','''||xxljob_dblink_conn||''')';
--在这里可以做一些为所欲为的事情...
--在这里可以做一些为所欲为的事情...
SELECT dblink_disconnect('xxljobDB');
Return;
end loop;
exception when others then
SELECT dblink_disconnect('xxljobDB');
raise exception 'error--(%)(%)',sqlerrm,insertSql;
end;
$BODY$
language plpgsql;
postgresql官网地址:https://www.postgresql.org
postgresql推荐书籍:https://www.postgresql.org/docs/books/
pg中的dblink需要创建扩展“create extension dblink;”否则无法使用,当然创建扩展只需要一次即可。
pg中的dblink无法保存,只可以作为会话中的一种连接信息,无法作为像Oracle数据库中连接对象保存在数据库中,即每次使用都需要创建连接,无法直接使用。(笔者使用的pg版本为pg12.5)【如果有更新的版本或者什么方式可以保留下连接dblink对象,希望各位大佬能够不吝赐教】
能否用dblink去连接oracle数据库呢?像oracle的dblink一样,连接SQL Server、MySQL、PostgreSQL?
答案是不行的,源码里面可以看到PostgreSQL的dblink是使用的pg的c语言接口去创建连接的,而不是使用ODBC来创建:
dblink_connect(PG_FUNCTION_ARGS)
{
char *conname_or_str = NULL;
char *connstr = NULL;
char *connname = NULL;
char *msg;
PGconn *conn = NULL;
remoteConn *rconn = NULL;
dblink_init();
if (PG_NARGS() == 2)
{
conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(1));
connname = text_to_cstring(PG_GETARG_TEXT_PP(0));
}
else if (PG_NARGS() == 1)
conname_or_str = text_to_cstring(PG_GETARG_TEXT_PP(0));
if (connname)
rconn = (remoteConn *) MemoryContextAlloc(TopMemoryContext,
sizeof(remoteConn));
/* first check for valid foreign data server */
connstr = get_connect_string(conname_or_str);
if (connstr == NULL)
connstr = conname_or_str;
/* check password in connection string if not superuser */
dblink_connstr_check(connstr);
conn = PQconnectdb(connstr);
if (PQstatus(conn) == CONNECTION_BAD)
{
msg = pchomp(PQerrorMessage(conn));
PQfinish(conn);
if (rconn)
pfree(rconn);
ereport(ERROR,
(errcode(ERRCODE_SQLCLIENT_UNABLE_TO_ESTABLISH_SQLCONNECTION),
errmsg("could not establish connection"),
errdetail_internal("%s", msg)));
}
/* check password actually used if not superuser */
dblink_security_check(conn, rconn);
/* attempt to set client encoding to match server encoding, if needed */
if (PQclientEncoding(conn) != GetDatabaseEncoding())
PQsetClientEncoding(conn, GetDatabaseEncodingName());
if (connname)
{
rconn->conn = conn;
createNewConnection(connname, rconn);
}
else
{
if (pconn->conn)
PQfinish(pconn->conn);
pconn->conn = conn;
}
PG_RETURN_TEXT_P(cstring_to_text("OK"));
}