postgresql 9.4 - novinky (a jsonb) - · pdf filealter table all in tablespace tablespace1 ......

57
PostgreSQL 9.4 - Novinky (a JSONB) Tomáš Vondra, GoodData ([email protected]) http://blog.pgaddict.com ([email protected])

Upload: truongkhanh

Post on 19-Mar-2018

250 views

Category:

Documents


3 download

TRANSCRIPT

PostgreSQL 9.4 - Novinky (a JSONB)Tomáš Vondra, GoodData ([email protected])

http://blog.pgaddict.com ([email protected])

9.4 release noteshttp://www.postgresql.org/docs/9.4/static/release-9-4.html

článek od Pavla Stěhule (květen 2015)http://www.root.cz/clanky/postgresql-9-4-transakcni-sql-json-databaze/

What's new in PostgreSQL 9.4 (Magnus Hagander)http://www.hagander.net/talks/postgresql94_2.pdf

PostgreSQL Conference Europe 2014 (říjen, Madrid)https://wiki.postgresql.org/wiki/PostgreSQL_Conference_Europe_Talks_2014

Postup vývoje 9.4

2013 červen 9.3 branch

2013 červen commitfest #1

2013 září commitfest #2

2013 listopad commitfest #3

2014 leden commitfest #4

2014 květen beta 1

2014 červenec beta 2

2014 říjen beta 3

2014 listopad (?) PostgreSQL 9.4.0

Aktuální stav

● testuje se beta3– Pomozte s testováním!

– Aplikační testy nade vše (zkuste svoji aplikaci na 9.4).

● trochu statistiky– 2222 souborů změněno

– 131805 nových řádek (+)

– 59333 smazaných řádek (-)

● méně než 9.3 ...– ale všichni víme že LOC je výborná metrika ;-)

https://www.openhub.net/p/postgres

Takže co je vlastně nového?

● vývojářské / SQL vlastnosti● DBA a administrace● replikace a recovery● infrastruktura

Vývojářské / SQL vlastnosti

● agregační funkce– FILTER aggregates

– ordered-set aggregates

– další menší vylepšení

● vylepšení aktualizovatelných pohledů● UNNEST (WITH ORDINALITY)● pl/pgsql stacktrace● JSONB (nejlepší na závěr)

agregační výrazy (FILTER)

SELECT a, SUM(CASE WHEN b < 10 THEN c ELSE NULL END) AS pod_10, SUM(CASE WHEN b >= 10 THEN c ELSE NULL END) AS nad_10FROM tabulka GROUP BY a;

SELECT a, SUM(c) FILTER (WHERE b < 10) AS pod_10, SUM(c) FILTER (WHERE b >= 10) AS nad_10FROM tabulka GROUP BY a;

ordered-set aggregates

● pořadí vstupních hodnot není definováno– často nepodstatné (MIN, MAX, SUM, …)

– někdy na něm ale záleží (array_agg, string_agg, …)

– lze ho určit pomocí ORDER BY ve volání funkce

SELECT a, SUM(b ORDER BY c) AS suma_b_serazene_dle_c, ARRAY_AGG(b ORDER BY c) AS pole_b_serazene_dle_cFROM tabulka GROUP BY a;

( Toto není ordered-set aggregate! )

ordered-set aggregates

● některé agregační funkce pořadí vyžadují– z definice (jinak to prostě nedává smysl)

– rank, percentil, ...

– direct / aggregate argumenty (rozdíl)

SELECT a, PERCENTILE_DISC(0.5) WITHIN GROUP (ORDER BY b) AS median_bFROM tabulka GROUP BY a;

ordered-set aggregates

● některé agregační funkce pořadí vyžadují– z definice (jinak to prostě nedává smysl)

– rank, percentil, ...

– direct / aggregate argumenty (rozdíl)

SELECT a, PERCENTILE_DISC(ARRAY[0.25, 0.5, 0.75]) WITHIN GROUP (ORDER BY b) AS median_bFROM tabulka GROUP BY a;

( proprietární rozšíření )

hypotetické agregační funkce

● Kam by se zařadila daná hodnota?– pořadí – rank(..), dense_rank(..)

– relativní (0, 1) – percent_rank(..), cume_dist(..)

SELECT

a,

rank('xyz') WITHIN GROUP (ORDER BY b),

dense_rank('xyz') WITHIN GROUP (ORDER BY b),

