唯一性约束jsonb对postgres数据库性能有什么影响

问题描述 投票:0回答:1

我正在设计一个 postgres 数据库,其中有一个包含 jsonb 类型列的表。我希望这个专栏是独一无二的。表中不需要有两个具有完全相同 json 配置的对象。对于未保存在数据库中的每个副本,它将为我节省大约 5 分钟的计算时间。我意识到 json 唯一性在字典方面的风险(不能保证键的顺序),但我认为一个好的 json 编码器可以缓解这个问题。

我担心的是数据库性能。我想确保我们正在尽一切可能确保插入不会因 jsonb 的唯一性约束而严重减慢。与 varchar 或 int 上的唯一性约束相比,jsonb 上的唯一性约束有多糟糕?我们说的是毫秒、秒还是分钟?

我研究了哈希索引,这听起来确实是我获得最佳性能所需的一切。但。只有B树类型的索引可以是唯一的,这很奇怪。为什么?

postgresql database-performance database-optimization
1个回答
0
投票

字典存在 json 唯一性的风险(不保证键的顺序)

JSONB
确实保证了键的顺序。它还会忽略无关紧要的空格,并对内部排序的键进行重复数据删除。你计划做的事情将会很好地进行。

如果您想使用普通的

json
,我会担心它的工作方式与预先验证的
text
非常相似,并且会保留您保存到其中的所有内容,从而使
{"a":1}
{ "a" : 1 }
不平等。此外,由于这个原因,没有内置的
json=json
运算符,因此您无法定义
unique
类型的
json
列。

性能方面,你要自己测试一下,看看是否可以接受。强制执行限制总是会产生一些成本,无论如何它都不是免费的。

db<>fiddle

演示
select '{"a":1}'::jsonb = E' {\n\t "a" : 1 \n} '::jsonb
?栏?
t
select '{"a":1}'::json = E' {\n\t "a" : 1 \n} '::json;
ERROR:  operator does not exist: json = json
LINE 1: select '{"a":1}'::json = E' {\n\t "a" : 1 \n} '::json;
                               ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
create table test_no_unique(a jsonb);
select setseed(.42);

explain analyze verbose
insert into test_no_unique 
select jsonb_build_object(n::text,n)
from generate_Series(1,1e5)n;
查询计划
在public.test_no_unique上插入(成本=0.00..17.50行=0宽度=0)(实际时间=724.632..724.633行=0循环=1)
-> pg_catalog.generate_series n 上的函数扫描(成本=0.00..17.50行=1000宽度=32)(实际时间=33.737..354.927行=100000循环=1)
输出:jsonb_build_object((n.n)::text, n.n)
函数调用:generate_series('1'::numeric, '100000'::numeric)
规划时间:0.068毫秒
执行时间:725.363 ms
create table test_unique_no_onconflict(a jsonb unique);
select setseed(.42);

explain analyze verbose
insert into test_unique_no_onconflict 
select jsonb_build_object(n::text,n)
from generate_Series(1,1e5)n;
查询计划
在public.test_unique_no_onconflict上插入(成本=0.00..17.50行=0宽度=0)(实际时间=1953.638..1953.639行=0循环=1)
-> pg_catalog.generate_series n 上的函数扫描(成本=0.00..17.50行=1000宽度=32)(实际时间=64.094..392.841行=100000循环=1)
输出:jsonb_build_object((n.n)::text, n.n)
函数调用:generate_series('1'::numeric, '100000'::numeric)
规划时间:0.047 ms
执行时间:1955.242 ms
create table test_unique_on_conflict_do_nothing_without_dupes(a jsonb unique);
select setseed(.42);

explain analyze verbose
insert into test_unique_on_conflict_do_nothing_without_dupes 
select jsonb_build_object(n::text,n)
from generate_Series(1,1e5)n
on conflict do nothing;
查询计划
插入public.test_unique_on_conflict_do_nothing_without_dupes(成本=0.00..17.50行=0宽度=0)(实际时间=5938.022..5938.023行=0循环=1)
解决冲突:没什么
插入的元组:100000
冲突元组:0
-> pg_catalog.generate_series n 上的函数扫描(成本=0.00..17.50行=1000宽度=32)(实际时间=33.093..527.146行=100000循环=1)
输出:jsonb_build_object((n.n)::text, n.n)
函数调用:generate_series('1'::numeric, '100000'::numeric)
规划时间:0.043毫秒
执行时间:5938.573 ms

这里,请注意,与之前的示例相比,调用 random() 并进行强制转换会花费一些成本,处理冲突也花费了一些成本,但通过有效地将 36% 的行写入表中,它也节省了一些成本。

create table test_unique_on_conflict_do_nothing_with_dupes(a jsonb unique);
select setseed(.42);

explain analyze verbose
insert into test_unique_on_conflict_do_nothing_with_dupes 
select jsonb_build_object((random()*1e3)::int::text,(random()*1e2)::int::text)
from generate_Series(1,1e5)n
on conflict do nothing;
查询计划
插入public.test_unique_on_conflict_do_nothing_with_dupes(成本=0.00..47.50行=0宽度=0)(实际时间=5067.804..5067.805行=0循环=1)
解决冲突:没什么
插入的元组:63241
冲突元组:36759
-> pg_catalog.generate_series n 上的函数扫描(成本=0.00..37.50行=1000宽度=32)(实际时间=66.536..491.087行=100000循环=1)
输出:jsonb_build_object((((random() * '1000'::双精度))::integer)::text, (((random() * '100'::双精度))::integer): :文字)
函数调用:generate_series('1'::numeric, '100000'::numeric)
规划时间:0.121毫秒
执行时间:5082.982 ms
© www.soinside.com 2019 - 2024. All rights reserved.