標籤:
根據Betteridge定律(任何頭條的設問句可以用一個詞來回答:不是),除非你的JSON資料很少修改,並且查詢很多。
最新版的PostgreSQL添加更多對JSON的支援,我們曾經問過PostgreSQL是否可以替換MongoDB作為JSON資料庫,答案顯而易見,但我們更希望的是,啊哈,這個問題由讀者來問了。
“PostgreSQL不是已經有一些json的支援了嗎?”
是的,在PostgreSQL 9.4之前的版本也有JSON 資料類型了,你可以這樣:
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資訊,如果做?
select doc->>‘address‘ FROM justjson; ?column?--------------------------------- { + "line1":"52 The Elms", + "line2":"Elmstreet", + "postcode":"ES1 1ES" + }(1 row)
doc欄位的 ->> 操作符是查詢JSON對象的某個欄位並返迴文本,用數字也可以當作數組的索引,但仍返迴文本。跟 ->> 類似的還有 -> 操作符,返回不轉文本的內容,可以用它來導航搜尋JSON對象,如:
select doc->‘address‘->>‘postcode‘ FROM justjson; ?column?---------- ES1 1ES(1 row)
還有個更簡短的寫法來指定搜尋路徑,用 #>> 操作符,如夢:
select doc#>>‘{address,postcode}‘ FROM justjson; ?column?---------- ES1 1ES(1 row)
通過儲存完整的JSON資料類型可使其跟來源資料完全一樣並且不會丟失內容,但為保持完全一致也帶來了成本,效能的缺失,而且不能索引...所有,儘管可以很方便的維持一致性和保持JSON文檔,但仍有很大的提升空間,所以引入了JSONB。
"JSONB有什麼不同?"
JSONB可以將整個JSON文檔轉有層級的KEY/VALUE資料對,所有的空白字元刪除了,重複鍵只保留最後一次,鍵也沒有排序,而是用HASH來儲存了,上面的例子中用JSONB的版本的話,看來起類似這樣:
>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文檔資料庫如MongoDB或RethinkDB。
參考
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/
<譯:朱淦 [email protected]>
PostgreSQL是不是你的下一個JSON資料庫?