percent_rank('xyz') WITHIN GROUP (ORDER BY b)

FROM tabulka GROUP BY a;

agregační funkce / další vylepšení

● group keys v EXPLAIN

EXPLAIN SELECT COUNT(a) FROM tabulka GROUP BY b;

QUERY PLAN-------------------------------------------------------------- HashAggregate (cost=195.00..195.11 rows=11 width=8) Group Key: b -> Seq Scan on tabulka (cost=0.00..145.00 rows=100 width=8)(3 rows)

automaticky updatovatelné pohledy

● pro jednoduché pohledy lze „překládat“ DML příkazy– jedna tabulka ve FROM

– bez agregací / množinových operací, apod.

– pouze jednoduché odkazy na sloupce tabulky

– nesmí být označený pomocí „security_barrier“

● od 9.4 lze používat výrazy, konstanty, volání funkcí– samozřejmě tyto sloupce nelze měnit

CREATE VIEW zamestnanci_view AS SELECT emp_id, dept_id, (salary*0.6) AS cista_mzda FROM zamestnanci_tabulka WHERE dept_id IN (10, 20);

automaticky updatovatelné pohledy

● řádky odfiltrované přes WHERE nelze updatovat● řádek ale může „vypadnout“

– WITH CHECK OPTION tomu zabrání

– další krok na cestě k „Row Level Security“ (řízení přístupu k řádkům)

CREATE VIEW zamestnanci_view AS SELECT id_zamestnance, id_oddeleni, (plat*0.6) AS cista_mzda FROM zamestnanci_tabulka WHERE id_oddeleni IN (10, 20) WITH CHECK OPTION;

UPDATE zamestnanci_view SET id_oddeleni = 30;

unnest

SELECT unnest(ARRAY[1,2,3]) AS a;

a --- 1 2 3(3 rows)

unnest

SELECT unnest(ARRAY[1,2,3]) AS a,

unnest(ARRAY[4,5,6]) AS b;

unnest

SELECT unnest(ARRAY[1,2,3]) AS a,

unnest(ARRAY[4,5,6]) AS b;

a | b ---+--- 1 | 4 2 | 5 3 | 6(3 rows)

unnest

SELECT unnest(ARRAY[1,2,3]) AS a,

unnest(ARRAY[4,5]) AS b;

unnest

SELECT unnest(ARRAY[1,2,3]) AS a,

unnest(ARRAY[4,5]) AS b;

a | b ---+--- 1 | 4 2 | 5 3 | 4 1 | 5 2 | 4 3 | 5(6 rows)

unnest

SELECT *

FROM unnest(ARRAY[1,2,3], ARRAY[4,5]) AS t(a,b);

a | b ---+--- 1 | 4 2 | 5 3 |(3 rows)

unnest

SELECT *

FROM unnest(ARRAY[1,2,3], ARRAY[4,5])

WITH ORDINALITY AS t(a,b);

a | b | ordinality ---+---+------------ 1 | 4 | 1 2 | 5 | 2 3 | | 3(3 rows)

unnest / ROWS FROM

SELECT a, b, ordinality

FROM ROWS FROM (unnest(ARRAY[1,2,3]),

unnest(ARRAY[4,5]))

WITH ORDINALITY AS t(a,b);

SELECT a, b, ordinality

FROM ROWS FROM (unnest(ARRAY[1,2,3]),

generate_series(1,10))

WITH ORDINALITY AS t(a,b);

PL/pgSQL / call stack

CREATE OR REPLACE FUNCTION inner_func() RETURNS integer AS $$DECLARE stack text;BEGIN GET DIAGNOSTICS stack = PG_CONTEXT; RAISE NOTICE E'--- Call Stack ---\n%', stack; RETURN 1;END;$$ LANGUAGE plpgsql;

SELECT outer_func();

NOTICE: --- Call Stack ---PL/pgSQL function inner_func() line 4 at GET DIAGNOSTICSPL/pgSQL function outer_func() line 3 at RETURN...

PL/pgSQL / call stack

CREATE OR REPLACE FUNCTION inner_func() RETURNS integer AS $$DECLARE stack text;BEGIN GET DIAGNOSTICS stack = PG_CONTEXT; RAISE NOTICE E'--- Call Stack ---\n%', stack; RETURN 1;EXCEPTION WHEN others THEN GET STACKED DIAGNOSTICS stack = PG_ERROR_CONTEXT; RAISE NOTICE E'--- Exception Call Stack ---\n%', stack;END;$$ LANGUAGE plpgsql;

