PostgreSQL ve BRIN indeksleri
Kendin için test et. Bu 5 yaşında bir dizüstü bilgisayarda bir ssd ile sorun değil.
EXPLAIN ANALYZE
CREATE TABLE electrothingy
AS
SELECT
x::int AS id,
(x::int % 20000)::int AS locid, -- fake location ids in the range of 1-20000
now() AS tsin, -- static timestmap
97.5::numeric(5,2) AS temp, -- static temp
x::int AS usage -- usage the same as id not sure what we want here.
FROM generate_series(1,1728000000) -- for 1.7 billion rows
AS gs(x);
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series gs (cost=0.00..15.00 rows=1000 width=4) (actual time=173119.796..750391.668 rows=1728000000 loops=1)
Planning time: 0.099 ms
Execution time: 1343954.446 ms
(3 rows)
Bu yüzden tabloyu oluşturmak için 22 dakika sürdü. Büyük ölçüde, çünkü masa mütevazı bir 97GB. Sonra dizinleri oluşturacağız,
CREATE INDEX ON electrothingy USING brin (tsin);
CREATE INDEX ON electrothingy USING brin (id);
VACUUM ANALYZE electrothingy;
Dizinlerin oluşturulması da çok zaman aldı. BRIN oldukları için sadece 2-3 MB'lar ve koç içinde kolayca saklanıyorlar. 96 GB okuma anlık değil, ancak iş yükünüzdeki dizüstü bilgisayarım için gerçek bir sorun değil.
Şimdi sorgularız.
explain analyze
SELECT max(temp)
FROM electrothingy
WHERE id BETWEEN 1000000 AND 1001000;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=5245.22..5245.23 rows=1 width=7) (actual time=42.317..42.317 rows=1 loops=1)
-> Bitmap Heap Scan on electrothingy (cost=1282.17..5242.73 rows=993 width=7) (actual time=40.619..42.158 rows=1001 loops=1)
Recheck Cond: ((id >= 1000000) AND (id <= 1001000))
Rows Removed by Index Recheck: 16407
Heap Blocks: lossy=128
-> Bitmap Index Scan on electrothingy_id_idx (cost=0.00..1281.93 rows=993 width=0) (actual time=39.769..39.769 rows=1280 loops=1)
Index Cond: ((id >= 1000000) AND (id <= 1001000))
Planning time: 0.238 ms
Execution time: 42.373 ms
(9 rows)
Zaman damgalarıyla güncelleme
Burada, bir zaman damgası sütununda indeksleme ve arama talebini yerine getirmek için farklı zaman damgalarına sahip bir tablo oluşturuyoruz, oluşturma işlemi biraz daha uzun sürüyor çünkü işlem (işlem için önbelleğe alınmış) to_timestamp(int)
büyük ölçüde daha yavaşnow()
EXPLAIN ANALYZE
CREATE TABLE electrothingy
AS
SELECT
x::int AS id,
(x::int % 20000)::int AS locid,
-- here we use to_timestamp rather than now(), we
-- this calculates seconds since epoch using the gs(x) as the offset
to_timestamp(x::int) AS tsin,
97.5::numeric(5,2) AS temp,
x::int AS usage
FROM generate_series(1,1728000000)
AS gs(x);
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Function Scan on generate_series gs (cost=0.00..17.50 rows=1000 width=4) (actual time=176163.107..5891430.759 rows=1728000000 loops=1)
Planning time: 0.607 ms
Execution time: 7147449.908 ms
(3 rows)
Şimdi bunun yerine zaman damgası değerinde bir sorgu çalıştırabiliriz.
explain analyze
SELECT count(*), min(temp), max(temp)
FROM electrothingy WHERE tsin BETWEEN '1974-01-01' AND '1974-01-02';
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------------------------
Aggregate (cost=296073.83..296073.84 rows=1 width=7) (actual time=83.243..83.243 rows=1 loops=1)
-> Bitmap Heap Scan on electrothingy (cost=2460.86..295490.76 rows=77743 width=7) (actual time=41.466..59.442 rows=86401 loops=1)
Recheck Cond: ((tsin >= '1974-01-01 00:00:00-06'::timestamp with time zone) AND (tsin <= '1974-01-02 00:00:00-06'::timestamp with time zone))
Rows Removed by Index Recheck: 18047
Heap Blocks: lossy=768
-> Bitmap Index Scan on electrothingy_tsin_idx (cost=0.00..2441.43 rows=77743 width=0) (actual time=40.217..40.217 rows=7680 loops=1)
Index Cond: ((tsin >= '1974-01-01 00:00:00-06'::timestamp with time zone) AND (tsin <= '1974-01-02 00:00:00-06'::timestamp with time zone))
Planning time: 0.140 ms
Execution time: 83.321 ms
(9 rows)
Sonuç:
count | min | max
-------+-------+-------
86401 | 97.50 | 97.50
(1 row)
Bu yüzden 83.321 ms'de, 1.7 Milyar sıra içeren bir tabloda 86.401 kayıt toplayabiliriz. Bu makul olmalı.
Saat biten
Saatin sonunu hesaplamak da oldukça kolaydır, zaman damgalarını kısaltın ve ardından sadece bir saat ekleyin.
SELECT date_trunc('hour', tsin) + '1 hour' AS tsin,
count(*),
min(temp),
max(temp)
FROM electrothingy
WHERE tsin >= '1974-01-01'
AND tsin < '1974-01-02'
GROUP BY date_trunc('hour', tsin)
ORDER BY 1;
tsin | count | min | max
------------------------+-------+-------+-------
1974-01-01 01:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 02:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 03:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 04:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 05:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 06:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 07:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 08:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 09:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 10:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 11:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 12:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 13:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 14:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 15:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 16:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 17:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 18:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 19:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 20:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 21:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 22:00:00-06 | 3600 | 97.50 | 97.50
1974-01-01 23:00:00-06 | 3600 | 97.50 | 97.50
1974-01-02 00:00:00-06 | 3600 | 97.50 | 97.50
(24 rows)
Time: 116.695 ms
Ancak, toplamada bir dizin kullanmadığını, ancak yapabildiğine dikkat etmek önemlidir. Bu genellikle sorgunuzsa, muhtemelen üzerinde bir BRIN istiyorsanız, bu konuda date_trunc('hour', tsin)
küçük bir sorun var.date_trunc
değişmez olmadığı yüzden bunu yapabilmek için önce onu sarmanız gerekir.
Bölümleme
PostgreSQL hakkındaki bir diğer önemli bilgi noktası ise, PG 10'un DDL'ye bölümleme getirmesidir . Böylece, örneğin, her yıl için kolayca bölümler oluşturabilirsiniz. Mütevazı veritabanınızı küçük olanlara ayırın. Bunu yaparken, daha hızlı olacak olan BRIN yerine kullanımı kullanıp btree endekslerini kullanabilmelisiniz.
CREATE TABLE electrothingy_y2016 PARTITION OF electrothingy
FOR VALUES FROM ('2016-01-01') TO ('2017-01-01');
Ya da her neyse.