塊級(ctid)掃描在IoT(物聯網)極限寫和消費讀並存場景的應用
標籤
PostgreSQL , 塊掃描 , 行號掃描 , ctid , tid scan , IoT , 物聯網 , 極限寫入 , 實時消費 , 實時讀 , 堆表 , heap , 時序
背景
在物聯網有一個非常普遍的資料需求,就是資料的寫入,另一個普遍的需求則是資料的消費(按時序讀取),以及流式計算。
關於流式計算,請參考
《(流式、lambda、觸發器)實時處理大比拼 – 物聯網(IoT)金融,時序處理最佳實踐》
《流計算風雲再起 – PostgreSQL攜PipelineDB力挺IoT》
《”物聯網”流式處理應用 – 用PostgreSQL實時處理(萬億每天)》
接下來我們談一談極限寫入和消費。
寫入
從資料儲存結構來看,PostgreSQL的HEAP儲存是非常適合高速寫入的,追加式寫入。以下文章中已得到高速寫入的驗證。
《PostgreSQL 如何瀟灑的處理每天上百TB的資料增量》
塊(時序列)索引
BRIN索引,也被稱為塊索引,是針對資料塊後設資料建立的索引(例如某個自增長欄位,物理儲存和欄位的值存在很好的線性相關性,那麼每個塊的資料區間就具有非常強的獨立性),BRIN索引非常小,對寫入效能的影響可以忽略。
BRIN適合物理儲存和欄位的值存在很好的線性相關性的欄位,例如時序欄位。
或者使用cluster或order 重排後,適合對應欄位。
消費
消費是指非同步的讀取資料,處理資料的過程,例如IoT場景,資料的寫入延遲要求非常低,所以要求寫入吞吐特別大。
而處理方面,則通過消費機制,進行處理。
那麼如何消費呢?
通常可以根據索引進行消費,比如前面提到的BRIN索引,對寫入吞吐的影響小,同時支援=,以及範圍的檢索。如果有時序欄位的話,BRIN是非常好的選擇。
然而並非所有的資料寫入場景都有時序欄位(當然使用者可以新增一個時間欄位來解決這個問題)。當沒有時序欄位時,如何消費效率最高呢?
塊掃描
塊掃描是很好的選擇,前面提到了資料儲存是HEAP,追加形式。
PostgreSQL提供了一種tid scan的掃描方法,告訴資料庫你要搜尋哪個資料塊的哪條記錄。
select * from tbl where ctid=`(100,99)`;
這條SQL指查詢100號資料塊的第100條記錄。
這種掃描效率非常之高,可以配合HEAP儲存,在消費(讀取記錄)時使用。
評估塊記錄數
PostgreSQL暫時沒有提供返回整個資料塊的所有記錄的介面,只能返回某個資料塊的某一條記錄,所以如果我們需要讀取某個資料塊的記錄,需要列舉該資料塊的所有行。
如何評估一個資料塊有多少條記錄,或者最多有多少條記錄?
PAGE layout
https://www.postgresql.org/docs/10/static/storage-page-layout.html
HeapTupleHeaderData Layout
Field | Type | Length | Description |
---|---|---|---|
t_xmin | TransactionId | 4 bytes | |
t_xmax | TransactionId | 4 bytes | delete XID stamp |
t_cid | CommandId | 4 bytes | insert and/or delete CID stamp (overlays with t_xvac) |
t_xvac | TransactionId | 4 bytes | XID for VACUUM operation moving a row version |
t_ctid | ItemPointerData | 6 bytes | current TID of this or newer row version |
t_infomask2 | uint16 | 2 bytes | number of attributes, plus various flag bits |
t_infomask | uint16 | 2 bytes | various flag bits |
t_hoff | uint8 | 1 byte | offset to user data |
Overall Page Layout
Item | Description |
---|---|
PageHeaderData | 24 bytes long. Contains general information about the page, including free space pointers. |
ItemIdData | Array of (offset,length) pairs pointing to the actual items. 4 bytes per item. |
Free space | The unallocated space. New item pointers are allocated from the start of this area, new items from the end. |
Items | The actual items themselves. |
Special space | Index access method specific data. Different methods store different data. Empty in ordinary tables. |
單頁最大記錄數估算
最大記錄數=block_size/(ctid+tuple head)=block_size/(4+27);
postgres=# select current_setting(`block_size`);
current_setting
-----------------
32768
(1 row)
postgres=# select current_setting(`block_size`)::int/31;
?column?
----------
1057
(1 row)
如果需要評估更精確的行數,可以加上欄位的固定長度,變長欄位的頭(4BYTE)。
例子
生成指定block TID的函式
create or replace function gen_tids(blkid int) returns tid[] as $$
select array(
SELECT (`(`||blkid||`,` || s.i || `)`)::tid
FROM generate_series(0,current_setting(`block_size`)::int/31) AS s(i)
) ;
$$ language sql strict immutable;
讀取某個資料塊的記錄
postgres=# create table test(id int);
CREATE TABLE
postgres=# insert into test select generate_series(1,10000);
INSERT 0 10000
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where ctid = any
(
array
(
SELECT (`(0,` || s.i || `)`)::tid
FROM generate_series(0, current_setting(`block_size`)::int/31) AS s(i)
)
);
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------------
Tid Scan on postgres.test (cost=25.03..40.12 rows=10 width=4) (actual time=0.592..0.795 rows=909 loops=1)
Output: test.id
TID Cond: (test.ctid = ANY ($0))
Buffers: shared hit=1057
InitPlan 1 (returns $0)
-> Function Scan on pg_catalog.generate_series s (cost=0.01..25.01 rows=1000 width=6) (actual time=0.087..0.429 rows=1058 loops=1)
Output: (((`(0,`::text || (s.i)::text) || `)`::text))::tid
Function Call: generate_series(0, ((current_setting(`block_size`::text))::integer / 31))
Planning time: 0.106 ms
Execution time: 0.881 ms
(10 rows)
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where ctid = any(gen_tids(1));
Tid Scan on postgres.test (cost=1.32..1598.90 rows=1058 width=4) (actual time=0.026..0.235 rows=909 loops=1)
Output: id
TID Cond: (test.ctid = ANY (`{"(1,0)","(1,1)","(1,2)","(1,3)","(1,4)","(1,5)","(1,6)","(1,7)","(1,8)","(1,9)","(1,10)","(1,11)","(1,12)","(1,13)","(1,14)","(1,15)","(1,16)","(1,17)","(1,18)","(1,19)","(1,20)","(1,21)","(1,22)","(1,23)
","(1,24)","(1,25)"
....
Buffers: shared hit=1057
Planning time: 1.084 ms
Execution time: 0.294 ms
(6 rows)
postgres=# select ctid,* from test where ctid = any(gen_tids(11));
ctid | id
--------+-------
(11,1) | 10000
(1 row)
postgres=# select ctid,* from test where ctid = any(gen_tids(9));
ctid | id
---------+------
(9,1) | 8182
(9,2) | 8183
(9,3) | 8184
(9,4) | 8185
(9,5) | 8186
(9,6) | 8187
...
(9,904) | 9085
(9,905) | 9086
(9,906) | 9087
(9,907) | 9088
(9,908) | 9089
(9,909) | 9090
(909 rows)
擴充套件場景
如果資料沒有更新,刪除;那麼CTID還可以作為索引來使用,例如全文檢索(ES),可以在建立索引時使用ctid來指向資料庫中的記錄,而不需要另外再建一個PK,也能大幅度提升寫入效能。
參考
https://www.citusdata.com/blog/2016/03/30/five-ways-to-paginate/
相關文章
- 讀者寫者與生產者消費者應用場景
- 移動物聯網NB-IoT在細分領域的應用
- 中移物聯網在車聯網場景的 TiDB 探索和實現TiDB
- 水壓監控物聯網有什麼應用場景和功能作用
- 網際網路+物業“錢景”可觀 達萬億級消費
- 5G物聯網在智慧路燈的應用場景及行業痛點行業
- 海口擴充區塊鏈+,遊客身份和消費視窗多應用場景逐步落地區塊鏈
- 在物聯網中應用機器學習機器學習
- 也談人工智慧在消費金融領域的場景應用體現人工智慧
- 基於Azure IoT開發.NET物聯網應用系列-全新的Azure IoT架構架構
- 樂訊通雲通訊:物聯網在智慧家居中都有哪些應用場景?
- 國密在車聯網安全認證場景中的應用
- “區塊鏈 + 物聯網”的發展現狀和應用案例區塊鏈
- 全渠道消費場景購物痛點:速度和便利性
- DHL:物聯網在物流業的應用
- PHP 快速掃描列表建立無限極分類樹PHP
- 區塊鏈物聯網的垂直領域應用區塊鏈
- RTL8710CM Wi-Fi 物聯網(IoT)應用
- 一場有關場景的消費升級如何破題?
- 使用索引快速全掃描(Index FFS)避免全表掃描的若干場景索引Index
- Flutter - IOT領域應用場景實戰Flutter
- Worldpay:中國消費者處在聯網裝置購物的最前沿
- 區塊鏈的應用領域—物聯網和物流領域(二)區塊鏈
- 物聯網在公共安全中的應用
- 物聯網在智慧林業中的應用
- 【物聯網】IoT與智慧時代
- 當 JS 遇上物聯網(IoT)JS
- 物聯網的應用模式模式
- eMarketer:物聯網正在重塑快速消費品行業行業
- C# 掃描並讀取圖片中的文字C#
- 物聯網技術在工業中的應用
- 智慧城市的物聯網和移動應用
- 掃描線及其應用
- 區塊鏈應用場景解析區塊鏈
- 物聯網在冶金行業的應用趨勢研究行業
- 區塊鏈技術應用場景思考-去中心化儲存區塊鏈中心化
- 應用驅動的物聯網產業才符合中國市場產業
- 區塊鏈應用場景有哪些?區塊鏈