(od 9.2, oboje Pavel Stěhule)

DBA a administrace

● MATERIALIZED VIEWS● přesun objektů mezi tablespacy● vylepšení GIN (komprese, fast scan)● ALTER SYSTEM / pg_reload_conf● nové konfigurační parametry● pg_prewarm● pg_stat_statements (query ID)

MATERIALIZED VIEWS

CREATE MATERIALIZED VIEW my_view AS SELECT …;

CREATE UNIQUE INDEX my_index ON my_view (…);

REFRESH MATERIALIED VIEW my_view;

REFRESH MATERIALIED VIEW CONCURRENTLY my_view;

SET TABLESPACE ...

ALTER TABLE ALL IN TABLESPACE tablespace1 OWNED BY uzivatel SET TABLESPACE tablespace2 [NOWAIT];

ALTER INDEX ...

ALTER VIEW ...

ALTER MATERIALIZED VIEW …

(rozdíl oproti Pavlově článku / Magnusově prezentaci)

Vylepšení GIN indexů

● pro hodnoty složené z „částí“ (pole, slova, …)● index se skládá z položek

key1 => [rowid1, rowid2, rowid3, …]

key2 => [rowid10, rowid20, rowid30, …]

● v podstatě bitmapové indexy (zvláštně kódované)– jde použít na skalární typy (extenze btree_gin)

● dvě významná vylepšení v 9.4– komprese posting listů (seznam odkazů na řádky)

– fast scan (dotazy typu „častý & vzácný“ výrazně rychlejší)

ALTER SYSTEM

● dosud bylo nutné přímo editovat postgresql.conf

$ vim /var/lib/pgsql/9.1/data/postgresql.conf

● nově je možné toho dosáhnout přímo z databáze

ALTER SYSTEM SET work_mem = '128MB';

● vytvoří se nový soubor s konfigurací (include)

postgresql.auto.conf

