奔跑吧,大屏-時間+空間實時四維資料透視
標籤
PostgreSQL , 遞迴查詢 , 大屏播報 , 最擁堵的路口 , 最旺的商鋪 , 某個區域最擁堵的廁所 , 資料透視 , 流式計算 , 時序資料
背景
隨著物聯網的發展,資料的產生越來越快。比如馬路的汽車通過資料,使用者的駐留資料,水紋感測器的資料,電商的FEED資料,網約車的軌跡資料 等等。
這麼多的資料,用途也不一樣,有需要流式實時統計的,也有時序處理相關需求的,還有全量分析需求的。
這些都有對應的解決方案。
《流計算風雲再起 – PostgreSQL攜PipelineDB力挺IoT》
《時序資料庫有哪些特點? TimescaleDB時序資料庫介紹》
《ApsaraDB的左右互搏(PgSQL+HybridDB+OSS) – 解決OLTP+OLAP混合需求》
以指揮中心的大屏為例,有一些需求就很有意思,比如
1. 展示最新的感測器TOP VALUE資料。
2. 選擇時間軸,按區域,展示人流量。
例子
1 輸出所有感測器上報的最新值
這個需要和我之前寫的這個例子很相似。
《時序資料合併場景加速分析和實現 – 複合索引,視窗分組查詢加速,變態遞迴加速》
設計表結構,gid表示感測器ID,val是上傳的值,crt_time是時間。
假設有1萬個感測器,插入1000萬條記錄。
create unlogged table sort_test(
id serial8 primary key, -- 自增主鍵
s_id int, -- 感測器ID
val numeric(10,2), -- 感測器值
crt_time timestamp default clock_timestamp() -- 上報時間
);
寫入1000萬感測器測試資料
postgres=# insert into sort_test (s_id,val) select random()*10000, random()*100 from generate_series(1,10000000);
INSERT 0 10000000
建立索引
postgres=# create index idx_test on sort_test using btree(s_id,id desc);
使用遞迴呼叫的方法,獲取所有感測器的最新值(以每個感測器的最大的自增ID為最新上報標記)
create type r as (s_id int, val numeric(10,2)); -- 複合型別
with recursive skip as (
(
select (s_id,val)::r as r from sort_test where id in (select id from sort_test where s_id is not null order by s_id,id desc limit 1)
)
union all
(
select (
select (s_id,val)::r as r from sort_test where id in (select id from sort_test t where t.s_id>(s.r).s_id and t.s_id is not null order by s_id,id desc limit 1)
) from skip s where (s.r).s_id is not null
) -- 這裡的where (s.r).s_id is not null 一定要加, 否則就死迴圈了.
)
select (t.r).s_id, (t.r).val from skip t where t.* is not null;
1000萬條記錄,篩選1萬條最新記錄,耗費時間:129毫秒。
為什麼能這麼快?因為用了遞迴,減少了掃描量和運算量。
s_id | val
-------+-------
0 | 83.55
1 | 91.62
2 | 72.70
3 | 45.46
4 | 99.97
5 | 17.04
6 | 8.96
7 | 25.83
8 | 28.10
9 | 26.19
10 | 83.03
11 | 1.30
......
Time: 128.779 ms
使用遊標則更快,一次獲取10條,僅花費0.36毫秒。
postgres=# begin;
BEGIN
Time: 0.095 ms
postgres=# declare cur cursor for with recursive skip as (
(
select (s_id,val)::r as r from sort_test where id in (select id from sort_test where s_id is not null order by s_id,id desc limit 1)
)
union all
(
select (
select (s_id,val)::r as r from sort_test where id in (select id from sort_test t where t.s_id>(s.r).s_id and t.s_id is not null order by s_id,id desc limit 1)
) from skip s where (s.r).s_id is not null
) -- 這裡的where (s.r).s_id is not null 一定要加, 否則就死迴圈了.
)
select (t.r).s_id, (t.r).val from skip t where t.* is not null;
DECLARE CURSOR
Time: 0.841 ms
postgres=# fetch 10 from cur;
s_id | val
------+-------
0 | 83.55
1 | 91.62
2 | 72.70
3 | 45.46
4 | 99.97
5 | 17.04
6 | 8.96
7 | 25.83
8 | 28.10
9 | 26.19
(10 rows)
Time: 0.364 ms
2 輸出某個城市的車流TOP 10路口
相比第一個例子,做了一次收斂,按VALUE排序,輸出最大的。
假設每個路口有感測器不斷上報路口通過的車流數量。大屏展示通過量最大的10個路口。
為了測試方便,我這裡依舊使用第一個例子的資料,末尾加上。
postgres=# with recursive skip as (
(
select (s_id,val)::r as r from sort_test where id in (select id from sort_test where s_id is not null order by s_id,id desc limit 1)
)
union all
(
select (
select (s_id,val)::r as r from sort_test where id in (select id from sort_test t where t.s_id>(s.r).s_id and t.s_id is not null order by s_id,id desc limit 1)
) from skip s where (s.r).s_id is not null
) -- 這裡的where (s.r).s_id is not null 一定要加, 否則就死迴圈了.
)
select (t.r).s_id, (t.r).val from skip t where t.* is not null order by 2 desc limit 10;
s_id | val
------+-------
997 | 99.99
2233 | 99.97
610 | 99.97
4 | 99.97
6735 | 99.96
545 | 99.93
2992 | 99.91
4747 | 99.90
543 | 99.89
7229 | 99.88
(10 rows)
Time: 126.052 ms
1000萬條記錄,篩選1萬條最新記錄,輸出TOP 10,耗費時間:126毫秒。
3 某個區域,某個時間段,按鈕人流量輸出TOP 商鋪
相比前兩個例子,多了兩個維度:
一個是時間維度,使用者可以勾選時間段進行分析。另一個是區域維度,使用者要勾選地區,輸出地區內的資料。
思考:
空間索引不像B-TREE索引是有序儲存的,空間索引是GIST索引,使用了類似聚類分割槽的結構,因此在進行多列複合時,GIST的空間查詢結合索引排序輸出第一條,是行不通的,會引入顯示的SORT。
原理參考
《從難纏的模糊查詢聊開 – PostgreSQL獨門絕招之一 GIN , GiST , SP-GiST , RUM 索引原理與技術背景》
同時查詢條件包含了時間區間作為條件,索引非驅動列(子段gid+VAL)的排序也是行不通的。
什麼時候能使用複合索引的查詢+排序?
僅僅當排序列前面的所有列都是等值查詢時,才能使用隱式排序,並且索引的順序要和排序的順序一致。例如index(a,b,c)支援where a=? and b=? order by c,但是不支援where a> ? and b=? order by c等等。
重新規劃測試資料,為了測試方便, 以point取代經緯度,真實業務可以使用geometry型別。
create table test (
id serial8 primary key, -- 自增序列
gid int, -- 商鋪ID
val int, -- 商鋪人流
pos point, -- 商鋪位置, 為了測試方便, 以point取代經緯度
crt_time timestamp -- 上傳時間
);
插入1000萬測試資料,1萬個店鋪ID,1億的點陣範圍中的隨機point。
postgres=# insert into test (gid,val,pos,crt_time) select random()*10000, random()*100000, point(random()*10000, random()*10000), clock_timestamp() from generate_series(1,10000000);
postgres=# select min(crt_time),max(crt_time) from test;
min | max
----------------------------+----------------------------
2017-04-13 20:04:18.969268 | 2017-04-13 20:04:54.578339
(1 row)
時間+空間 的快速感測器最大值篩選怎麼加速呢?
分兩種情況優化
1. 總的感測器(店鋪)不多(例如1萬個店鋪)
利用索引快速搜尋每個GID的最大VAL,使用partial index,規避時間問題;使用CPU完成點面判斷。
例子,
例如我們允許使用者勾選的最小時間範圍是2小時,可以每2小時建一個partial index。(使用這麼多partial index很變態,也不優雅。建議10.0的分割槽表優化後,每2小時切一個分割槽。)
create index idx_test_1 on test (gid, val desc) where crt_time between `2017-04-13 20:04:18.969268` and `2017-04-13 20:04:30.969268`;
這個區間的總資料量, 約350萬。
postgres=# select count(*) from test where crt_time between `2017-04-13 20:04:18.969268` and `2017-04-13 20:04:30.969268`;
count
---------
3461005
(1 row)
使用這個partial index,以及遞迴呼叫,取出該區間的所有店鋪的最大值。然後根據點面判斷,得到某個區域的資料,再排序輸出TOP 10。
with recursive skip as (
(
select t0 from test t0 where id in
(select id from test where gid is not null and crt_time between `2017-04-13 20:04:18.969268` and `2017-04-13 20:04:30.969268` order by gid,val desc limit 1) -- 時間引數,取出最小GID的最大val。作為啟動記錄
)
union all
(
select (
select t1 from test t1 where id in (select id from test t where t.gid > (s.t0).gid and t.gid is not null
and crt_time between `2017-04-13 20:04:18.969268` and `2017-04-13 20:04:30.969268` -- 時間引數
order by gid,val desc limit 1)
) from skip s where (s.t0).gid is not null
) -- 這裡的where (s.t0).gid is not null 一定要加, 否則就死迴圈了.
)
select (t.t0).* from skip t where t.* is not null
and circle `((5000,5000), 1000)` @> (t.t0).pos -- 區域引數
order by (t.t0).val desc limit 10; -- 取出前十的店鋪
135毫秒返回
id | gid | val | pos | crt_time
---------+------+-------+-------------------------------------+----------------------------
1754353 | 4001 | 99997 | (4755.64117543399,5253.53815406561) | 2017-04-13 20:04:24.563999
600729 | 5874 | 99996 | (5507.96090625226,4394.04523000121) | 2017-04-13 20:04:20.851141
1137330 | 4248 | 99995 | (4332.14340358973,4383.84034205228) | 2017-04-13 20:04:22.575639
2609044 | 7209 | 99995 | (5809.22217573971,4967.18854177743) | 2017-04-13 20:04:27.328745
1330926 | 2834 | 99994 | (4153.9505450055,4986.64934188128) | 2017-04-13 20:04:23.197925
208578 | 3439 | 99994 | (4186.14753056318,5103.39797474444) | 2017-04-13 20:04:19.598547
703010 | 5736 | 99993 | (4913.89285307378,4628.21466382593) | 2017-04-13 20:04:21.178653
298380 | 7680 | 99992 | (4539.91844784468,4454.29485291243) | 2017-04-13 20:04:19.884725
996318 | 7658 | 99992 | (4462.14715018868,5504.16304729879) | 2017-04-13 20:04:22.122626
3120169 | 3261 | 99991 | (4814.33014851063,4505.81138487905) | 2017-04-13 20:04:28.98197
(10 rows)
Time: 135.480 ms
執行計劃如下
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=937.82..937.83 rows=1 width=40) (actual time=147.241..147.243 rows=10 loops=1)
Output: ((t.t0).id), ((t.t0).gid), ((t.t0).val), ((t.t0).pos), ((t.t0).crt_time)
Buffers: shared hit=80066
CTE skip
-> Recursive Union (cost=1.00..935.54 rows=101 width=64) (actual time=0.037..141.284 rows=10002 loops=1)
Buffers: shared hit=80066
-> Nested Loop (cost=1.00..9.03 rows=1 width=64) (actual time=0.036..0.036 rows=1 loops=1)
Output: t0.*
Inner Unique: true
Buffers: shared hit=8
-> HashAggregate (cost=0.57..0.58 rows=1 width=8) (actual time=0.022..0.023 rows=1 loops=1)
Output: test.id
Group Key: test.id
Buffers: shared hit=4
-> Limit (cost=0.43..0.55 rows=1 width=16) (actual time=0.017..0.018 rows=1 loops=1)
Output: test.id, test.gid, test.val
Buffers: shared hit=4
-> Index Scan using idx_test_1 on public.test (cost=0.43..431864.13 rows=3461209 width=16) (actual time=0.017..0.017 rows=1 loops=1)
Output: test.id, test.gid, test.val
Index Cond: (test.gid IS NOT NULL)
Buffers: shared hit=4
-> Index Scan using test_pkey on public.test t0 (cost=0.43..8.45 rows=1 width=72) (actual time=0.012..0.012 rows=1 loops=1)
Output: t0.*, t0.id
Index Cond: (t0.id = test.id)
Buffers: shared hit=4
-> WorkTable Scan on skip s (cost=0.00..92.45 rows=10 width=32) (actual time=0.014..0.014 rows=1 loops=10002)
Output: (SubPlan 1)
Filter: ((s.t0).gid IS NOT NULL)
Rows Removed by Filter: 0
Buffers: shared hit=80058
SubPlan 1
-> Nested Loop (cost=1.20..9.22 rows=1 width=64) (actual time=0.013..0.013 rows=1 loops=10001)
Output: t1.*
Inner Unique: true
Buffers: shared hit=80058
-> HashAggregate (cost=0.76..0.77 rows=1 width=8) (actual time=0.009..0.009 rows=1 loops=10001)
Output: t_1.id
Group Key: t_1.id
Buffers: shared hit=40033
-> Limit (cost=0.43..0.75 rows=1 width=16) (actual time=0.008..0.008 rows=1 loops=10001)
Output: t_1.id, t_1.gid, t_1.val
Buffers: shared hit=40033
-> Index Scan using idx_test_1 on public.test t_1 (cost=0.43..369056.35 rows=1153736 width=16) (actual time=0.008..0.008 rows=1 loops=10001)
Output: t_1.id, t_1.gid, t_1.val
Index Cond: ((t_1.gid > (s.t0).gid) AND (t_1.gid IS NOT NULL))
Buffers: shared hit=40033
-> Index Scan using test_pkey on public.test t1 (cost=0.43..8.45 rows=1 width=72) (actual time=0.003..0.003 rows=1 loops=10000)
Output: t1.*, t1.id
Index Cond: (t1.id = t_1.id)
Buffers: shared hit=40025
-> Sort (cost=2.28..2.29 rows=1 width=40) (actual time=147.240..147.241 rows=10 loops=1)
Output: ((t.t0).id), ((t.t0).gid), ((t.t0).val), ((t.t0).pos), ((t.t0).crt_time)
Sort Key: ((t.t0).val) DESC
Sort Method: top-N heapsort Memory: 26kB
Buffers: shared hit=80066
-> CTE Scan on skip t (cost=0.00..2.27 rows=1 width=40) (actual time=0.252..147.138 rows=317 loops=1)
Output: (t.t0).id, (t.t0).gid, (t.t0).val, (t.t0).pos, (t.t0).crt_time
Filter: ((t.* IS NOT NULL) AND (`<(5000,5000),1000>`::circle @> (t.t0).pos))
Rows Removed by Filter: 9685
Buffers: shared hit=80066
Planning time: 0.508 ms
Execution time: 147.505 ms
(62 rows)
2. 店鋪很多,但是時間+空間收斂後,記錄數不多(比如幾百萬)
這種情況,可以考慮使用時間分割槽表。然後構建空間索引。
通過時間條件,定位到指定的分割槽,通過空間索引,篩選資料。對篩選後的資料,通過少量CPU計算得到TOP店鋪。
例子
2.1 將表按時間分割槽(例如每2小時一個分割槽,前面有介紹為什麼這麼做)
略,我這裡假設每兩小時約1千萬資料。
2.2 建立空間索引
postgres=# create index idx_test_gist on test using gist(pos);
CREATE INDEX
2.3 透視
SQL中輸入時間條件時,PostgreSQL會自動鎖定到分割槽表,我這裡為了簡便,直接寫TEST表。
使用視窗查詢,得到TOP SQL
select * from
(
select row_number() over(partition by gid order by val desc) as rn, * from test
where
circle `((5000,5000), 1000)` @> pos -- 區域引數
) t
where rn = 1 -- 取出該區間內每個店鋪的最大值
order by val desc limit 10; -- 取出前十的店鋪
效率
rn | id | gid | val | pos | crt_time
----+---------+------+-------+-------------------------------------+----------------------------
1 | 7859807 | 2311 | 99999 | (4900.04640072584,4950.79724118114) | 2017-04-13 20:04:46.013424
1 | 4658616 | 3699 | 99999 | (5625.03716442734,5338.90711143613) | 2017-04-13 20:04:35.467025
1 | 1754353 | 4001 | 99997 | (4755.64117543399,5253.53815406561) | 2017-04-13 20:04:24.563999
1 | 6076598 | 4610 | 99997 | (5679.03681658208,4793.08029171079) | 2017-04-13 20:04:40.09587
1 | 6139261 | 4069 | 99997 | (5225.87833926082,4101.83480009437) | 2017-04-13 20:04:40.301817
1 | 600729 | 5874 | 99996 | (5507.96090625226,4394.04523000121) | 2017-04-13 20:04:20.851141
1 | 4281282 | 9720 | 99996 | (5036.95292398334,4731.64941649884) | 2017-04-13 20:04:34.237957
1 | 5579952 | 1503 | 99996 | (4271.09604235739,5250.28191972524) | 2017-04-13 20:04:38.469311
1 | 5310205 | 1317 | 99995 | (4439.0160869807,4796.70224711299) | 2017-04-13 20:04:37.590451
1 | 1137330 | 4248 | 99995 | (4332.14340358973,4383.84034205228) | 2017-04-13 20:04:22.575639
(10 rows)
Time: 633.342 ms
執行計劃
Limit (cost=39265.88..39265.91 rows=10 width=48) (actual time=730.704..730.706 rows=10 loops=1)
Output: t.rn, t.id, t.gid, t.val, t.pos, t.crt_time
Buffers: shared hit=317037, temp read=1921 written=1928
-> Sort (cost=39265.88..39266.01 rows=50 width=48) (actual time=730.702..730.703 rows=10 loops=1)
Output: t.rn, t.id, t.gid, t.val, t.pos, t.crt_time
Sort Key: t.val DESC
Sort Method: top-N heapsort Memory: 26kB
Buffers: shared hit=317037, temp read=1921 written=1928
-> Subquery Scan on t (cost=38939.80..39264.80 rows=50 width=48) (actual time=520.846..728.927 rows=10001 loops=1)
Output: t.rn, t.id, t.gid, t.val, t.pos, t.crt_time
Filter: (t.rn = 1)
Rows Removed by Filter: 303477
Buffers: shared hit=317037, temp read=1921 written=1928
-> WindowAgg (cost=38939.80..39139.80 rows=10000 width=48) (actual time=520.844..703.933 rows=313478 loops=1)
Output: row_number() OVER (?), test.id, test.gid, test.val, test.pos, test.crt_time
Buffers: shared hit=317037, temp read=1921 written=1928
-> Sort (cost=38939.80..38964.80 rows=10000 width=40) (actual time=520.837..594.505 rows=313478 loops=1)
Output: test.gid, test.val, test.id, test.pos, test.crt_time
Sort Key: test.gid, test.val DESC
Sort Method: external merge Disk: 15368kB
Buffers: shared hit=317037, temp read=1921 written=1928
-> Index Scan using idx_test_gist on public.test (cost=0.42..38275.42 rows=10000 width=40) (actual time=0.240..336.235 rows=313478 loops=1)
Output: test.gid, test.val, test.id, test.pos, test.crt_time
Index Cond: (`<(5000,5000),1000>`::circle @> test.pos)
Buffers: shared hit=317037
Planning time: 0.140 ms
Execution time: 734.226 ms
(27 rows)
核心層面優化(空間GRID分割槽表的支援)
讓PostgreSQL支援空間GRID分割槽(實際上你現在就可以使用繼承來實現,觸發器中使用grid+mod判斷應該插入哪個分割槽)。
參考如下
《蜂巢的藝術與技術價值 – PostgreSQL PostGIS`s hex-grid》
對於時間+空間維度的資料透視,可以建立空間grid分割槽 + 時間分割槽 二級分割槽。
檢索時,通過分割槽表直接過濾到目標子分割槽表。再通過btree索引,遞迴呼叫,篩選出每個店鋪在候選區間的峰值資料,最後加上少量CPU運算,得到TOP店鋪。
使用這種方法,時間+空間的四維資料透視,查詢效率可以進入100毫秒以內。
業務優化方法
1. 對於例子1和2,由於業務層面取的都是最近的資料,歷史資料並不關心。除了使用遞迴優化,還有2種方法。
方法1,不記錄歷史,將插入換成插入或更新。使用這種方法,查詢sort_test得到的始終是最新的值。
create unlogged table sort_test(
s_id int primary key, -- 感測器ID
val numeric(10,2), -- 感測器值
crt_time timestamp default clock_timestamp() -- 上報時間
);
insert into sort_test(s_id,val,crt_time) values (?,?,?) on conflict (s_id) do update set val=excluded.val,crt_time=excluded.crt_time;
方法2,記錄歷史,同時記錄最新狀態。使用觸發器完成這項工作。
分解:
資料插入時,自動更新最後一條記錄。(寫入量和更新量同等)
例子
建立一個狀態表記錄最新狀態,建立一個觸發器,寫入歷史資料時,自動更新最新狀態表。
create unlogged table hist(
id serial8 primary key, -- 自增主鍵
s_id int, -- 感測器ID
val numeric(10,2), -- 感測器值
crt_time timestamp default clock_timestamp() -- 上報時間
);
create unlogged table hist_stat(
s_id int primary key, -- 感測器ID
val numeric(10,2), -- 感測器值
crt_time timestamp default clock_timestamp() -- 上報時間
);
create or replace function tg() returns trigger as $$
declare
begin
insert into hist_stat (s_id,val,crt_time) values (NEW.s_id,NEW.val,NEW.crt_time) on conflict (s_id) do update set val=excluded.val,crt_time=excluded.crt_time;
return null;
end;
$$ language plpgsql strict;
create trigger tg after insert on hist for each row execute procedure tg();
插入資料,自動更新到最新狀態
postgres=# insert into hist(s_id,val) values(1,1);
INSERT 0 1
postgres=# insert into hist(s_id,val) values(1,1);
INSERT 0 1
postgres=# insert into hist(s_id,val) values(1,1);
INSERT 0 1
postgres=# insert into hist(s_id,val) values(1,1);
INSERT 0 1
postgres=# insert into hist(s_id,val) values(1,1);
INSERT 0 1
postgres=# select * from hist;
id | s_id | val | crt_time
----+------+------+----------------------------
3 | 1 | 1.00 | 2017-04-13 22:23:25.165286
4 | 1 | 1.00 | 2017-04-13 22:23:26.23929
5 | 1 | 1.00 | 2017-04-13 22:23:26.646152
6 | 1 | 1.00 | 2017-04-13 22:23:26.991189
7 | 1 | 1.00 | 2017-04-13 22:23:27.376265
(5 rows)
postgres=# select * from hist_stat ;
s_id | val | crt_time
------+------+----------------------------
1 | 1.00 | 2017-04-13 22:23:27.376265
(1 row)
查詢時,直接查詢最新狀態表,連遞迴呼叫都省了。
postgres=# select * from hist_stat ;
s_id | val | crt_time
------+------+----------------------------
1 | 1.00 | 2017-04-13 22:23:27.376265
(1 row)
2. 對於例子3,由於分析的是歷史資料,而且分析維度是時間和空間兩個維度。
因此可以將其中一個維度作為分割槽,將資料打散,打散之後,對分割槽建立另一個維度的索引。
這樣的話,在查詢時,可以將資料儘量的收斂到更小的範圍。
空間和時間都支援分割槽。(空間分割槽建議使用網格化的表述,便於查詢和定位分割槽)。
參考
《流計算風雲再起 – PostgreSQL攜PipelineDB力挺IoT》
《時序資料庫有哪些特點? TimescaleDB時序資料庫介紹》
《ApsaraDB的左右互搏(PgSQL+HybridDB+OSS) – 解決OLTP+OLAP混合需求》
《時序資料合併場景加速分析和實現 – 複合索引,視窗分組查詢加速,變態遞迴加速》
《從難纏的模糊查詢聊開 – PostgreSQL獨門絕招之一 GIN , GiST , SP-GiST , RUM 索引原理與技術背景》
《蜂巢的藝術與技術價值 – PostgreSQL PostGIS`s hex-grid》
相關文章
- Oracle臨時表空間檢視、新增臨時表空間資料檔案、修改預設臨時表空間 方法!Oracle
- 關於時間、物質結構、四維空間的猜想
- 【實驗】重建臨時表空間解決臨時表空間過大問題
- 【Oracle-資料庫維護】-刪除臨時表空間Oracle資料庫
- 【臨時表空間組】臨時表空間組的建立、維護及應用
- 資料視覺化--實驗五:高維非空間資料視覺化視覺化
- oracle建立臨時表空間和資料表空間以及刪除Oracle
- 時間和空間的完美統一!阿里雲時空資料庫正式商業化阿里資料庫
- 臨時表空間資料刪除問題
- MYSQL造資料佔用臨時表空間MySql
- 【資料結構】-時間複雜度和空間複雜度資料結構時間複雜度
- 第8章 紐約計程車軌跡的空間和時間資料分析
- 大資料匯入之MySql設計之空間換時間的設計變更大資料MySql
- win10設定息屏時間在哪裡 修改息屏時間的方法Win10
- JS時間軸效果(類似於qq空間時間軸效果)JS
- C語言高效程式設計的四大祕技之以空間換時間C語言程式設計
- 查詢時若時間為空,開始時間取今天的零點,結束時間取當前時間
- Sgz PBKAv構建AR視訊空間大資料大資料
- 批量處理時臨時增加回滾表空間臨時表空間檔案
- 坑系列 — 時間和空間的平衡
- 時間與空間複雜度分析複雜度
- 坑系列 --- 時間和空間的平衡
- 檢視資料庫表空間資料庫
- oracle 臨時表空間Oracle
- oracle臨時表空間Oracle
- 檢視系統中各種等待時間佔用的資料庫時間比例資料庫
- 自學 資料結構四月二十一日_時間複雜度&空間複雜度資料結構時間複雜度
- Hive 資料更新時間Hive
- 星環科技分散式向量資料庫Transwarp Hippo正式釋出,擴充大語言模型時間和空間維度分散式資料庫模型
- 移動資料檔案、系統表空間檔案、臨時表空間檔案
- 時空資料庫實踐(含紐約TAXI資料透視分析)-PostGIS+TimescaleDB=>PostgreSQL資料庫SQL
- 掌握時間與空間:深入探討Golang中的時間戳與時區轉換Golang時間戳
- 檢視oracle臨時表空間佔用率的檢視Oracle
- 臨時表空間過大的解決方法
- 冰與火之歌:「時間」與「空間」複雜度複雜度
- 臨時表空間的空間使用情況查詢
- 空間資料庫三維空間兩點距離計算錯誤資料庫
- 臨時表空間的建立、刪除,設定預設臨時表空間