PostgreSQL是否是你的下一個JSON數據庫?

根據Betteridge定律(任何頭條的設問句能夠用一個詞來回答:不是),除非你的JSON數據不多修改,而且查詢不少。
html

最新版的PostgreSQL添加更多對JSON的支持,咱們曾經問過PostgreSQL是否能夠替換MongoDB做爲JSON數據庫,答案顯而易見,但咱們更但願的是,啊哈,這個問題由讀者來問了。sql

「PostgreSQL不是已經有一些json的支持了嗎?」

是的,在PostgreSQL 9.4以前的版本也有JSON 數據類型了,你能夠這樣:mongodb

CREATE TABLE justjson ( id INTEGER, doc JSON)
>INSERT INTO justjson VALUES ( 1, '{
    "name":"fred",
    "address":{
        "line1":"52 The Elms",
        "line2":"Elmstreet",
        "postcode":"ES1 1ES"
        }
    }');

保存了JSON的原始文本到數據庫,包括空白行和鍵順序及從新的鍵,咱們來查看下保存的數據:數據庫

>SELECT * FROM justjson;
 id |               doc
----+---------------------------------
  1 | {                              +
    |     "name":"fred",             +
    |     "address":{                +
    |         "line1":"52 The Elms", +
    |         "line2":"Elmstreet",   +
    |         "postcode":"ES1 1ES"   +
    |         }                      +
    |     }
(1 row)

跟保存以前的文本如出一轍,但咱們仍能夠解析出具體的數據出來,PostgreSQL提供了一套JSON的操做方法進行查找,例如,咱們只要查出address信息,若是作?express

select doc->>'address' FROM justjson;  
            ?column?
---------------------------------
 {                              +
         "line1":"52 The Elms", +
         "line2":"Elmstreet",   +
         "postcode":"ES1 1ES"   +
         }
(1 row)

doc字段的 ->> 操做符是查詢JSON對象的某個字段並返回文本,用數字也能夠看成數組的索引,但仍返回文本。跟 ->> 相似的還有 -> 操做符,返回不轉文本的內容,能夠用它來導航搜索JSON對象,如:json

select doc->'address'->>'postcode' FROM justjson;  
 ?column?
----------
 ES1 1ES
(1 row)

還有個更簡短的寫法來指定搜索路徑,用 #>> 操做符,如夢:c#

select doc#>>'{address,postcode}' FROM justjson;  
 ?column?
----------
 ES1 1ES
(1 row)

經過保存完整的JSON數據類型可以使其跟源數據徹底同樣而且不會丟失內容,但爲保持徹底一致也帶來了成本,性能的缺失,並且不能索引...全部,儘管能夠很方便的維持一致性和保持JSON文檔,但仍有很大的提高空間,因此引入了JSONB。數組

"JSONB有什麼不一樣?"

JSONB能夠將整個JSON文檔轉有層級的KEY/VALUE數據對,全部的空白字符刪除了,重複鍵只保留最後一次,鍵也沒有排序,而是用HASH來保存了,上面的例子中用JSONB的版本的話,看來起相似這樣:post

>CREATE TABLE justjsonb ( id INTEGER, doc JSONB)
>INSERT INTO justjsonb VALUES ( 1, '{
    "name":"fred",
    "address":{
        "line1":"52 The Elms",
        "line2":"Elmstreet",
        "postcode":"ES1 1ES"
        }
    }');
>SELECT * FROM justjsonb;
 id |                                                doc
----+----------------------------------------------------------------------------------------------------
  1 | {"name": "fred", "address": {"line1": "52 The Elms", "line2": "Elmstreet", "postcode": "ES1 1ES"}}
(1 row)

能夠看到,全部非文本內容都消失了,替換成JSON文檔須要的最少格式,這種壓縮方式表示當數據插入時會自動格式化,這樣能夠減小以後訪問數據分析處理的工做量。性能

"PostgreSQL的這種數據有點像HSTORE"

看到鍵值對,JSONB還真有點像PostgreSQL的HSTORE擴展,它也能夠保存鍵值對,但它是一個擴展,而,JSONB(以及JSON)是在PostgreSQL內核的,HSTORE只有一級層級,但PostgreSQL能夠有嵌套的元素,而且,HSTORE只能存字符串,而JSONB還能夠存JSON的所數字類型。

