francs=> create table user_ini(id int4 ,user_id int8, user_name character varying(64),create_time timestamp(6) with time zone default clock_timestamp()); CREATE TABLE francs=> insert into user_ini(id,user_id,user_name) select r,round(random()*2000000), r || '_francs' from generate_series(1,2000000) as r; INSERT 0 2000000
备注: 生成 200 万测试数据.
2.3 生成 json 测试数据
1 2 3 4 5 6
francs=> create table tbl_user_json(id serial, user_info json); CREATE TABLE francs=> insert into tbl_user_json(user_info) select row_to_json(user_ini) from user_ini; INSERT 0 2000000 Time: 63469.336 ms
2.4 生成 jsonb 测试数据
1 2 3 4 5 6
francs=> create table tbl_user_jsonb(id serial, user_info jsonb); CREATE TABLE francs=> insert into tbl_user_jsonb(user_info) select row_to_json(user_ini)::jsonb from user_ini; INSERT 0 2000000 Time: 78300.553 ms
备注: 从时间来看, jsonb 插入速度比 json 插入速度稍慢, 再来看下两个表的大小如何?
2.5 比较表大小
1 2 3 4 5 6 7 8 9 10 11 12 13
francs=> /dt+ tbl_user_json List of relations Schema | Name | Type | Owner | Size | Description --------+---------------+-------+--------+--------+------------- francs | tbl_user_json | table | francs | 269 MB | (1 row) francs=> /dt+ tbl_user_jsonb List of relations Schema | Name | Type | Owner | Size | Description --------+----------------+-------+--------+--------+------------- francs | tbl_user_jsonb | table | francs | 329 MB | (1 row)
francs=> create index idx_tbl_user_jsonb_user_Info on tbl_user_jsonb using gin (user_Info); CREATE INDEX Time: 214253.873 ms francs=> di+ idx_tbl_user_jsonb_user_Info List of relations Schema | Name | Type | Owner | Table | Size | Description --------+------------------------------+-------+--------+----------------+--------+------------- francs | idx_tbl_user_jsonb_user_info | index | francs | tbl_user_jsonb | 428 MB | (1 row)
备注: 索引很大,创建很慢,一般不会这么建索引.
4.2 基于 key/value 检索可以使用索引
1 2 3 4 5 6 7 8 9 10 11 12
francs=> explain analyze select * from tbl_user_jsonb where user_info @> '{"user_id": 1017031}'; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------- Bitmap Heap Scan on tbl_user_jsonb(cost=59.50..6637.58rows=2000width=140) (actual time=0.340..0.345rows=1loops=1) Recheck Cond: (user_info @> '{"user_id": 1017031}'::jsonb) Rows Removed by Index Recheck: 1 Heap Blocks: exact=2 -> Bitmap Index Scan on idx_tbl_user_jsonb_user_info(cost=0.00..59.00rows=2000width=0) (actual time=0.319..0.319rows=2loops=1) Index Cond: (user_info @> '{"user_id": 1017031}'::jsonb) Planning time: 0.118 ms Execution time: 0.391 ms (8 rows)
4.3 以下查询不走索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
francs=> explain analyze select * from tbl_user_jsonb where user_info->>'user_name' ='4_francs'; QUERY PLAN --------------------------------------------------------------------------------------------------------------------- Seq Scan on tbl_user_jsonb(cost=0.00..72098.00 rows=10000 width=140) (actual time=0.036..4640.794 rows=1 loops=1) Filter: ((user_info ->> 'user_name'::text) = '4_francs'::text) Rows Removed by Filter: 1999999 Planning time: 1.101 ms Execution time: 4640.851 ms (5 rows) francs=> explain analyze select * from tbl_user_jsonb where user_info->'user_name' ?'4_francs'; QUERY PLAN -------------------------------------------------------------------------------------------------------------------- Seq Scan on tbl_user_jsonb(cost=0.00..72098.00 rows=2000 width=140) (actual time=0.187..5387.658 rows=1 loops=1) Filter: ((user_info ->'user_name'::text) ? '4_francs'::text) Rows Removed by Filter: 1999999 Planning time: 0.382 ms Execution time: 5387.762 ms (5 rows)
备注: 以上的 ? 操作没走索引, 但 ? 操作支持索引检索,创建以下索引.
4.4 删除之前索引并新建以下索引
1 2 3 4 5 6 7 8 9 10 11 12 13 14
francs=> create index idx_gin_user_info_user_name on tbl_user_jsonb using gin((user_info ->'user_name')); CREATE INDEX francs=> explain analyze select * from tbl_user_jsonb where user_info->'user_name' ?'4_francs'; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------- Bitmap Heap Scan on tbl_user_jsonb(cost=35.50..6618.58 rows=2000 width=140) (actual time=0.067..0.069 rows=1 loops=1) Recheck Cond: ((user_info ->'user_name'::text) ? '4_francs'::text) Heap Blocks: exact=1 -> Bitmap Index Scan on idx_gin_user_info_user_name(cost=0.00..35.00 rows=2000 width=0) (actual time=0.037..0.037 rows=1 loops=1) Index Cond: ((user_info ->'user_name'::text) ? '4_francs'::text) Planning time: 0.151 ms Execution time: 0.129 ms (7 rows)
francs=> create index idx_gin_user_info_id on tbl_user_json using btree (((user_info ->> 'id')::integer)); CREATE INDEX francs=> create index idx_gin_user_infob_id on tbl_user_jsonb using btree (((user_info ->> 'id')::integer)); CREATE INDEX
备注: 为什么使用函数索引? 由于 –> 操作返回的是 text 类型, 接下来的查询会用到 id 字段比较, 需要转换成整型.
5.2 json 表范围扫描
1 2 3 4 5 6 7 8 9 10 11
francs=> explain analyze select id,user_info->'id',user_info->'user_name'from tbl_user_json where (user_info->>'id')::int4 > '1' and (user_info->>'id')::int4 < '10000'; QUERY PLAN -------------------------------------------------------------------------------------------------------------------------------------- Bitmap Heap Scan on tbl_user_json(cost=190.94..22275.60 rows=10000 width=36) (actual time=2.417..60.585 rows=9998 loops=1) Recheck Cond: ((((user_info ->> 'id'::text))::integer > 1) AND (((user_info ->> 'id'::text))::integer < 10000)) Heap Blocks: exact=167 -> Bitmap Index Scan on idx_gin_user_info_id(cost=0.00..188.44 rows=10000 width=0) (actual time=2.329..2.329 rows=9998 loops=1) Index Cond: ((((user_info ->> 'id'::text))::integer > 1) AND (((user_info ->> 'id'::text))::integer < 10000)) Planning time: 0.183 ms Execution time: 64.116 ms (7 rows)
5.3 jsonb 表范围扫描
1 2 3 4 5 6 7 8 9 10 11
francs=> explain analyze select id,user_info->'id',user_info->'user_name'from tbl_user_jsonb where (user_info->>'id')::int4 > '1' and (user_info->>'id')::int4 < '10000'; QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------- Bitmap Heap Scan on tbl_user_jsonb(cost=190.94..23939.63 rows=10000 width=140) (actual time=2.593..24.308 rows=9998 loops=1) Recheck Cond: ((((user_info ->> 'id'::text))::integer > 1) AND (((user_info ->> 'id'::text))::integer < 10000)) Heap Blocks: exact=197 -> Bitmap Index Scan on idx_gin_user_infob_id(cost=0.00..188.44 rows=10000 width=0) (actual time=2.494..2.494 rows=9998 loops=1) Index Cond: ((((user_info ->> 'id'::text))::integer > 1) AND (((user_info ->> 'id'::text))::integer < 10000)) Planning time: 0.142 ms Execution time: 27.851 ms (7 rows)