PostgreSQL 基于 inherits 實(shí)現(xiàn)分表的示例代碼
背景
生產(chǎn)環(huán)境有一張 sales_order_full 表數(shù)據(jù)量達(dá)到 3.3 億. 存儲(chǔ)了公司4個(gè)大區(qū)全量銷售數(shù)據(jù).并且數(shù)據(jù)量還在持續(xù)增長. 數(shù)據(jù)量過大對業(yè)務(wù)功能讀寫都造成極大的 IO 壓力.為保證系統(tǒng)穩(wěn)定,團(tuán)隊(duì)制定了短期的計(jì)劃,對 sales_order_full 表按大區(qū)進(jìn)行拆分.來減輕單表壓力.
首先你需要了解 PostgreSQL 的 inherits 技術(shù)
在 PostgreSQL 中,INHERITANCE 是一種特性,允許一個(gè)表從另一個(gè)表繼承屬性(如列、約束等)。這意味著,如果一個(gè)表繼承自另一個(gè)表,它將自動(dòng)獲得所有父表的列和約束。這對于簡化數(shù)據(jù)庫設(shè)計(jì),特別是當(dāng)你想要在不同的層次中共享相同結(jié)構(gòu)的數(shù)據(jù)時(shí)非常有用。 (解釋來自百度)
分析訂單 Full 表
CREATE TABLE "public"."sales_order_full" (
"id" int8 NOT NULL DEFAULT nextval('sales_order_mixed_id_seq'::regclass),
"version_number varchar",
"order_id" varchar(298),
"country_cd varchar(29)",
"product_no" varchar(35),
"serial_no" varchar(273),
"qty" int4,
"fiscal_qtr",
"order_dt" time,
PRIMARY KEY ("order_id")
)
- ID: 自增索引序列.當(dāng)前索引序列值已很大
- order_id: 訂單ID,唯一主鍵
- country_cd: 通過country_cd 從 sales_geo_config 表中匹配所屬大區(qū) (歷史原因?qū)е?.
拆分思路
- 重建 sales_order_full 表
- 放棄 sales_order_mixed_id_seq 重建 id 索引.
- full 增加區(qū)域字段.便于數(shù)據(jù)分表
- 初始化子表并初始化數(shù)據(jù)
- 代碼邏輯調(diào)整
定義分表
| sales_order_full [主表] | 只做請求轉(zhuǎn)發(fā).不存儲(chǔ)任何數(shù)據(jù) |
| ales_order_full_cp | 存儲(chǔ) geo = CP 區(qū)域數(shù)據(jù) |
| sales_order_full_hd | 存儲(chǔ) geo = HD 區(qū)域數(shù)據(jù) |
| sales_order_full_cy | 存儲(chǔ) geo = CY 區(qū)域數(shù)據(jù) |
| sales_order_full_sy | 存儲(chǔ) geo = SY 區(qū)域數(shù)據(jù) |
| sales_order_full_unkonw | 兜底未匹配出區(qū)域的數(shù)據(jù) |
前期工作
原 sales_order_full 表改名為 sales_order_full_all_data_bak
第一步:創(chuàng)建新 Full 表
CREATE TABLE "public"."sales_order_full" (
"id" bigserial, -- bigserial 會(huì)自動(dòng)創(chuàng)建名為 sales_order_full_id_seq 自增索引
"version_number varchar",
"order_id" varchar(298),
"country_cd varchar(29)",
"product_no" varchar(35),
"serial_no" varchar(273),
"qty" int4,
"fiscal_qtr",
"geo", --所屬大區(qū)
"order_dt" time,
PRIMARY KEY ("order_id")
)
第二步: 創(chuàng)建子表
因?yàn)榉直磔^少.沒有必要?jiǎng)討B(tài)創(chuàng)建.統(tǒng)一進(jìn)行分表初始化即可.
inherits 方式創(chuàng)建的子表. 索引需要重新設(shè)置.
子表中 id 字段會(huì)復(fù)用主表中的 ID 自增索引.
CHECK(geo = 'CP') 意思是對 geo 字段進(jìn)行約束.只能存儲(chǔ) CP 大區(qū)數(shù)據(jù).
-- 創(chuàng)建子表,并設(shè)置 full 表的父子關(guān)系
create table IF NOT EXISTS "sales_order_full_cp" (CHECK(geo = 'CP'))
inherits (sales_order_full);
-- 設(shè)置主鍵信息
ALTER TABLE "sales_order_full_cp" ADD CONSTRAINT "sales_order_full_cp_pkey"
PRIMARY KEY (order_id);
-- 設(shè)置對應(yīng)索引
CREATE INDEX "sales_order_full_cp_mixed_idx" ON "public"."sales_order_full_cp"
USING btree (
"fiscal_qtr",
"order_id",
"country_cd"
);
第三步:數(shù)據(jù)流轉(zhuǎn)子表
處理明確 Geo 的數(shù)據(jù)
insert into sales_order_full_cp (order_id,geo,country_cd,product_no,serial_no,qty,fiscal_qtr,order_dt,version_number) select -- 注意 geo 字段取自 sales_geo_config 表 a.order_id,b.geo,a.country_cd,a.product_no,a.serial_no,a.qty,a.fiscal_qtr,a.order_dt,version_number from sales_order_full_cp_all_data_bak a left join sales_geo_config b on a.country_cd = b.country where b.geo = 'CP';
處理 unkonw 因?yàn)檫@類數(shù)據(jù)沒有明確Geo,所以要設(shè)置默認(rèn)值 'UNKNOWN'
insert into sales_order_full_unknown
(order_id,geo,country_cd,product_no,serial_no,qty,fiscal_qtr,order_dt,versionnumber)
select
-- 注意 geo 直接默認(rèn)
a.order_id,'UNKNOWN',a.country_cd,a.product_no,a.serial_no,a.qty,a.fiscal_qtr,a.order_dt,versionnumber
from
sales_order_full_cp_all_data_bak a
left join sales_geo_config b on a.country_cd = b.country
where b.geo is null or b.geo not in ('HD','CY','SY','CP');
第四步:數(shù)據(jù)對比并清理 sales_order_full_all_data_bak 表
注意這里得 select count(*) from sales_order_full;
再強(qiáng)調(diào)一遍: sales_order_full 表不存儲(chǔ)任何數(shù)據(jù),只做調(diào)用的中轉(zhuǎn)。
通過 Explan 查看執(zhí)行過程(簡略):
-> Parallel Index Only Scan using sales_order_full_unknown -> Parallel Seq Scan on sales_order_full_cp -> Parallel Seq Scan on sales_order_full_cy -> Parallel Seq Scan on sales_order_sy -> Parallel Seq Scan on sales_order_full_hd
可以看到.數(shù)據(jù)來源全部來自5張子表.
select count(*) from sales_order_full; result: 330000000 select count(*) from sales_order_all_data_bak; result: 330000000 -- 結(jié)果相等 直接刪,釋放數(shù)據(jù)庫空間 drop table sales_order_all_data_bak;
第五步:調(diào)整代碼邏輯
- 對 sales_order_full 表查詢操作,要求指定 geo 精確分表信息.
select * from sales_order_full where geo = 'HD' and xx = 'xx' 或 select * from sales_order_full_hd where xx = 'xx'
- 新增修改操作必須精確到具體子表操作.
insert into sales_order_full_cp (order_id,geo,country_cd,product_no,serial_no,qty,fiscal_qtr,order_dt,version_numer) select a.order_id,b.geo,a.country_cd,a.product_no,a.serial_no,a.qty,a.fiscal_qtr,a.order_dt,versionnumber from order_inbound inbound left join sales_geo_config b on inbound.country_cd = b.country where b.geo = 'CP' and inbound.version_number = '001'; update sales_order_full_cp set order_status = 'Closed' where order_id = 'xx';
疑問1:為什么不用觸發(fā)器?
有同事疑問,為何不給 sales_order_full 主表創(chuàng)建觸發(fā)器.這樣數(shù)據(jù)新增就只操作主表.讓主表進(jìn)行轉(zhuǎn)發(fā)處理豈不是更方便.
答案自然否定的.起碼完全不合適我們. 系統(tǒng)訂單全部是批處理的場景.大量數(shù)據(jù)通過觸發(fā)器轉(zhuǎn)發(fā)會(huì)大大降低性能,因?yàn)橛|發(fā)器會(huì)逐行檢查處理.性能損耗無法接受.
疑問2:既然操作都細(xì)化到子表. sales_order_full 還有何用?
1.萬惡的分頁查詢場景.假設(shè)要全區(qū)域分頁查詢.我們只需操作 sales_order_full 表. 由數(shù)據(jù)庫幫我們完成 limit 和 offset . 不需要自己控制 5 張子表的分頁.
2.對子表的結(jié)構(gòu)修改.只需調(diào)整 sales_order_full 表.調(diào)整會(huì)自動(dòng)同步到子表.
疑問3:為何不用成熟分表方案.如 Sharding-JDBC?
數(shù)據(jù)庫自帶的就超好用了.非常輕量.處理只依賴數(shù)據(jù)庫 IO. 不占應(yīng)用內(nèi)存
到此這篇關(guān)于PostgreSQL 基于 inherits 實(shí)現(xiàn)分表的示例代碼的文章就介紹到這了,更多相關(guān)PostgreSQL inherits分表內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!?
相關(guān)文章
PostgreSQL因大量并發(fā)插入導(dǎo)致的主鍵沖突的解決方案
在數(shù)據(jù)庫操作中,并發(fā)插入是一個(gè)常見的場景,然而,當(dāng)大量并發(fā)插入操作同時(shí)進(jìn)行時(shí),可能會(huì)遇到主鍵沖突的問題,本文將深入探討 PostgreSQL 中解決因大量并發(fā)插入導(dǎo)致的主鍵沖突的方法,并通過具體的示例進(jìn)行詳細(xì)說明,需要的朋友可以參考下2024-07-07
PGSQL查詢最近N天的數(shù)據(jù)及SQL語句實(shí)現(xiàn)替換字段內(nèi)容
PostgreSQL提供了WITH語句,允許你構(gòu)造用于查詢的輔助語句,下面這篇文章主要給大家介紹了關(guān)于PGSQL查詢最近N天的數(shù)據(jù)及SQL語句實(shí)現(xiàn)替換字段內(nèi)容的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-03-03
postgreSQL中的內(nèi)連接和外連接實(shí)現(xiàn)操作
這篇文章主要介紹了postgreSQL中的內(nèi)連接和外連接實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-01-01
PostgreSql生產(chǎn)級別數(shù)據(jù)庫安裝要注意事項(xiàng)
這篇文章主要介紹了PostgreSql生產(chǎn)級別數(shù)據(jù)庫安裝要注意事項(xiàng),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-08-08
PostgreSQL利用遞歸優(yōu)化求稀疏列唯一值的方法
這篇文章主要介紹了PostgreSQL利用遞歸優(yōu)化求稀疏列唯一值的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01
PostgreSQL 用戶名大小寫規(guī)則小結(jié)
PostgreSQL默認(rèn)不區(qū)分用戶名大小寫,創(chuàng)建和連接時(shí)自動(dòng)轉(zhuǎn)為小寫,使用雙引號可強(qiáng)制區(qū)分,下面就來介紹一下PostgreSQL 用戶名大小寫規(guī)則,感興趣的可以了解一下2025-06-06
Debian中PostgreSQL數(shù)據(jù)庫安裝配置實(shí)例
這篇文章主要介紹了Debian中PostgreSQL數(shù)據(jù)庫安裝配置實(shí)例,一個(gè)簡明教程,需要的朋友可以參考下2014-06-06
PostgreSQL 實(shí)現(xiàn)將多行合并轉(zhuǎn)為列
這篇文章主要介紹了PostgreSQL 實(shí)現(xiàn)將多行合并轉(zhuǎn)為列的操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12