「那JSONB到底帶給我啥好處呢?」

索引,處處用上索引,你不能在PostgreSQL對JSON類型建立真正的索引,你能夠建立表達式索引(expression indexes),但只限於你想索引的內容,例如:

create index justjson_postcode on justjson ((doc->'address'->>'postcode'));

只有郵編(postcode)索引了,其它都沒有索引。

而JSONB,支持GIN索引,一種通用返轉索引(Generalized Inverted Index),PostgreSQL提供了另一套索引操做符來支持,包括 @> 包括JSON,<@ 最包含,? 測試字符串是否存在,?| 任意字符串是否存在,?& 全部存大的字符串。

有兩類索引可用,默認叫 json_ops,它支持全部操做符(譯者:指普通json操做符)和一個只支持&>操做符的jsonb_path_ops索引(譯者:指索引操做符),默認索引給JSON中的每一個鍵值都建立了索引,其實 jsonb_path_ops只建立了一個比默認複雜的更高壓縮的hash表索引,但默認索引擔任更多操做能力同時增長了空間成本。給表添加一些數據,咱們再來看看某個郵編,若是咱們建立了一個默認的GIN JSON索引而後查詢:

explain select * from justjsonb where doc @> '{ "address": { "postcode":"HA36CC" } }';  
                           QUERY PLAN
-----------------------------------------------------------------
 Seq Scan on justjsonb  (cost=0.00..3171.14 rows=100 {"address": {"postcode": "HA36CC"}}'::jsonb)
(2 rows)

能夠看出來是順序掃瞄表,若是咱們加個默認的JSON GIN索引後再看看有什麼不一樣?

> create index justjsonb_gin on justjsonb using gin (doc);
> explain select * from justjsonb where doc @> '{ "address": { "postcode":"HA36CC" } }';
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Bitmap Heap Scan on justjsonb  (cost=40.78..367.62 rows=100 {"address": {"postcode": "HA36CC"}}'::jsonb)
   ->  Bitmap Index Scan on justjsonb_gin  (cost=0.00..40.75 rows=100 {"address": {"postcode": "HA36CC"}}'::jsonb)
(4 rows)

搜索性能提高很大,但隱藏了空間的耗費,例中是41%的數據大小,讓咱們刪除索引重複執行jsonb_path_ops GIN索引。

> create index justjsonb_gin on justjsonb using gin (doc jsonb_path_ops);
> explain select * from justjsonb where doc @> '{ "address": { "postcode":"HA36CC" } }';
                                  QUERY PLAN
-------------------------------------------------------------------------------
 Bitmap Heap Scan on justjsonb  (cost=16.78..343.62 rows=100 {"address": {"postcode": "HA36CC"}}'::jsonb)
   ->  Bitmap Index Scan on justjsonb_gin  (cost=0.00..16.75 rows=100 {"address": {"postcode": "HA36CC"}}'::jsonb)
(4 rows)

總成本低了點,索引體積小了不少,這是典型的建立索引速度和空間平衡的方法,但比順序掃瞄性能高不少。


「我應該用它做爲個人JSON數據庫嗎?」

若是你常常更新你的JSON文檔,回答是否認的,PostgreSQL最擅長的是存儲和攻取JSON文檔及他們的字段,但儘管如此你能夠取出單個字段,你也不能更新單個字段;實際上你能夠,將整個JSON解析出來,添加新的字段再寫回,讓JSON分析器處理重複,但你很明顯不想依賴這個。

若是你的主要數據用關係數據庫用得很好,JSON數據只是一羣補充(靜態數據),那麼用PostgreSQL就能夠了,並且用JSONB表示和索引能力將更高效。另外,若是你的數據模型是可變內容的集合,那麼你可能會尋找同樣主流工業級的json文檔數據庫如MongoDBRethinkDB

參考

PostgreSQL vs MongoDB http://my.oschina.net/Suregogo/blog/358277

Query JSON in PostgreSQL http://schinckel.net/2014/05/25/querying-json-in-postgres/

原文: https://www.compose.io/articles/is-postgresql-your-next-json-database/

<譯:朱淦 350050183@qq.com 2015.8.9>

相關文章
相關標籤/搜索