PostgreSQL相似搜尋分散式架構設計與實踐-dblink非同步呼叫與多機並行(遠端遊標+記錄UDF例項)
標籤
PostgreSQL , 多表並行 , 多機並行 , dblink , 非同步呼叫 , 相似搜尋
背景
背景請參考如下:
《PostgreSQL 相似搜尋設計與效能 – 地址、QA、POI等文字 毫秒級相似搜尋實踐》
當需要進行相似搜尋的資料量大於單機處理能力時,我們需要水平拆分來提高搜尋能力。
或者可以使用阿里雲的PolarDB for PG的產品(類似ORACLE RAC,支援增加計算節點)。比水平分庫的好處是資料是共享儲存的,不需要拆分。
回到水平分庫的場景,如果我們把資料庫拆成了多個,那麼,如何讓查詢並行起來呢?
用DBLINK非同步呼叫,可以讓查詢並行起來。架構設計如下:
實際上採用DBLINK非同步呼叫實現並行的例子很多:
《PostgreSQL dblink非同步呼叫實現 並行hash分片JOIN – 含資料交、並、差 提速案例》
進入正題,下面是一個DEMO,按部就班的演示如何使用非同步呼叫實現多庫並行相似搜尋。
DEMO
1、我們這裡使用本地的4個DB來代表遠端資料庫,這4個DB完全可以安裝到遠端。這裡只是為了測試方便。
本地庫名:
postgres
遠端庫名:
db0
db1
db2
db3
2、首先需要建立使用者和測試DB
create role test login encrypted password `secret`;
create database db0 with owner test;
create database db1 with owner test;
create database db2 with owner test;
create database db3 with owner test;
3、在本地某個庫中建立dblink外掛
create extension dblink;
4、建立連線遠端庫的SERVER
CREATE SERVER db0 FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr `127.0.0.1`, dbname `db0`);
CREATE SERVER db1 FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr `127.0.0.1`, dbname `db1`);
CREATE SERVER db2 FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr `127.0.0.1`, dbname `db2`);
CREATE SERVER db3 FOREIGN DATA WRAPPER dblink_fdw OPTIONS (hostaddr `127.0.0.1`, dbname `db3`);
5、配置連線遠端庫的使用者密碼(使用者密碼都是遠端庫的,可不是本地的哦,你想用本地使用者連遠端庫,沒門)
CREATE USER MAPPING FOR postgres SERVER db0 OPTIONS (user `test`, password `secret`);
CREATE USER MAPPING FOR postgres SERVER db1 OPTIONS (user `test`, password `secret`);
CREATE USER MAPPING FOR postgres SERVER db2 OPTIONS (user `test`, password `secret`);
CREATE USER MAPPING FOR postgres SERVER db3 OPTIONS (user `test`, password `secret`);
遠端庫操作
在所有遠端庫上建立測試表,灌入測試資料,建立相似搜尋函式。(注意下面的指令碼需要調整好對應排程dbname)
1、必要的外掛(注意下面的指令碼需要調整好對應排程dbname)
c db3 postgres
create extension pg_trgm;
create extension dblink;
2、主表和相似搜尋依賴的索引(注意下面的指令碼需要調整好對應排程dbname)
c db3 test
create unlogged table tbl(id int primary key, info text);
create index idx_tbl_info on tbl using gin (info gin_trgm_ops);
-- alter table tbl set (parallel_workers =64);
3、建立分割槽(本文僅做測試,真正的分割槽表用法請參考: 《PostgreSQL 11 分割槽表用法及增強 – 增加HASH分割槽支援 (hash, range, list)》 )
do language plpgsql $$
declare
begin
for i in 0..63
loop
execute format(`drop table if exists tbl%s `, i);
execute format(`create unlogged table tbl%s (like tbl including all) inherits(tbl)`, i);
-- 提前設定好表級並行度,方便後面做並行測試
-- execute format(`alter table tbl%s set (parallel_workers =64)`, i);
end loop;
end;
$$;
4、建立連線函式
create or replace function conn(
name, -- dblink名字
text -- 連線串,URL
) returns void as $$
declare
begin
perform dblink_connect($1, $2);
return;
exception when others then
return;
end;
$$ language plpgsql strict;
5、建立生成隨機函式的函式
-- 生成隨機漢字串
create or replace function gen_hanzi(int) returns text as $$
declare
res text;
begin
if $1 >=1 then
select string_agg(chr(19968+(random()*20901)::int), ``) into res from generate_series(1,$1);
return res;
end if;
return null;
end;
$$ language plpgsql strict;
6、寫入測試資料,隨機文字(注意下面的指令碼需要調整好對應排程dbname)
do language plpgsql $$
declare
dbname name := `db3`;
begin
for i in 0..63
loop
perform conn(`link`||i, `hostaddr=127.0.0.1 user=test password=secret dbname=`||dbname);
perform dblink_send_query(`link`||i, format(`insert into tbl%s select generate_series(1, 15625), gen_hanzi(64); analyze tbl%s;`, i, i));
end loop;
end;
$$;
7、建立相似搜尋用到的UDF
create or replace function get_res(
text, -- 要按相似搜的文字
int8, -- 限制返回多少條
float4 default 0.3, -- 相似度閾值,低於這個值不再搜搜
float4 default 0.1 -- 相似度遞減步長,直至閾值
) returns setof record as $$
declare
lim float4 := 1;
begin
-- 判定
if not ($3 <= 1 and $3 > 0) then
raise notice `$3 must >0 and <=1`;
return;
end if;
if not ($4 > 0 and $4 < 1) then
raise notice `$4 must >0 and <=1`;
return;
end if;
loop
-- 設定相似度閾值
perform set_limit(lim);
-- 檢視當前閾值下,有沒有相似記錄
perform similarity(info, $1) as sml, * from tbl where info % $1 limit 1;
-- 如果有,則返回N條
if found then
return query select similarity(info, $1) as sml, * from tbl where info % $1 order by sml desc limit $2;
return;
end if;
-- 否則繼續,降低閾值
-- 當閾值小於0.3時,不再降閾值搜尋,認為沒有相似。
if lim < $3 then
return;
else
lim := lim - $4;
end if;
end loop;
end;
$$ language plpgsql strict;
本地庫操作
建立建立遠端連線的函式
create or replace function conn(
name, -- dblink名字
text -- 連線串,URL
) returns void as $$
declare
begin
perform dblink_connect($1, $2);
return;
exception when others then
return;
end;
$$ language plpgsql strict;
返回遊標
1、定義UDF1 – 返回遊標(如果返回記錄數很多,建議使用遊標,因為PLPGSQL是需要等所有記錄都拿到才會開始返回,返回記錄的話RT會較高)
例子
create or replace function get_res_cursor(
text, -- 要按相似搜的文字
int8, -- 限制返回多少條
float4 default 0.3, -- 相似度閾值,低於這個值不再搜搜
float4 default 0.1 -- 相似度遞減步長,直至閾值
) returns setof refcursor as $$
declare
i int := 1;
ref refcursor[];
res refcursor;
dbname name[] := array[`db0`, `db1`, `db2`, `db3`]; -- 定義叢集
db name;
begin
foreach db in array dbname
loop
ref[i] := `link`||i;
res := ref[i];
perform conn(`link`||i, db);
perform dblink_open(`link`||i, `link`||i, format(`select * from get_res(%L, %s, %s, %s) as t(sml real, id int, info text)`, $1, $2, $3, $4));
return next res;
i := i+1;
end loop;
end;
$$ language plpgsql strict;
使用例子
postgres=# begin;
BEGIN
Time: 0.045 ms
postgres=# select * from get_res_cursor(`怮媕苸淏倍椡帪暀虻爴蕩巒讉輶魂馜慮範噞蠭鯀烳渃麠鈽趥剘偣瑴鑪颭蚢佚簀哌內霢擷槧緸褫齈跊甏軙襧漆疅泅睤帍槇驗縐櫺`, 10, 0.1, 0.05);
get_res_cursor
----------------
link1
link2
link3
link4
(4 rows)
Time: 18.624 ms
postgres=# select * from dblink_fetch(`link1`,`link1`,10) as t(sml real, id int, info text);
sml | id | info
-----+----+------
(0 rows)
Time: 219.972 ms
postgres=# select * from dblink_fetch(`link1`,`link1`,10) as t(sml real, id int, info text);
sml | id | info
-----+----+------
(0 rows)
Time: 0.252 ms
postgres=# select * from dblink_fetch(`link2`,`link2`,10) as t(sml real, id int, info text);
sml | id | info
-----+----+------
(0 rows)
Time: 215.891 ms
postgres=# select * from dblink_fetch(`link3`,`link3`,10) as t(sml real, id int, info text);
sml | id | info
-----+----+------
(0 rows)
Time: 215.188 ms
postgres=# select * from dblink_fetch(`link4`,`link4`,10) as t(sml real, id int, info text);
sml | id | info
----------+----+----------------------------------------------------------------------------------------------------------------------------------
0.779412 | 1 | 遞陊怮媕苸淏倍椡帪暀虻爴蕩巒讉輶魂馜慮範噞蠭鯀烳渃麠鈽趥剘偣瑴鑪颭蚢佚簀哌內霢擷槧緸褫齈跊甏軙襧漆疅泅睤帍槇驗縐櫺軑氐洚重銄懟諔
(1 row)
Time: 106.692 ms
返回記錄
1、定義UDF2 – 返回記錄(注意,建議限制返回的條數,因為PLPGSQL是需要等所有記錄都拿到才會開始返回)
例子
create or replace function get_res_record(
text, -- 要按相似搜的文字
int8, -- 限制返回多少條
float4 default 0.3, -- 相似度閾值,低於這個值不再搜搜
float4 default 0.1 -- 相似度遞減步長,直至閾值
) returns setof record as $$
declare
i int;
ref refcursor[];
res refcursor;
dbname name[] := array[`db0`, `db1`, `db2`, `db3`]; -- 定義叢集
db name;
begin
i := 1;
foreach db in array dbname
loop
perform conn(`link`||i, db);
perform 1 from dblink_get_result(`link`||i) as t(sml real, id int, info text);
perform dblink_send_query(`link`||i, format(`select * from get_res(%L, %s, %s, %s) as t(sml real, id int, info text)`, $1, $2, $3, $4));
i := i+1;
end loop;
i := 1;
foreach db in array dbname
loop
return query SELECT * FROM dblink_get_result(`link`||i) as t(sml real, id int, info text);
i := i+1;
end loop;
end;
$$ language plpgsql strict;
使用例子
postgres=# select * from get_res_record(`怮媕苸淏倍椡帪暀虻爴蕩巒讉輶魂馜慮範噞蠭鯀烳渃麠鈽趥剘偣瑴鑪颭蚢佚簀哌內霢擷槧緸褫齈跊甏軙襧漆疅泅睤帍槇驗縐櫺`, 10, 0.77, 0.4) as (sml real, id int, info text);
sml | id | info
----------+----+----------------------------------------------------------------------------------------------------------------------------------
0.779412 | 1 | 遞陊怮媕苸淏倍椡帪暀虻爴蕩巒讉輶魂馜慮範噞蠭鯀烳渃麠鈽趥剘偣瑴鑪颭蚢佚簀哌內霢擷槧緸褫齈跊甏軙襧漆疅泅睤帍槇驗縐櫺軑氐洚重銄懟諔
(1 row)
Time: 32.329 ms
小結
使用本文提到的方法,你就可以將多個PostgreSQL當成一個PostgreSQL來使用,實現並行相似搜尋的線性擴容。
效能指標,詳見:
《PostgreSQL 相似搜尋設計與效能 – 地址、QA、POI等文字 毫秒級相似搜尋實踐》
使用dblink非同步呼叫,實現相似文字搜尋的橫向線性擴充套件,效能不衰減。
參考
《PostgreSQL 相似搜尋設計與效能 – 地址、QA、POI等文字 毫秒級相似搜尋實踐》
《PostgreSQL dblink非同步呼叫實現 並行hash分片JOIN – 含資料交、並、差 提速案例》
https://www.postgresql.org/docs/10/static/dblink.html
相關文章
- 端遊HUD設計實踐與策略
- 遠端開發分散式C#程式設計例項分散式C#程式設計
- 作業幫多雲架構設計與實踐架構
- 餓了麼分散式KV架構與實踐分散式架構
- vivo 服務端監控架構設計與實踐服務端架構
- 分散式搜尋引擎Elasticsearch的架構分析分散式Elasticsearch架構
- 美團搜尋多業務商品排序探索與實踐排序
- 分散式架構的監控與指標分散式架構指標
- 搜尋引擎分散式系統思考實踐分散式
- 程式與執行緒、同步與非同步、阻塞與非阻塞、併發與並行執行緒非同步並行
- hive編寫udf實踐記錄Hive
- 分散式系統:遠端呼叫分散式
- 分散式搜尋系統的設計分散式
- 面向微服務架構設計理念與實踐微服務架構
- DAOS 分散式非同步物件儲存|架構設計分散式非同步物件架構
- PostgreSQL家譜、族譜類應用實踐-圖式關係儲存與搜尋SQL
- 前端與後端分離的架構例項(二)前端後端架構
- 前端與後端分離的架構例項(三)前端後端架構
- 前端與後端分離的架構例項(一)前端後端架構
- 一個前端與後端分離的架構例項前端後端架構
- Vue 專案架構設計與工程化實踐Vue架構
- vivo 全球商城:商品系統架構設計與實踐架構
- 非同步與並行~大話目錄非同步並行
- 分散式 PostgreSQL - Citus 架構及概念分散式SQL架構
- 分散式 PostgreSQL - Citus 架構及概念分散式SQL架構
- 愛奇藝逗芽表情搜尋分析與實踐
- bbossaop遠端服務介紹-遠端服務呼叫例項
- Python並行程式設計(二):多執行緒鎖機制利用Lock與RLock實現執行緒同步Python並行行程程式設計執行緒
- 探索Redis設計與實現13:Redis叢集機制及一個Redis架構演進例項Redis架構
- 架構與設計架構
- 幾種分散式呼叫鏈監控元件的實踐與比較(一)實踐分散式元件
- RPC框架/高效能遠端同步呼叫框架/分散式服務框架RPC框架分散式
- 智慧搜尋模型預估框架Augur的建設與實踐模型框架
- vivo全球商城:庫存系統架構設計與實踐架構
- 【搜尋引擎】 PostgreSQL 10 實時全文檢索和分詞、相似搜尋、模糊匹配實現類似Google搜尋自動提示SQL分詞Go
- 拜託,面試請不要再問我分散式搜尋引擎的架構原理!【石杉的架構筆記】面試分散式架構筆記
- 黃東旭:Cloud-Native 的分散式資料庫架構與實踐Cloud分散式資料庫架構
- 多利熊基於分散式架構實踐穩定性建設分散式架構