GitHub地址: https://github.com/QingyaFan/effective-backend
實(shí)際問題
實(shí)際業(yè)務(wù)中镀虐,會(huì)有非常大的地理數(shù)據(jù)集的存儲(chǔ)需求你弦,比如全世界的點(diǎn)狀POI蹬挺,數(shù)據(jù)量級(jí)已達(dá)億級(jí)別,存儲(chǔ)在單一的PostgreSQL數(shù)據(jù)表中,查詢性能會(huì)差很多鹅颊,如果對(duì)這些數(shù)據(jù)進(jìn)行空間分析丁存,復(fù)雜的計(jì)算肩杈,動(dòng)不動(dòng)就耗時(shí)十幾二十分鐘,不能滿足實(shí)時(shí)的要求解寝,因此扩然,我們開始尋找一個(gè)可以解決這個(gè)問題的方案。
要找方案聋伦,我們首先要弄清楚原來的瓶頸在哪里夫偶。為什么存儲(chǔ)在單表中性能差?
- 記錄多觉增,掃描時(shí)間過長兵拢;
- 寫入和更新時(shí)會(huì)產(chǎn)生表級(jí)的鎖,其他的寫入和更新操作只能等待逾礁;
- 單磁盤I/O性能有限说铃。
單表存儲(chǔ)可能存在的問題?
- 單表數(shù)據(jù)是單文件存在的嘹履,數(shù)據(jù)量過大可能會(huì)達(dá)到磁盤存儲(chǔ)單文件的大小瓶頸腻扇。
Citus是什么
首先,Citus
是一個(gè)PostgreSQL的擴(kuò)展砾嫉,類似于PostGIS幼苛、pgRouting,它主要的作用是將一張大表分布到多臺(tái)機(jī)器的數(shù)據(jù)庫中焕刮,并且可已經(jīng)將查詢分布到不同節(jié)點(diǎn)并行執(zhí)行蚓峦,最后匯總結(jié)果。Citus是單coordinator济锄,多worker結(jié)構(gòu)暑椰,coordinator統(tǒng)籌協(xié)調(diào),worker是真正干活的壯丁荐绝。
從Citus官網(wǎng)盜了一張圖來一汽,這張圖說的很清楚,當(dāng)存儲(chǔ)數(shù)據(jù)時(shí),Coordinator節(jié)點(diǎn)會(huì)把當(dāng)前表分為多個(gè)分片(shard)召夹,并將分片安裝某種算法分布到各個(gè)worker節(jié)點(diǎn)岩喷,并記錄一份元數(shù)據(jù),如果開啟了“分片復(fù)制”(Shard Replication)监憎,兩個(gè)不同的節(jié)點(diǎn)中可能會(huì)存在同一分片的不同副本纱意,為了防止單點(diǎn)故障造成的數(shù)據(jù)丟失。當(dāng)一個(gè)針對(duì)分布式表的查詢進(jìn)來鲸阔,Coordinator會(huì)根據(jù)數(shù)據(jù)的分布偷霉,將查詢分別分配到各個(gè)worker節(jié)點(diǎn),最后匯總結(jié)果褐筛。安排的明明白白类少。
什么時(shí)候用Citus!
- 數(shù)據(jù)分布式存儲(chǔ)的應(yīng)用(
Multi-tenant Application
)渔扎,實(shí)測單節(jié)點(diǎn)vs四臺(tái)citus集群入庫20G數(shù)據(jù)有7倍的差距硫狞; - 實(shí)時(shí)分析與統(tǒng)計(jì)(
Real-Time Dashboards
),只適合節(jié)點(diǎn)之間IO非常小的場景晃痴; - 時(shí)間序列數(shù)據(jù)的查詢分析(
Timeseries Data
)残吩,不甚了解。
Citus不是萬金油倘核,啥時(shí)候不要用世剖?
- 各個(gè)worker節(jié)點(diǎn)間有大量的數(shù)據(jù)流動(dòng),I/O過大笤虫,會(huì)將并行執(zhí)行節(jié)省的優(yōu)勢抵消掉旁瘫,反倒不如單節(jié)點(diǎn)高配機(jī)器
Citus的特點(diǎn)
- 從單節(jié)點(diǎn)數(shù)據(jù)庫到Citus集群無需更改應(yīng)用邏輯,無縫集成琼蚯。
當(dāng)你從單節(jié)點(diǎn)切換到Citus集群時(shí)酬凳,你會(huì)發(fā)現(xiàn)這是一件多么容易的事情,不需要更改任何應(yīng)用邏輯遭庶,之前你面對(duì)的是一個(gè)PostgreSQL宁仔,之后你還是面對(duì)的一個(gè)PostgreSQL,不同的是峦睡,之后你有了強(qiáng)大的存儲(chǔ)和算力翎苫,而且這都是由citus的coordinate節(jié)點(diǎn)管理的,不需要我們維護(hù)榨了。
- 并行執(zhí)行查詢提高效率煎谍,且性能隨著節(jié)點(diǎn)數(shù)量增加線性增加。但不適用于需要各節(jié)點(diǎn)匯總數(shù)據(jù)龙屉,涉及大量IO的情景呐粘,這樣反而會(huì)抵消并行執(zhí)行省下的時(shí)間满俗。
Citus通過使用鉤子和擴(kuò)展API來實(shí)現(xiàn)PostgreSQL的分布式存儲(chǔ)和并行計(jì)算,因此很多PostgreSQL擴(kuò)展也能使用citus提供的能力作岖,包括PostGIS唆垃,這就為我們使用Citus集群來管理和分析地理大數(shù)據(jù)提供了可能。但是需要注意痘儡,要配合Citus使用其它擴(kuò)展辕万,Citus必須是第一個(gè)啟用的擴(kuò)展,其次沉删,必須在coordinator和worker節(jié)點(diǎn)都安裝相應(yīng)擴(kuò)展渐尿。所以我們需要在所有機(jī)器節(jié)點(diǎn)上安裝Citus和PostGIS。
嘗試
本示例使用了6臺(tái)機(jī)器做集群丑念,1臺(tái)coordinator涡戳,5臺(tái)worker
- 首先在各個(gè)節(jié)點(diǎn)上安裝PostgreSQL结蟋,安裝Citus擴(kuò)展脯倚,安裝PostGIS擴(kuò)展;
- 修改PostgreSQL的配置嵌屎,啟動(dòng)時(shí)預(yù)加載citus推正,并配置各個(gè)節(jié)點(diǎn)的PostgreSQL互相聯(lián)通,可互相訪問宝惰;
- 啟動(dòng)數(shù)據(jù)庫植榕,啟用擴(kuò)展。
systemctl start postgresql-10.service
systemctl enable postgresql-10.service
psql -U postgers -c "create extension citus;"
psql -U postgers -c "create extension postgis;"
添加worker
在coordinator節(jié)點(diǎn)添加所有worker節(jié)點(diǎn)尼夺,
psql -U postgres -c "select * from master_add_node('ip-or-name', port);"
最后列出所有節(jié)點(diǎn)檢查是否添加成功尊残,
psql -U postgres -c "SELECT * FROM master_get_active_worker_nodes();"
遷移數(shù)據(jù)
遷移現(xiàn)有的數(shù)據(jù)到citus集群
允許服務(wù)間斷的場景:
首先將原來的數(shù)據(jù)備份,將一張表由單表調(diào)整為distributed淤堵,需要在灌數(shù)據(jù)之前聲明寝衫,所以我們需要三個(gè)步驟:首先備份并恢復(fù)表結(jié)構(gòu),然后聲明表為distributed拐邪,最后備份并恢復(fù)數(shù)據(jù)慰毅,此時(shí)數(shù)據(jù)會(huì)分布到各個(gè)worker。
- 備份表結(jié)構(gòu)
pg_dump -Fc --no-owner --schema-only --dbname db_name > db_name_schema.back
pg_restore --dbname db_name db_name_schema.back
- 聲明distributed
select * from create_distributed_table('table_name', 'id')
- 灌數(shù)據(jù)
pg_dump -Fc --no-owner --data-only --dbname db_name > db_name_data.back
pg_restore --dbname db_name db_name_data.back
不允許服務(wù)間斷
可以使用PostgreSQL的logical replication
扎阶,將在用的老數(shù)據(jù)庫指向Citus主節(jié)點(diǎn)汹胃,這樣新老數(shù)據(jù)庫處于同步狀態(tài),然后統(tǒng)一將服務(wù)的數(shù)據(jù)庫連接地址切換到Citus东臀。
測試性能
All things done着饥!接下來進(jìn)行一些簡單的測試。
導(dǎo)入數(shù)據(jù)與數(shù)據(jù)分布
數(shù)據(jù)量: 122608100 個(gè)多邊形惰赋,恢復(fù)數(shù)據(jù)耗時(shí)7分鐘贱勃,每個(gè)節(jié)點(diǎn) 9.1 G 數(shù)據(jù);這些數(shù)據(jù)恢復(fù)到一臺(tái)相同配置單主機(jī)的 PostgreSQL 數(shù)據(jù)庫數(shù)據(jù)量是,耗時(shí) 55分鐘贵扰,28 G 數(shù)據(jù)仇穗。將近8倍的差距,但是我們注意到數(shù)據(jù)是有一定程度的冗余的戚绕,因此citus可以承受單節(jié)點(diǎn)的數(shù)據(jù)丟失纹坐。
緩沖區(qū)操作處理時(shí)間
對(duì)1.2億的數(shù)據(jù)進(jìn)行6米半徑的緩沖區(qū)分析,并將結(jié)果存入數(shù)據(jù)庫
insert into buffer_result (id, the_geom) select id, st_buffer(the_geom, 6) as the_geom from table_name;
citus集群舞丛,56分鐘耘子;單機(jī),120分鐘球切,差距并不明顯谷誓,原因其實(shí)我們上面提到過,計(jì)算結(jié)果需要匯總吨凑,且涉及到了大量的IO捍歪,觀察處理過程注意到,15分鐘計(jì)算完成鸵钝,25分鐘匯總數(shù)據(jù)糙臼,16分鐘寫入結(jié)果,而單機(jī)不需要匯總數(shù)據(jù)恩商。
不涉及匯總的操作
對(duì)1.2億多邊形統(tǒng)計(jì)總節(jié)點(diǎn)數(shù):
select sum(st_npoints(the_geom)) from table_name;
citus集群耗時(shí)1.7秒变逃,而單節(jié)點(diǎn)耗時(shí)50秒,差距很明顯怠堪。
總結(jié)
以上的測試中揽乱,數(shù)據(jù)庫并沒有進(jìn)行很好的調(diào)優(yōu),是比較粗糙的結(jié)果粟矿,但也足以說明問題凰棉。
- 數(shù)據(jù)分布式存儲(chǔ)的應(yīng)用(
Multi-tenant Application
),實(shí)測單節(jié)點(diǎn)vs四臺(tái)citus集群入庫20G數(shù)據(jù)有7倍的差距嚷炉; - 實(shí)時(shí)分析(
Real-Time Dashboards
)渊啰,只適合節(jié)點(diǎn)之間IO非常小的場景。