● stále nutný explicitní reload / restart :-(

SELECT pg_reload_conf();

Konfigurační parametry

● autovacuum_work_mem

– odděleno z maintenance_work_mem● huge_pages

– Linuxové systémy s velkým objemem RAM● session_preload_libraries

– načtení sdílených knihoven– na rozdíl od local_preload_libraries libovolných

● wal_log_hints– standardně se nelogují– ale občas jsou užitečné - replikace, rewind

● (maintenance_)work_mem / effective_cache_size– navýšení default hodnot (4x)

pg_prewarm

● triviální způsob jak „zahřát cache“– page cache (kernel) i shared buffers (DB)

– statistiky shared_buffers lze získat přes pg_buffercache

pg_prewarm(regclass, mode text default 'buffer', fork text default 'main', first_block int8 default null, last_block int8 default null) RETURNS int8

(možná kombinace s pgfincore)

pg_stat_statements

● texty dotazů se ukládají do souboru– šetří sdílenou paměť

– odstraňuje limit na délku dotazu

● doplnění „query ID“ (interní hash dotazu)– identifikace dotazu (monitorovací nástroje)

– nestabilní mezi major verzemi / platformami

● možnost získat statistiky bez textů dotazů

SELECT * FROM pg_stat_statements(false);

– úspornější fungování monitorovacích nástrojů

Replikace a recovery

● přesun tablespaces v pg_basebackup

CREATE TABLESPACE ... LOCATION '...';

– jiné rozložení disku, stejná mašina => :-(

pg_basebackup -T olddir=newdir

● time-delayed standby (recovery.conf)

recovery_min_apply_delay=3600000

● pg_stat_archiver

– čas/počet archivovaných WAL segmentů (atd.)

– úspěšné i neúspěšné pokusy

Infrastruktura

● základ logické replikace– zpětná extrakce změn z transakčního logu

– základy v 9.4, další patche (9.5)

– alternativa k Slony, londiste, ...

● replikační sloty– flexibilní zachovávání WAL segmentů

– alternativa wal_keep_segments / archive_command

– alternativa hot_standby_feedback / vacuum_defer_cleanup_age

Infrastruktura

● background workers– uživatelské procesy spravované databází

(extensions)

– dynamická registrace, spouštění, ukončování

– max_worker_processes

– ...

– vnímáno jako základ pro „Parallel Query“

Spousta dalšího ...

http://www.postgresql.org/docs/9.4/static/release-9-4.html

● spousta malých změn a vylepšení– výkonnostní vylepšení

– zjednodušení práce

– atd. atd.

JSONB

Dokumentacehttp://www.postgresql.org/docs/9.4/static/datatype-json.html

NoSQL on ACIDhttps://wiki.postgresql.org/images/d/de/NoSQL_training_-_pgconf.eu.pdf

KV a dokumenty v PostgreSQL

HSTORE● kolekce key-value

– pouze řetězce, jedna úroveň (bez vnoření)

● jednoduchá definice, rychlý, snadná práce● PostgreSQL 8.2 (2006)

– predatuje mnohé NoSQL řešení

● ideální pro „řídké“ kolekce hodnot– spousta sloupců, neznámé sloupce, ...

KV a dokumenty v PostgreSQL

JSON● hierarchický model dokumentů● 9.2 – samostatný datový typ

– víceméně jenom validace vstupu, minimum funkcí

– možnost psát funkce v PL/V8, PL/Perl, …

● 9.3 – doplněny operátory / funkce pro manipulaci, ...

JSONB● binární reprezentace JSON (neplést s BSON)● rychlejší, širší paleta operátorů, robustnější● PostgreSQL 9.4 (namísto vyvíjeného HSTORE2)

JSONB příklad

CREATE TABLE json_data (data JSONB);

INSERT INTO json_data (data) VALUES

('{"name": "Apple Phone",

"type": "phone",

"brand": "ACME",

"price": 200,

"available": true,

"warranty_years": 1}');

JSONB příklad

CREATE TABLE json_data (data JSONB);

INSERT INTO json_data (data) VALUES

('{"full name": "John Joseph Carl Salinger",

"names": [

{"type": "firstname", "value": "John"},

{"type": "middlename", "value": "Joseph"},

{"type": "middlename", "value": "Carl"},

{"type": "lastname", "value": "Salinger"}

]}');

funkce a operátory

● http://www.postgresql.org/docs/9.4/static/functions-json.html

● extrakce hodnot z JSON dokumentů

● containment a existence

-> jako JSON dokument

#> jako JSON dokument

->> jako text

#>> jako text

@> containment (sub-dokument)

? existence klíče

?| existence (alespoň jeden klíč)

?& existence (všechny klíče)

extrakce hodnot (jako JSON)

SELECT '{"a" : {"b" : "c"}}'::jsonb -> 'a';

{"b": "c"}

SELECT '{"a" : {"b" : "c"}}'::jsonb -> 'a' -> 'b';

"c"

SELECT '{"a" : {"b" : "c"}}'::jsonb #> '{a, b}';

SELECT '{"a" : {"b" : "c"}}'::jsonb #> ARRAY['a', 'b'];

"c"

containment & existence

SELECT '{"a":1, "b":2}'::jsonb @> '{"b":2}'::jsonb;true

SELECT '{"a":1, "b":2}'::jsonb @> '{"b":3}'::jsonb;false

SELECT '{"a":1, "b":2}'::jsonb ? 'a';true

SELECT '{"a":1, "b":2}'::jsonb ?| ARRAY['a', 'c'];true

SELECT '{"a":1, "b":2}'::jsonb ?& ARRAY['a', 'c'];false

processing functions

● to_json

● array_to_json

● row_to_json

● json_build_array

● json_build_object

● json_object

● json_array_length

● json_each

● json_each_text

● json_extract_path

● json_extract_path_text

● json_object_keys

● json_populate_record

● json_populate_recordset

● json_array_elements

● json_array_elements_text

● json_typeof

● json_to_record

● json_to_recordset

http://www.postgresql.org/docs/9.4/static/functions-json.html

processing functions

● to_json

● array_to_json

● row_to_json

● json_build_array

● json_build_object

● json_object

● jsonb_array_length

● jsonb_each

● jsonb_each_text

● jsonb_extract_path

● jsonb_extract_path_text

● jsonb_object_keys

● jsonb_populate_record

● jsonb_populate_recordset

● jsonb_array_elements

● jsonb_array_elements_text

● jsonb_typeof

● jsonb_to_record

● jsonb_to_recordset

http://www.postgresql.org/docs/9.4/static/functions-json.html

Mailing list archive

CREATE TABLE messages ( id integer PRIMARY KEY, parent_id integer, thread_id integer, list varchar(32) NOT NULL, message_id varchar(200), ... sent timestamp, author text, subject text, headers jsonb, body_plain text, subject_tsvector tsvector, body_tsvector tsvector);

JSONB / diskový prostor

List of relations

Schema | Name | Size--------+----------------------+--------- public | headers_jsonb | 1244 MB public | headers_jsonb_beta_2 | 1330 MB public | headers_json | 1517 MB public | headers_text | 1517 MB

dump 1567 MB

Dotazování

SELECT COUNT(*) FROM messages WHERE headers ? 'bcc';

SELECT (headers->>'message-id') AS mid FROM messages WHERE headers ? 'bcc';

SELECT COUNT(*) FROM messages WHERE headers @> '{"from" : "[email protected]"}';

QUERY PLAN ------------------------------------------------------------------ Aggregate (cost=177501.99..177502.00 rows=1 width=0) -> Seq Scan on messages (cost=0.00..177499.70 rows=917 width=0) Filter: (headers @> '{"from": "[email protected]"}'::jsonb) Planning time: 0.178 ms(4 rows)

Indexování / btree

● nesmysl indexovat celé JSON dokumenty– ale indexy nad výrazy lze použít

CREATE INDEX messages_cc_idx ON messages ((headers->>'cc'));

SELECT * FROM messages WHERE headers->>'cc' = '[email protected]';

QUERY PLAN------------------------------------------------------------------------- Bitmap Heap Scan on messages (cost=200.09..16121.34 rows=4585 width=1529) Recheck Cond: ((headers ->> 'cc'::text) = '[email protected]'::text) -> Bitmap Index Scan on ttt (cost=0.00..198.94 rows=4585 width=0) Index Cond: ((headers ->> 'cc'::text) = '[email protected]'::text) Planning time: 1.044 ms(5 rows)

Indexování / GIN

CREATE INDEX messages_gin_idx ON messages USING gin(headers);

SELECT * FROM messages WHERE headers @> '{"cc" : "[email protected]"}';

QUERY PLAN-------------------------------------------------------------------------- Bitmap Heap Scan on messages (cost=51.11..3518.80 rows=917 width=1529) Recheck Cond: (headers @> '{"cc": "[email protected]"}'::jsonb) -> Bitmap Index Scan on messages_gin_idx (cost=0.00..50.88 rows=917 width=0) Index Cond: (headers @> '{"cc": "[email protected]"}'::jsonb) Planning time: 0.135 ms(5 rows)

Indexování / GIN

● jsonb_ops– výchozí operator class– všechny základní operátory @> ? ?| ?&

● jsonb_path_ops– menší, rychlejší indexy– pouze @> operátor

CREATE INDEX headers_path_idx ON messagesUSING gin(headers jsonb_path_ops);

● možnost subdocument indexůCREATE INDEX messages_cc_idx ON messages

USING gin((headers->'cc'));

Indexování / GIN

List of relations

Schema | Name | Size--------+---------------------------+-------- public | messages_btree_idx | 64 MB public | messages_gin_idx | 503 MB public | messages_gin_path_idx | 270 MB public | messages_hash_id_key | 67 MB public | messages_pkey | 36 MB public | messages_cc_idx | 25 MB(4 rows)

PostgrteSQL NoSQL benchmarkhttps://github.com/EnterpriseDB/pg_nosql_benchmark

http://bit.ly/1rXinmp / http://bit.ly/1t2jG1r

(nižší hodnoty jsou lepší)

Fulltext benchmark

● fulltextové vyhledávání v e-mailovém archivu

● kombinace slov s různou frekvencí

● 33k reálných dotazů z postgresql.org

SELECT id FROM messages

WHERE body_fts @@ ('high & performance')::tsquery

ORDER BY ts_rank(body_fts, ('high & performance')::tsquery)

DESC LIMIT 100;

0.1 1 10 100 10000

0.2

0.4

0.6

0.8

1

1.2

1.4

1.6

1.8

Fulltext benchmark / 9.3 vs. 9.4 (GIN fastscan)

9.4 durations, divided by 9.3 durations (e.g. 0.1 means 10x speedup)

duration on 9.3 [miliseconds, log scale]

9.4

du

ratio

n (

rela

tive

to

9.3

)