Co je JSON?
JSON je zkratka pro „JavaScript Object Notation“, což je typ datového formátu, který běžně používají webové aplikace. To znamená, že data by byla přenášena mezi webovými aplikacemi a servery v takovém formátu. JSON byl představen jako alternativa k formátu XML. Za „starých dobrých časů“ se data přenášela ve formátu XML, což je ve srovnání s JSON těžký datový typ. Níže je uveden příklad řetězce ve formátu JSON:
{ "ID":"001","name": "Ven", "Country": "Australia", "city": "Sydney", "Job Title":"Database Consultant"}
Řetězec JSON může obsahovat další objekt JSON v sobě, jak je znázorněno níže:
{ "ID":"001", "name": "Ven", "Job Title":"Database Consultant", "Location":{"Suburb":"Dee Why","city": "Sydney","State":"NSW","Country": "Australia"}}
Moderní webové a mobilní aplikace většinou generují data ve formátu JSON, také nazývaném „bajty JSON“, která jsou sbírána aplikačními servery a odesílána do databáze. Bajty JSON jsou postupně zpracovány, rozděleny do samostatných hodnot sloupců a vloženy do tabulky RDBMS.
Příklad:
{ "ID":"001","name": "Ven", "Country": "Australia", "city": "Sydney", "Job Title":"Database Consultant"}
Výše uvedená data JSON jsou převedena na SQL jako níže..
Insert into test (id, name, country,city,job_title) values (001,'Ven','Australia','Sydney','Database Consultant');
Pokud jde o ukládání a zpracování dat JSON, existují různé databáze NoSQL, které je podporují, a nejoblíbenější je MongoDB. Pokud jde o databáze RDBMS, až do nedávné doby se s řetězci JSON zacházelo jako s normálním textem a neexistovaly žádné datové typy, které by konkrétně rozpoznávaly, ukládaly nebo zpracovávaly řetězce formátu JSON. PostgreSQL, nejoblíbenější open-source databáze RDBMS, přišla s datovým typem JSON, který se ukázal jako velmi přínosný pro výkon, funkčnost a škálovatelnost, pokud jde o práci s daty JSON.
PostgreSQL + JSON
Databáze PostgreSQL se stala stále populárnější od doby, kdy byl představen datový typ JSON. Ve skutečnosti PostgreSQL překonává MongoDB, pokud jde o zpracování velkého množství dat JSON. Aplikace mohou ukládat řetězce JSON v databázi PostgreSQL ve standardním formátu JSON. Vývojáři jen potřebují sdělit aplikaci, aby odeslala řetězce JSON do databáze jako datový typ json a načetla je zpět ve formátu JSON. Uložení řetězce JSON v datovém typu JSON má několik výhod ve srovnání s uložením stejného v datovém typu TEXT. Datový typ JSON může přijímat pouze platné řetězce ve formátu JSON, pokud řetězec není ve správném formátu JSON, dojde k chybě. Datový typ JSON pomáhá aplikaci provádět efektivní vyhledávání založené na indexu, což brzy podrobně uvidíme.
Datový typ JSON byl představen v příspěvku PostgreSQL-9.2, kde byla provedena významná vylepšení. Hlavní přírůstek přišel v PostgreSQL-9.4 s přidáním datového typu JSONB. JSONB je pokročilá verze datového typu JSON, která ukládá data JSON v binárním formátu. Toto je hlavní vylepšení, které výrazně změnilo způsob vyhledávání a zpracování dat JSON v PostgreSQL. Pojďme se podrobně podívat na výhody datových typů JSON.
Datové typy JSON a JSONB
Datový typ JSON ukládá řetězce ve formátu JSON jako text, který není příliš výkonný a nepodporuje mnoho funkcí souvisejících s JSON používaných pro vyhledávání. Podporuje pouze tradiční indexování B-TREE a nepodporuje jiné typy indexů, které jsou nezbytné pro rychlejší a efektivnější operace vyhledávání v datech JSON.
JSONB, pokročilá verze datového typu JSON, se důrazně doporučuje pro ukládání a zpracování dokumentů JSON. Podporuje širokou škálu operátorů json a oproti JSON má četné výhody, jako je ukládání řetězců ve formátu JSON v binárním formátu a podpora funkcí JSON a indexování pro efektivní vyhledávání.
Podívejme se na rozdíly.
JSON | JSONB | |
---|---|---|
1 | Velmi podobné datovému typu TEXT, který ukládá pouze platný dokument JSON. | Ukládá dokumenty JSON v binárním formátu. |
2 | Ukládá dokumenty JSON tak, jak jsou, včetně mezer. | Ořezává bílá místa a ukládá ve formátu, který přispívá k rychlejšímu a efektivnějšímu vyhledávání |
3 | Nepodporuje indexování FULL-TEXT-SEARCH | Podporuje indexování FULL-TEXT-SEARCH |
4 | Nepodporuje širokou škálu funkcí a operátorů JSON | Podporuje všechny funkce a operátory JSON |
Příklad pro #4 uvedený výše
JSON
Níže je tabulka s datovým typem JSON
dbt3=# \d product
Table "dbt3.product"
Column | Type | Collation | Nullable | Default
----------------+--------+-----------+----------+---------
item_code | bigint | | not null |
productdetails | json | | |
Indexes:
"product_pkey" PRIMARY KEY, btree (item_code)
Nepodporuje tradiční operátory JSON (jako „@>“ nebo „#>“). Fulltextové vyhledávání v datech JSON se provádí pomocí „@>“ nebo „#>“ v SQL, který není podporován datovým typem JSON
dbt3=# select * from product where productdetails @> '{"l_shipmode":"AIR"}' and productdetails @> '{"l_quantity":"27"}';
ERROR: operator does not exist: json @> unknown
LINE 1: select * from product where productdetails @> '{"l_shipmode"...
^
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
dbt3=#
JSONB
Níže je tabulka s datovým typem JSONB
dbt3=# \d products
Table "dbt3.products"
Column | Type | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
item_code | bigint | | not null |
order_details | jsonb | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (item_code)
Podporuje PLNÉ TEXTOVÉ VYHLEDÁVÁNÍ v datech JSON pomocí operátorů (např. „@>“)
dbt3=# select * from products where order_details @> '{"l_shipmode" : "AIR"}' limit 2;
item_code | order_details
-----------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
4 | {"l_partkey": 21315, "l_orderkey": 1, "l_quantity": 28, "l_shipdate": "1996-04-21", "l_shipmode": "AIR", "l_commitdate": "1996-03-30", "l_shipinstruct": "NONE", "l_extendedprice": 34616.7}
8 | {"l_partkey": 42970, "l_orderkey": 3, "l_quantity": 45, "l_shipdate": "1994-02-02", "l_shipmode": "AIR", "l_commitdate": "1994-01-04", "l_shipinstruct": "NONE", "l_extendedprice": 86083.6}
(2 rows)
Stáhněte si Whitepaper Today Správa a automatizace PostgreSQL s ClusterControlZjistěte, co potřebujete vědět k nasazení, monitorování, správě a škálování PostgreSQLStáhněte si Whitepaper Jak dotazovat data JSON
Podívejme se na některé funkce PostgreSQL JSON související s datovými operacemi Níže je uvedeno, jak data JSON vypadají v tabulce. Sloupec „podrobnosti_objednávky“ je typu JSONB
dbt3=# select * from product_details ;
item_code | order_details
-----------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | {"l_partkey": 1551894, "l_orderkey": 1, "l_quantity": 17, "l_shipdate": "1996-03-13", "l_shipmode": "TRUCK", "l_commitdate": "1996-02-12", "l_shipinstruct": "DELIVER IN PERSON", "l_extendedprice": 33078.9}
2 | {"l_partkey": 673091, "l_orderkey": 1, "l_quantity": 36, "l_shipdate": "1996-04-12", "l_shipmode": "MAIL", "l_commitdate": "1996-02-28", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 38306.2}
3 | {"l_partkey": 636998, "l_orderkey": 1, "l_quantity": 8, "l_shipdate": "1996-01-29", "l_shipmode": "REG AIR", "l_commitdate": "1996-03-05", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 15479.7}
4 | {"l_partkey": 21315, "l_orderkey": 1, "l_quantity": 28, "l_shipdate": "1996-04-21", "l_shipmode": "AIR", "l_commitdate": "1996-03-30", "l_shipinstruct": "NONE", "l_extendedprice": 34616.7}
5 | {"l_partkey": 240267, "l_orderkey": 1, "l_quantity": 24, "l_shipdate": "1996-03-30", "l_shipmode": "FOB", "l_commitdate": "1996-03-14", "l_shipinstruct": "NONE", "l_extendedprice": 28974}
6 | {"l_partkey": 156345, "l_orderkey": 1, "l_quantity": 32, "l_shipdate": "1996-01-30", "l_shipmode": "MAIL", "l_commitdate": "1996-02-07", "l_shipinstruct": "DELIVER IN PERSON", "l_extendedprice": 44842.9}
7 | {"l_partkey": 1061698, "l_orderkey": 2, "l_quantity": 38, "l_shipdate": "1997-01-28", "l_shipmode": "RAIL", "l_commitdate": "1997-01-14", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 63066.3}
8 | {"l_partkey": 42970, "l_orderkey": 3, "l_quantity": 45, "l_shipdate": "1994-02-02", "l_shipmode": "AIR", "l_commitdate": "1994-01-04", "l_shipinstruct": "NONE", "l_extendedprice": 86083.6}
9 | {"l_partkey": 190355, "l_orderkey": 3, "l_quantity": 49, "l_shipdate": "1993-11-09", "l_shipmode": "RAIL", "l_commitdate": "1993-12-20", "l_shipinstruct": "TAKE BACK RETURN", "l_extendedprice": 70822.1}
10 | {"l_partkey": 1284483, "l_orderkey": 3, "l_quantity": 27, "l_shipdate": "1994-01-16", "l_shipmode": "SHIP", "l_commitdate": "1993-11-22", "l_shipinstruct": "DELIVER IN PERSON", "l_extendedprice": 39620.3}
(10 rows)
Vyberte všechny kódy položek včetně jejich dat odeslání
dbt3=# select item_code, order_details->'l_shipdate' as shipment_date from product_details ;
item_code | shipment_date
-----------+---------------
1 | "1996-03-13"
2 | "1996-04-12"
3 | "1996-01-29"
4 | "1996-04-21"
5 | "1996-03-30"
6 | "1996-01-30"
7 | "1997-01-28"
8 | "1994-02-02"
9 | "1993-11-09"
10 | "1994-01-16"
(10 rows)
Získejte item_code, množství a cenu všech objednávek doručených letecky
dbt3=# select item_code, order_details->'l_quantity' as quantity, order_details->'l_extendedprice' as price, order_details->'l_shipmode' as price from product_details where order_details->>'l_shipmode'='AIR';
item_code | quantity | price | price
-----------+----------+---------+-------
4 | 28 | 34616.7 | "AIR"
8 | 45 | 86083.6 | "AIR"
(2 rows)
Operátory JSON „->“ a „->>“ se používají pro výběr a porovnání v dotazu SQL. Operátor „->“ vrátí pole objektu JSON jako pole v uvozovkách a operátor „->>“ vrátí pole objektu JSON jako TEXT. Výše uvedené dva SQL jsou příklady zobrazení hodnot polí JSON tak, jak jsou. Níže je uveden příklad extrahování pole JSON ve formě TEXT.
Níže je příklad načtení pole JSON ve formě TEXT
dbt3=# select item_code, order_details->>'l_shipdate' as shipment_date from product_details ;
item_code | shipment_date
-----------+---------------
1 | 1996-03-13
2 | 1996-04-12
3 | 1996-01-29
4 | 1996-04-21
5 | 1996-03-30
6 | 1996-01-30
7 | 1997-01-28
8 | 1994-02-02
9 | 1993-11-09
10 | 1994-01-16
(10 rows)
Existuje další operátor nazvaný „#>“, který se používá k dotazování na datovou část prvku JSON, který je součástí řetězce JSON. Podívejme se na příklad.
Níže jsou data v tabulce.
dbt3=# select * from test_json ;
id | details
-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
10000 | {"Job": "Database Consultant", "name": "Venkata", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Dee Why", "Country": "Australia"}}
20000 | {"Job": "Database Consultant", "name": "Smith", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Manly", "Country": "Australia"}}
30000 | {"Job": "Developer", "name": "John", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Brookvale", "Country": "Australia"}}
50000 | {"cars": {"Ford": [{"doors": 4, "model": "Taurus"}, {"doors": 4, "model": "Escort"}], "Nissan": [{"doors": 4, "model": "Sentra"}, {"doors": 4, "model": "Maxima"}, {"doors": 2, "model": "Skyline"}]}}
40000 | {"Job": "Architect", "name": "James", "Location": {"city": "Melbourne", "State": "NSW", "Suburb": "Trugnania", "Country": "Australia"}}
Chci vidět všechny podrobnosti s „State“ „NSW“ a „State“ je klíč objektu JSON, který je součástí klíče „Umístění“. Níže je uveden postup, jak zadat stejný dotaz.
dbt3=# select * from test_json where details #> '{Location,State}'='"NSW"';
id | details
-------+------------------------------------------------------------------------------------------------------------------------------------------------
10000 | {"Job": "Database Consultant", "name": "Venkata", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Dee Why", "Country": "Australia"}}
20000 | {"Job": "Database Consultant", "name": "Smith", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Manly", "Country": "Australia"}}
30000 | {"Job": "Developer", "name": "John", "Location": {"city": "Sydney", "State": "NSW", "Suburb": "Brookvale", "Country": "Australia"}}
30000 | {"Job": "Architect", "name": "James", "Location": {"city": "Melbourne", "State": "NSW", "Suburb": "Trugnania", "Country": "Australia"}}
(4 rows)
S daty JSON lze provádět aritmetické operace. Přetypování typu je nutné, protože datová část sloupce JSON je TEXT.
dbt3=# select item_code, order_details->'l_quantity' as quantity, order_details->'l_extendedprice' as price, order_details->'l_shipmode' as price from product_details where (order_details->'l_quantity')::int > 10;
item_code | quantity | price | price
-----------+----------+---------+---------
1 | 17 | 33078.9 | "TRUCK"
2 | 36 | 38306.2 | "MAIL"
4 | 28 | 34616.7 | "AIR"
5 | 24 | 28974 | "FOB"
6 | 32 | 44842.9 | "MAIL"
7 | 38 | 63066.3 | "RAIL"
8 | 45 | 86083.6 | "AIR"
9 | 49 | 70822.1 | "RAIL"
10 | 27 | 39620.3 | "SHIP"
(9 rows)
Kromě výše uvedeného lze na JSON provádět také následující operace pomocí SQL včetně JOINů
- Řazení dat pomocí klauzule ORDER BY
- Agregace pomocí agregačních funkcí, jako je SUM, AVG, MIN, MAX atd.
- Seskupit data pomocí klauzule GROUP BY
A co výkon?
Data ve sloupcích JSON budou mít textový charakter a na základě velikosti dat lze očekávat problémy s výkonem. Prohledávání dat JSON může zabrat čas a výpočetní výkon, což má za následek pomalé odezvy aplikace (aplikací). Pro správce databází je nezbytné zajistit, aby SQL zasahující do sloupců JSON reagovaly dostatečně rychle a poskytovaly dobrý výkon. Vzhledem k tomu, že extrakce dat se provádí pomocí SQL, možnost, kterou by správci databází hledali, je možnost indexování a ano, typy dat JSON podporují možnosti indexování.
Pojďme se podívat na možnosti indexování, které nám JSON přináší.
Indexování JSONB
Datový typ JSONB podporuje indexování FULL-TEXT-SEARCH. Toto je nejdůležitější schopnost JSONB, na kterou se správci databází budou těšit při používání datových typů JSONB. Normální index v klíči objektu JSON nemusí pomoci při použití operátorů specifických pro JSON ve vyhledávacích dotazech. Níže je dotaz TEXTOVÉHO VYHLEDÁVÁNÍ, který se týká FULL-TABLE-SCAN
dbt3=# explain select * from products where order_details @> '{"l_shipmode" : "AIR"}';
QUERY PLAN
--------------------------------------------------------------------
Seq Scan on products (cost=0.00..4205822.65 rows=59986 width=252)
Filter: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)
(2 rows)
JSONB podporuje typ indexu FULL-TEXT-SEARCH zvaný GIN, který pomáhá s dotazy jako výše.
Nyní mi dovolte vytvořit index GIN a uvidíme, zda to pomůže
dbt3=# create index od_gin_idx on products using gin(order_details jsonb_path_ops);
CREATE INDEX
Pokud můžete pozorovat níže, dotaz zvedne index GIN
dbt3=# explain select * from products where order_details @> '{"l_shipmode" : "AIR"}';
QUERY PLAN
-------------------------------------------------------------------------------
Bitmap Heap Scan on products (cost=576.89..215803.18 rows=59986 width=252)
Recheck Cond: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)
-> Bitmap Index Scan on od_gin_idx (cost=0.00..561.90 rows=59986 width=0)
Index Cond: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)
A index B-TREE místo GIN by NEPOmohl
dbt3=# create index idx on products((order_details->>'l_shipmode'));
CREATE INDEX
dbt3=# \d products
Table "dbt3.products"
Column | Type | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
item_code | bigint | | not null |
order_details | jsonb | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (item_code)
"idx" btree ((order_details ->> 'l_shipmode'::text))
Níže vidíte, dotaz preferuje FULL-TABLE-SCAN
dbt3=# explain select * from products where order_details @> '{"l_shipmode" : "AIR"}';
QUERY PLAN
--------------------------------------------------------------------
Seq Scan on products (cost=0.00..4205822.65 rows=59986 width=252)
Filter: (order_details @> '{"l_shipmode": "AIR"}'::jsonb)
Co je index GIN?
GIN je zkratka pro Generalized Inverted Index. Základní schopností GIN Index je urychlit fulltextové vyhledávání. Při vyhledávání na základě konkrétních klíčů nebo prvků v TEXTU nebo dokumentu je správnou volbou GIN Index. GIN Index ukládá páry „Klíč“ (nebo prvek nebo hodnota) a „seznam pozic“. Seznam pozic je rowID klíče. To znamená, že pokud se „Klíč“ vyskytuje na více místech v dokumentu, index GIN uloží klíč pouze jednou spolu s jeho pozicí výskytů, což nejen udržuje kompaktní velikost indexu GIN a také pomáhá urychlit vyhledávání způsob. Toto je vylepšení v Postgres-9.4.
Výzvy s indexem GIN
V závislosti na složitosti dat může být údržba indexů GIN nákladná. Vytváření indexů GIN spotřebovává čas a zdroje, protože index musí prohledávat celý dokument, aby našel klíče a jejich ID řádků. Může to být ještě náročnější, pokud je index GIN nafouknutý. Také velikost indexu GIN může být velmi velká na základě velikosti a složitosti dat.
Indexování JSON
JSON nepodporuje textové vyhledávání a indexy jako GIN
dbt3=# create index pd_gin_idx on product using gin(productdetails jsonb_path_ops);
ERROR: operator class "jsonb_path_ops" does not accept data type json
Normální indexování jako B-TREE je podporováno JSON i JSONB
Ano, normální indexy jako B-TREE Index podporují datové typy JSON i JSONB a nejsou vhodné pro operace textového vyhledávání. Každý klíč objektu JSON lze indexovat jednotlivě, což by skutečně pomohlo POUZE v případě, že je stejný klíč objektu použit v klauzuli WHERE.
Dovolte mi vytvořit index B-TREE na JSONB a podívat se, jak to funguje
dbt3=# create index idx on products((order_details->>'l_shipmode'));
CREATE INDEX
dbt3=# \d products
Table "dbt3.products"
Column | Type | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
item_code | bigint | | not null |
order_details | jsonb | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (item_code)
"idx" btree ((order_details ->> 'l_shipmode'::text))
Již jsme se naučili výše, že index B-TREE NENÍ užitečný pro urychlení SQL provádějících FULL-TEXTOVÉ VYHLEDÁVÁNÍ v datech JSON pomocí operátorů (jako „@>“) a takové indexy by POUZE pomohly urychlit dotazy jako ten níže, což jsou typické SQL typu RDBMS (které nejsou vyhledávacími dotazy). Každý klíč objektu JSON lze indexovat samostatně, což by pomohlo urychlit dotazy, když se tyto indexované klíče objektů JSON používají klauzuli WHERE.
V příkladu níže je použit klíč objektu „l_shipmode“ v klauzuli WHERE a od té doby je indexováno, dotaz se chystá prohledat index. Pokud si přejete hledat pomocí jiného Object Key, pak by dotaz zvolil provedení FULL-TABLE-SCAN.
dbt3=# explain select * from products where order_details->>'l_shipmode'='AIR';
QUERY PLAN
---------------------------------------------------------------------------------
Index Scan using idx on products (cost=0.56..1158369.34 rows=299930 width=252)
Index Cond: ((order_details ->> 'l_shipmode'::text) = 'AIR'::text)
Totéž funguje také s datovým typem JSON
dbt3=# create index idx on products((order_details->>'l_shipmode'));
CREATE INDEX
dbt3=# \d products
Table "dbt3.products"
Column | Type | Collation | Nullable | Default
---------------+--------+-----------+----------+---------
item_code | bigint | | not null |
order_details | json | | |
Indexes:
"products_pkey" PRIMARY KEY, btree (item_code)
"idx" btree ((order_details ->> 'l_shipmode'::text))
Pokud můžete pozorovat, dotaz používá Index
dbt3=# explain select * from products where order_details->>'l_shipmode'='AIR';
QUERY PLAN
---------------------------------------------------------------------------------
Index Scan using idx on products (cost=0.56..1158369.34 rows=299930 width=252)
Index Cond: ((order_details ->> 'l_shipmode'::text) = 'AIR'::text)
Závěr
Zde je několik věcí, které je třeba pamatovat při používání PostgreSQL JSON Data...
- PostgreSQL je jednou z nejlepších možností pro ukládání a zpracování dat JSON
- Se všemi výkonnými funkcemi může být PostgreSQL vaší databází dokumentů
- Viděl jsem architektury, kde jsou vybrány dvě nebo více datových úložišť, se směsí databází PostgreSQL a NoSQL, jako je databáze MongoDB nebo Couchbase. REST API by pomohlo aplikacím přenést data do různých datových úložišť. S PostgreSQL podporujícím JSON se této složitosti architektury lze vyhnout pouhým výběrem jednoho úložiště dat.
- Data JSON v PostgreSQL lze dotazovat a indexovat, což poskytuje neuvěřitelný výkon a škálovatelnost
- Datový typ JSONB je nejpreferovanější možností, protože má dobré úložiště a výkon. Plně podporuje FULL-TEXT-SEARCHING a indexování. Poskytuje dobrý výkon
- Datový typ JSON použijte pouze v případě, že chcete ukládat řetězce JSON jako JSON a neprovádíte příliš složité textové vyhledávání.
- Největší výhodou JSON v PostgreSQL je, že vyhledávání lze provádět pomocí SQL
- Výkon vyhledávání JSON v PostgreSQL byl srovnatelný s nejlepšími databázemi NoSQL, jako je MongoDB