根據RFC 7159中的說明,JSON 數據類型是用來存儲 JSON(JavaScript Object Notation)數據的。這種數據也能夠被存儲爲text
,可是 JSON 數據類型的優點在於能強制要求每一個被存儲的值符合 JSON 規則。也有不少 JSON 相關的函數和操做符能夠用於存儲在這些數據類型中的數據html
PostgreSQL支持兩種 JSON 數據類型:json 和 jsonb。它們幾乎接受徹底相同的值集合做爲輸入。二者最大的區別是效率。json數據類型存儲輸入文本的精準拷貝,處理函數必須在每 次執行時必須從新解析該數據。而jsonb數據被存儲在一種分解好的二進制格式中,由於須要作附加的轉換,它在輸入時要稍慢一些。可是 jsonb在處理時要快不少,由於不須要從新解析。python
重點:jsonb支持索引
因爲json類型存儲的是輸入文本的準確拷貝,存儲時會空格和JSON 對象內部的鍵的順序。若是一個值中的 JSON 對象包含同一個鍵超過一次,全部的鍵/值對都會被保留( 處理函數會把最後的值看成有效值)。git
jsonb不保留空格、不保留對象鍵的順序而且不保留重複的對象鍵。若是在輸入中指定了重複的鍵,只有最後一個值會被保留。github
推薦把JSON 數據存儲爲jsonb
在把文本 JSON 輸入轉換成jsonb時,JSON的基本類型(RFC 7159 )會被映射到原生的 PostgreSQL類型。所以,jsonb數據有一些次要額外約束。好比:
jsonb將拒絕除 PostgreSQL numeric數據類型範圍以外的數字,而json則不會。golang
JSON 基本類型和相應的PostgreSQL類型sql
JSON 基本類型 | PostgreSQL類型 | 註釋 |
---|---|---|
string |
text |
不容許\u0000 ,若是數據庫編碼不是 UTF8,非 ASCII Unicode 轉義也是這樣 |
number |
numeric |
不容許NaN 和 infinity 值 |
boolean |
boolean |
只接受小寫true 和false 拼寫 |
null |
(無) | SQL NULL 是一個不一樣的概念 |
-- 簡單標量/基本值 -- 基本值能夠是數字、帶引號的字符串、true、false或者null SELECT '5'::json; -- 有零個或者更多元素的數組(元素不須要爲同一類型) SELECT '[1, 2, "foo", null]'::json; -- 包含鍵值對的對象 -- 注意對象鍵必須老是帶引號的字符串 SELECT '{"bar": "baz", "balance": 7.77, "active": false}'::json; -- 數組和對象能夠被任意嵌套 SELECT '{"foo": [true, "bar"], "tags": {"a": 1, "b": null}}'::json; -- "->" 經過鍵得到 JSON 對象域 結果爲json對象 select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->'nickname' as nickname; nickname ------------- "goodspeed" -- "->>" 經過鍵得到 JSON 對象域 結果爲text select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->>'nickname' as nickname; nickname ----------- goodspeed -- "->" 經過鍵得到 JSON 對象域 結果爲json對象 select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->'nickname' as nickname; nickname ------------- "goodspeed" -- "->>" 經過鍵得到 JSON 對象域 結果爲text select '{"nickname": "goodspeed", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->>'nickname' as nickname; nickname ----------- goodspeed
當一個 JSON 值被輸入而且接着不作任何附加處理就輸出時, json會輸出和輸入徹底相同的文本,而jsonb 則不會保留語義上沒有意義的細節數據庫
SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::json; json ------------------------------------------------- {"bar": "baz", "balance": 7.77, "active":false} -- jsonb 不會保留語義上的細節,key 的順序也和原始數據不一致 SELECT '{"bar": "baz", "balance": 7.77, "active":false}'::jsonb; jsonb -------------------------------------------------- {"bar": "baz", "active": false, "balance": 7.77}
在使用JSON文檔時,推薦 將JSON 文檔存儲爲固定的結構。(該結構是非強制的,可是有一個可預測的結構會使集合的查詢更容易。 )
設計JSON文檔建議:
任何更新都在整行上要求一個行級鎖。爲了減小鎖爭奪,JSON 文檔應該每一個表示 一個原子數據(業務規則上的不可拆分,可獨立修改的數據)。這些經常使用的比較操做符只對jsonb 有效,而不適用於jsonjson
經常使用的比較操做符小程序
操做符 | 描述 |
---|---|
< |
小於 |
> |
大於 |
<= |
小於等於 |
>= |
大於等於 |
= |
等於 |
<> or != |
不等於 |
json和jsonb 操做符設計模式
-> 和 ->>
操做符使用 ->> 查出的數據爲text
使用 -> 查出的數據爲json 對象
-- nickname 爲 gs 的用戶 這裏使用 ->> 查出的數據爲text,因此匹配項也應該是text select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->>'nickname' = 'gs'; select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->>'nickname' = 'gs'; -- 使用 -> 查詢,會拋出錯誤,這裏不管匹配項是text類型的 'gs' 仍是 json 類型的 '"gs"'::json都會拋出異常,json 類型不支持 等號(=)操做符 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->'nickname' = '"gs"'; ERROR: operator does not exist: json = unknown -- json 類型不支持 "=" 操做符 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json->'nickname' = '"gs"'::json; ERROR: operator does not exist: json = json -- jsonb 格式是能夠查詢成功的,這裏使用 -> 查出的數據爲json 對象,因此匹配項也應該是json 對象 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->'nickname' = '"gs"';
#> 和 #>>
操做符使用 #>> 查出的數據爲text
使用 #> 查出的數據爲json 對象
select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json#>'{tags,0}' as tag; tag ---------- "python" select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json#>>'{tags,0}' as tag; tag -------- python select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb#>'{tags,0}' = '"python"'; ?column? ---------- t select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb#>>'{tags,0}' = 'python'; ?column? ---------- t select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json#>>'{tags,0}' = 'python'; ?column? ---------- t -- 會拋出錯誤,這裏不管匹配項是text類型的 'python' 仍是 json 類型的 '"python"'::json都會拋出異常,json 類型不支持 等號(=)操做符 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::json#>'{tags,0}' = '"python"'; ERROR: operator does not exist: json = unknown
額外的jsonb操做符
@>
操做符-- nickname 爲 nickname 的用戶 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb @> '{"nickname": "gs"}'::jsonb; -- 等同於如下查詢 -- 這裏使用 -> 查出的數據爲json 對象,因此匹配項也應該是json 對象 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->'nickname' = '"gs"'; select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->>'nickname' = 'gs'; -- 查詢有 python 和 golang 標籤的數據 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb @> '{"tags": ["python", "golang"]}'; ?column? ---------- t
?
操做符、?|
操做符和?&
操做符-- 查詢有 avatar 屬性的用戶 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb ? 'avatar'; -- 查詢有 avatar 屬性 而且avatar 數據不爲空的數據 select '{"nickname": "gs", "avatar": null, "tags": ["python", "golang", "db"]}'::jsonb->>'avatar' is not null; -- 查詢 有 avatar 或 tags 的數據 select '{"nickname": "gs", "tags": ["python", "golang", "db"]}'::jsonb ?| array['avatar', 'tags']; ?column? ---------- t -- 查詢 既有 avatar 又有 tags 的用戶 select '{"nickname": "gs", "tags": ["python", "golang", "db"]}'::jsonb ?& array['avatar', 'tags']; ?column? ---------- f -- 查詢 tags 中包含 python 標籤的數據 select '{"nickname": "gs", "avatar": "avatar_url", "tags": ["python", "golang", "db"]}'::jsonb->'tags' ? 'python'; ?column? ---------- t
-- 更新 account content 字段(覆蓋式更新) update account set content = jsonb_set(content, '{}', '{"nickname": "gs", "tags": ["python", "golang", "db"]}', false); -- 修改nickanme爲nickanme 的用戶標籤 update account set content = jsonb_set(content, '{tags}', '["test", "心理"]', true) where content @> '{"nickname": "nickname"}'::jsonb; update account set content = jsonb_set(content, '{tags}', '["test", "心理", "醫療"]', true) where content @> '{"nickname": "nickname"}'::jsonb; -- 更新account content字段中 weixin_mp 的值(若是沒有會建立) update account set content = jsonb_set(content, '{weixin_mp}', '"weixin_mp5522bd28-ed4d-11e8-949c-7200014964f0"', true) where id='5522bd28-ed4d-11e8-949c-7200014964f0'; -- 更新account 去除content 中weixin 字段(若是沒有weixin 字段也不會拋出異常) update account set content= content - 'weixin' where id='5522bd28-ed4d-11e8-949c-7200014964f0';
做爲縮進JSON文本返回from_json。
select jsonb_pretty('[{"f1":1,"f2":null},2,null,3]'); jsonb_pretty -------------------- [ + { + "f1": 1, + "f2": null+ }, + 2, + null, + 3 + ] (1 row)
jsonb_set() 函數參數以下:
jsonb_set(target jsonb, // 須要修改的數據 path text[], // 數據路徑 new_value jsonb, // 新數據 create_missing boolean default true)
若是create_missing 是true (缺省是true),而且path指定的路徑在target 中不存在,那麼target將包含path指定部分, new_value替換部分, 或者new_value添加部分。
-- target 結構 select jsonb_pretty('[{"f1":1,"f2":null},2]'); jsonb_pretty -------------------- [ + { + "f1": 1, + "f2": null+ }, + 2 + ] -- 更新 target 第0 個元素 key 爲 f1 的值,若是f1 不存在 忽略 select jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}','[2,3,4]', false); jsonb_set --------------------------------------------- [{"f1": [2, 3, 4], "f2": null}, 2, null, 3] -- 更新 target 第0 個元素 key 爲 f3 的值,若是f3 不存在 建立 select jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}','[2,3,4]'); jsonb_set --------------------------------------------- [{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2] -- 更新 target 第0 個元素 key 爲 f3 的值,若是f3 不存在 忽略 select jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}','[2,3,4]', false); jsonb_set --------------------------------------------- [{"f1": 1, "f2": null}, 2]
詳細的json 函數和操做符能夠參考文檔:JSON 函數和操做符
咱們使用下面的例子來講明一下json 的查詢性能
-- account 表 id 使用uuid 類型,須要先添加uuid-ossp模塊。 CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -- create table create table account (id UUID NOT NULL PRIMARY KEY default uuid_generate_v1(), content jsonb, created_at timestamptz DEFAULT CURRENT_TIMESTAMP, updated_at timestamptz DEFAULT CURRENT_TIMESTAMP); json=> \d account Table "public.account" Column | Type | Collation | Nullable | Default --------------+--------------------------+-----------+----------+-------------------- id | uuid | | not null |uuid_generate_v1() content | jsonb | | | created_at | timestamp with time zone | | | CURRENT_TIMESTAMP updated_at | timestamp with time zone | | | CURRENT_TIMESTAMP Indexes: "account_pkey" PRIMARY KEY, btree (id)
一個好的實踐是把 created_at和 updated_at 也放入jsonb 字段,這裏只是示例
content 數據結構爲:
content = { "nickname": {"type": "string"}, "avatar": {"type": "string"}, "weixin": {"type": "string"}, "tags": {"type": "array", "items": {"type": "string"}}, }
批量插入數據
-- 插入100w條有 nickname avatar tags 爲["python", "golang", "c"]的數據 insert into account select uuid_generate_v1(), ('{"nickname": "nn-' || round(random()*20000000) || '", "avatar": "avatar_url", "tags": ["python", "golang", "c"]}')::jsonb from (select * from generate_series(1,100000)) as tmp; -- 插入100w條有 nickname tags 爲["python", "golang"]的數據 insert into account select uuid_generate_v1(), ('{"nickname": "nn-' || round(random()*2000000) || '", "tags": ["python", "golang"]}')::jsonb from (select * from generate_series(1,1000000)) as tmp; -- 插入100w條有 nickname tags 爲["python"]的數據 insert into account select uuid_generate_v1(), ('{"nickname": "nn-' || round(random()*2000000) || '", "tags": ["python"]}')::jsonb from (select * from generate_series(1,1000000)) as tmp;
--content 中有avatar key 的數據條數 count(*) 查詢不是一個好的測試語句,就算是有索引,也只能起到過濾的做用,若是結果集比較大,查詢速度仍是會很慢 explain analyze select count(*) from account where content::jsonb ? 'avatar'; QUERY PLAN ---------------------------------------------------------------------------------------- Finalize Aggregate (cost=29280.40..29280.41 rows=1 width=8) (actual time=170.366..170.366 rows=1 loops=1) -> Gather (cost=29280.19..29280.40 rows=2 width=8) (actual time=170.119..174.451 rows=3 loops=1) Workers Planned: 2 Workers Launched: 2 -> Partial Aggregate (cost=28280.19..28280.20 rows=1 width=8) (actual time=166.034..166.034 rows=1 loops=3) -> Parallel Seq Scan on account (cost=0.00..28278.83 rows=542 width=0) (actual time=0.022..161.937 rows=33333 loops=3) Filter: (content ? 'avatar'::text) Rows Removed by Filter: 400000 Planning Time: 0.048 ms Execution Time: 174.486 ms -- content 中沒有avatar key 的數據條數 explain analyze select count(*) from account where content::jsonb ? 'avatar' = false; QUERY PLAN ---------------------------------------------------------------------------------------- Finalize Aggregate (cost=30631.86..30631.87 rows=1 width=8) (actual time=207.770..207.770 rows=1 loops=1) -> Gather (cost=30631.65..30631.86 rows=2 width=8) (actual time=207.681..212.357 rows=3 loops=1) Workers Planned: 2 Workers Launched: 2 -> Partial Aggregate (cost=29631.65..29631.66 rows=1 width=8) (actual time=203.565..203.565 rows=1 loops=3) -> Parallel Seq Scan on account (cost=0.00..28278.83 rows=541125 width=0) (actual time=0.050..163.629 rows=400000 loops=3) Filter: (NOT (content ? 'avatar'::text)) Rows Removed by Filter: 33333 Planning Time: 0.050 ms Execution Time: 212.393 ms
--查詢content 中nickname 爲nn-194318的數據 explain analyze select * from account where content@>'{"nickname": "nn-194318"}'; QUERY PLAN ---------------------------------------------------------------------------------------- Gather (cost=1000.00..29408.83 rows=1300 width=100) (actual time=0.159..206.990 rows=1 loops=1) Workers Planned: 2 Workers Launched: 2 -> Parallel Seq Scan on account (cost=0.00..28278.83 rows=542 width=100) (actual time=130.867..198.081 rows=0 loops=3) Filter: (content @> '{"nickname": "nn-194318"}'::jsonb) Rows Removed by Filter: 433333 Planning Time: 0.047 ms Execution Time: 207.007 ms -- 對應的查詢id 爲 'b5b3ed06-7d35-11e9-b3ea-00909e9dab1d' 的數據 explain analyze select * from account where id='b5b3ed06-7d35-11e9-b3ea-00909e9dab1d'; QUERY PLAN ---------------------------------------------------------------------------------------- Index Scan using account_pkey on account (cost=0.43..8.45 rows=1 width=100) (actual time=0.912..0.914 rows=1 loops=1) Index Cond: (id = 'b5b3ed06-7d35-11e9-b3ea-00909e9dab1d'::uuid) Planning Time: 0.348 ms Execution Time: 0.931 ms
經過結果能夠看到 使用 jsonb 查詢和使用主鍵查詢速度差別巨大,經過看查詢分析記錄能夠看到,這兩個語句最大的差異在於使用主鍵的查詢用到了索引,而content nickname 的查詢沒有索引可使用。
接下來測試一下使用索引時的查詢速度。
JSONB 最經常使用的是GIN
索引,GIN 索引能夠被用來有效地搜索在大量jsonb文檔(數據)中出現 的鍵或者鍵值對。
GIN(Generalized Inverted Index, 通用倒排索引) 是一個存儲對(key, posting list)集合的索引結構,其中key是一個鍵值,而posting list 是一組出現過key的位置。如(‘hello', '14:2 23:4')中,表示hello在14:2和23:4這兩個位置出現過,在PG中這些位置實際上就是元組的tid(行號,包括數據塊ID(32bit),以及item point(16 bit) )。在表中的每個屬性,在創建索引時,均可能會被解析爲多個鍵值,因此同一個元組的tid可能會出如今多個key的posting list中。
經過這種索引結構能夠快速的查找到包含指定關鍵字的元組,所以GIN索引特別適用於多值類型的元素搜索,好比支持全文搜索,數組中元素的搜索,而PG的GIN索引模塊最初也是爲了支持全文搜索而開發的。
jsonb
的默認 GIN 操做符類支持使用頂層鍵存在運算符?
、?&
以及?|
操做符和路徑/值存在運算符@>
的查詢。
-- 建立默認索引 CREATE INDEX idxgin ON api USING GIN (jdoc);
非默認的 GIN 操做符類jsonb_path_ops
只支持索引@>
操做符。
-- 建立指定路徑的索引 CREATE INDEX idxginp ON api USING GIN (jdoc jsonb_path_ops); -- create index ix_account_content_nickname_gin on account using gin (content, (content->'nickname')); -- create index ix_account_content_tags_gin on account using gin (content, (content->'nickname')); -- create index ix_account_content_tags_gin on account using gin ((content->'tags'));
PostgreSQL 擁有開放的索引接口,使得PG支持很是豐富的索引方法,例如btree , hash , gin , gist , sp-gist , brin , bloom , rum , zombodb , bitmap (greenplum extend),用戶能夠根據不一樣的數據類型,以及查詢的場景,選擇不一樣的索引。
建立默認索引
-- 建立簡單索引 create index ix_account_content on account USING GIN (content);
如今下面這樣的查詢就能使用該索引:
-- content 中有avatar key 的數據條數 explain analyze select count(*) from account where content::jsonb ? 'avatar'; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------ Aggregate (cost=4180.49..4180.50 rows=1 width=8) (actual time=43.462..43.462 rows=1 loops=1) -> Bitmap Heap Scan on account (cost=30.07..4177.24 rows=1300 width=0) (actual time=8.362..36.048 rows=100000 loops=1) Recheck Cond: (content ? 'avatar'::text) Heap Blocks: exact=2032 -> Bitmap Index Scan on ix_account_content (cost=0.00..29.75 rows=1300 width=0) (actual time=8.125..8.125 rows=100000 loops=1) Index Cond: (content ? 'avatar'::text) Planning Time: 0.078 ms Execution Time: 43.503 ms
和以前沒有添加索引時速度提高了3倍。
-- 查詢content 中nickname 爲nn-194318的數據 explain analyze select * from account where content@>'{"nickname": "nn-194318"}'; QUERY PLAN ---------------------------------------------------------------------------------------- Bitmap Heap Scan on account (cost=46.08..4193.24 rows=1300 width=100) (actual time=0.097..0.097 rows=1 loops=1) Recheck Cond: (content @> '{"nickname": "nn-194318"}'::jsonb) Heap Blocks: exact=1 -> Bitmap Index Scan on ix_account_content (cost=0.00..45.75 rows=1300 width=0) (actual time=0.091..0.091 rows=1 loops=1) Index Cond: (content @> '{"nickname": "nn-194318"}'::jsonb) Planning Time: 0.075 ms Execution Time: 0.132 ms
這個查詢效率提高更明顯,居然比使用主鍵還要高效。
可是下面這種查詢並不能使用索引:
-- 查詢content 中不存在 avatar key 的數據條數 explain analyze select count(*) from account where content::jsonb ? 'avatar' = false; QUERY PLAN ---------------------------------------------------------------------------------------- Finalize Aggregate (cost=30631.86..30631.87 rows=1 width=8) (actual time=207.641..207.641 rows=1 loops=1) -> Gather (cost=30631.65..30631.86 rows=2 width=8) (actual time=207.510..211.062 rows=3 loops=1) Workers Planned: 2 Workers Launched: 2 -> Partial Aggregate (cost=29631.65..29631.66 rows=1 width=8) (actual time=203.739..203.739 rows=1 loops=3) -> Parallel Seq Scan on account (cost=0.00..28278.83 rows=541125 width=0) (actual time=0.024..163.444 rows=400000 loops=3) Filter: (NOT (content ? 'avatar'::text)) Rows Removed by Filter: 33333 Planning Time: 0.068 ms Execution Time: 211.097 ms
該索引也不能被用於下面這樣的查詢,由於儘管操做符?
是可索引的,但它不能直接被應用於被索引列content:
explain analyze select count(1) from account where content -> 'tags' ? 'c'; QUERY PLAN ---------------------------------------------------------------------------------------- Finalize Aggregate (cost=30634.57..30634.58 rows=1 width=8) (actual time=184.864..184.864 rows=1 loops=1) -> Gather (cost=30634.35..30634.56 rows=2 width=8) (actual time=184.754..189.652 rows=3 loops=1) Workers Planned: 2 Workers Launched: 2 -> Partial Aggregate (cost=29634.35..29634.36 rows=1 width=8) (actual time=180.755..180.755 rows=1 loops=3) -> Parallel Seq Scan on account (cost=0.00..29633.00 rows=542 width=0) (actual time=0.022..177.051 rows=33333 loops=3) Filter: ((content -> 'tags'::text) ? 'c'::text) Rows Removed by Filter: 400000 Planning Time: 0.074 ms Execution Time: 189.716 ms
使用表達式索引
-- 建立路徑索引 create index ix_account_content_tags on account USING GIN ((content->'tags'));
-- 測試查詢性能 explain analyze select count(1) from account where content -> 'tags' ? 'c'; QUERY PLAN ---------------------------------------------------------------------------------------- Aggregate (cost=4631.74..4631.75 rows=1 width=8) (actual time=49.274..49.275 rows=1 loops=1) -> Bitmap Heap Scan on account (cost=478.07..4628.49 rows=1300 width=0) (actual time=8.655..42.074 rows=100000 loops=1) Recheck Cond: ((content -> 'tags'::text) ? 'c'::text) Heap Blocks: exact=2032 -> Bitmap Index Scan on ix_account_content_tags (cost=0.00..477.75 rows=1300 width=0) (actual time=8.417..8.417 rows=100000 loops=1) Index Cond: ((content -> 'tags'::text) ? 'c'::text) Planning Time: 0.216 ms Execution Time: 49.309 ms
如今,WHERE 子句content -> 'tags' ? 'c'
將被識別爲可索引操做符?在索引表達式content -> 'tags'
上的應用。
也能夠利用包含查詢的方式,例如:
-- 查尋 "tags" 包含數組元素 "c" 的數據的個數 select count(1) from account where content @> '{"tags": ["c"]}';
content 列上的簡單 GIN 索引(默認索引)就能支持索引查詢。 可是索引將會存儲content列中每個鍵 和值的拷貝,
表達式索引只存儲tags 鍵下找到的數據。
雖然簡單索引的方法更加靈活(由於它支持有關任意鍵的查詢),但定向的表達式索引更小而且搜索速度比簡單索引更快。
儘管jsonb_path_ops操做符類只支持用 @>操做符的查詢,但它比起默認的操做符類 jsonb_ops有更客觀的性能優點。一個 jsonb_path_ops索引一般也比一個相同數據上的 jsonb_ops要小得多,而且搜索的專注性更好,特 別是當查詢包含頻繁出如今該數據中的鍵時。所以,其上的搜索操做 一般比使用默認操做符類的搜索表現更好。
json
和 jsonb
,jsonb 性能優於json,且jsonb 支持索引。jsonb 查詢優化時一個好的方式是添加GIN 索引
最後,感謝女友支持和包容,比❤️
也能夠在公號輸入如下關鍵字獲取歷史文章:公號&小程序
| 設計模式
| 併發&協程