Ren*_*aro 18 postgresql database-design
我正在设计一个数据库(在 PostgreSQL 9.6 上),它将存储来自分布式应用程序的数据。由于应用程序的分布式特性,SERIAL由于潜在的竞争条件,我不能使用自动递增整数 ( ) 作为我的主键。
自然的解决方案是使用 UUID,或全局唯一标识符。Postgres 带有一个内置的UUIDtype,非常适合。
我对 UUID 的问题与调试有关:它是一个非人类友好的字符串。标识符ff53e96d-5fd7-4450-bc99-111b91875ec5什么也没告诉我,而ACC-f8kJd9xKCd,虽然不能保证是唯一的,但告诉我我正在处理一个ACC对象。
从编程的角度来看,调试与几个不同对象相关的应用程序查询是很常见的。假设程序员错误地ACC在ORD(订单)表中搜索(帐户)对象。使用人类可读的标识符,程序员可以立即识别问题,而在使用 UUID 时,他会花一些时间找出问题所在。
我不需要 UUID 的“保证”唯一性;我确实需要一些空间来生成没有冲突的密钥,但 UUID 有点矫枉过正。此外,最坏的情况是,如果发生冲突(数据库拒绝它并且应用程序可以恢复),也不会是世界末日。因此,权衡考虑,一个更小但对人友好的标识符将是我的用例的理想解决方案。
我想出的标识符具有以下格式:{domain}-{string},其中{domain}替换为对象域(帐户,订单,产品)并且{string}是随机生成的字符串。在某些情况下,{sub-domain}在随机字符串之前插入一个甚至可能是有意义的。让我们忽略的长度{domain},并{string}为保证唯一性的目的。
如果有助于索引/查询性能,格式可以具有固定大小。
知道:
ACC-f8kJd9xKCd.关于性能,存储此密钥的最佳方法是什么?
以下是四种可能的解决方案,但由于我对数据库的经验很少,我不确定哪个(如果有)是最好的。
VARCHAR)(Postgres 在CHAR(n)和之间没有区别VARCHAR(n),所以我忽略了CHAR)。
经过一些研究,我发现使用 进行字符串比较VARCHAR,特别是在连接操作上,比使用INTEGER. 这是有道理的,但在这种规模下我应该担心吗?
bytea)与 Postgres 不同,MySQL 没有本机UUID类型。有几篇文章解释了如何使用 16 字节BINARY字段而不是 36字节字段来存储 UUID VARCHAR。这些帖子让我想到了将密钥存储为二进制文件(bytea在 Postgres 上)。
这节省了尺寸,但我更关心性能。我没有找到关于哪个比较更快的解释:二进制或字符串的解释。我相信二进制比较更快。如果是,那么bytea可能比 更好VARCHAR,即使程序员现在每次都必须对数据进行编码/解码。
我可能是错的,但我认为两者bytea并VARCHAR会(通过文字或文字),由字节比较(平等)字节。有没有办法“跳过”这个逐步比较并简单地比较“整个事情”?(我不这么认为,但它不需要检查费用)。
我认为按原样存储bytea是最好的解决方案,但我想知道是否还有其他我忽略的替代方案。此外,我在解决方案 1 中表达的相同担忧也成立:比较的开销是否足以让我担心?
我想出了两个可以工作的非常“有创意”的解决方案,我只是不确定在多大程度上(即,如果我将它们缩放到表中的几千行以上会遇到问题)。
UUID但附有“标签”不使用 UUID 的主要原因是程序员可以更好地调试应用程序。但是,如果我们可以同时使用两者呢:数据库UUID仅将所有键存储为s,但它在进行查询之前/之后包装对象。
例如,程序员请求ACC-{UUID},数据库忽略该ACC-部分,获取结果,并将所有结果作为 返回{domain}-{UUID}。
也许这可以通过一些带有存储过程或函数的hackery来实现,但我想到了一些问题:
我以前从未使用过存储过程或函数,所以我不确定这是否可能。有人可以透露一些信息吗?如果我能在程序员和存储的数据之间添加一个透明层,这似乎是一个完美的解决方案。
cidr是的,你没有看错。事实证明,IPv6 地址格式完美地解决了我的问题。
bytea.contains,取决于域及其层次结构的表示方式。例如,假设我使用代码0000来表示域“产品”。Key0000:0db8:85a3:0000:0000:8a2e:0370:7334将代表产品0db8:85a3:0000:0000:8a2e:0370:7334。
这里的主要问题是:与 相比bytea,使用cidr数据类型有什么主要优点或缺点吗?
ltree如果 IPV6 有效,那就太好了。它不支持“ACC”。ltree做。
标签路径是由点分隔的零个或多个标签的序列,例如 L1.L2.L3,表示从分层树的根到特定节点的路径。标签路径的长度必须小于 65kB,但最好保持在 2kB 以下。在实践中,这不是主要限制;例如,DMOZ 目录(http://www.dmoz.org)中最长的标签路径约为 240 字节。
你会像这样使用它,
CREATE EXTENSION ltree;
SELECT replace('ACC-f8kJd9xKCd', '-', '.')::ltree;
Run Code Online (Sandbox Code Playgroud)
我们创建示例数据。
SELECT x, (
CASE WHEN x%7=0 THEN 'ACC'
WHEN x%3=0 THEN 'XYZ'
ELSE 'COM'
END ||'.'|| md5(x::text)
)::ltree
FROM generate_series(1,10000) AS t(x);
CREATE INDEX ON foo USING GIST (ltree);
ANALYZE foo;
x | ltree
-----+--------------------------------------
1 | COM.c4ca4238a0b923820dcc509a6f75849b
2 | COM.c81e728d9d4c2f636f067f89cc14862c
3 | XYZ.eccbc87e4b5ce2fe28308fd9f2a7baf3
4 | COM.a87ff679a2f3e71d9181a67b7542122c
5 | COM.e4da3b7fbbce2345d7772b0674a318d5
6 | XYZ.1679091c5a880faf6fb5e6087eb1b2dc
7 | ACC.8f14e45fceea167a5a36dedd4bea2543
8 | COM.c9f0f895fb98ab9159f51fd0297e236d
Run Code Online (Sandbox Code Playgroud)
还有中提琴..
QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on foo (cost=103.23..234.91 rows=1414 width=57) (actual time=0.422..0.908 rows=1428 loops=1)
Recheck Cond: ('ACC'::ltree @> ltree)
Heap Blocks: exact=114
-> Bitmap Index Scan on foo_ltree_idx (cost=0.00..102.88 rows=1414 width=0) (actual time=0.389..0.389 rows=1428 loops=1)
Index Cond: ('ACC'::ltree @> ltree)
Planning time: 0.133 ms
Execution time: 1.033 ms
(7 rows)
Run Code Online (Sandbox Code Playgroud)
有关更多信息和运算符,请参阅文档
如果您要创建产品 ID,我会使用 ltree。如果你需要一些东西来创建它们,我会使用 UUID。
小智 1
仅仅关于与bytea的性能比较。网络的比较分三步完成:首先比较网络部分的公共位,然后比较网络部分的长度,然后比较整个未屏蔽的地址。请参阅:network_cmp_internal
所以它应该比直接进入 memcmp 的 bytea 慢一点。我在一个包含 1000 万行的表上运行了一个简单的测试,以查找单个行:
我不能说 bytea 和 cidr 之间有很大差异(尽管差距保持一致),只是附加声明if- 猜测这对于 10m 元组来说还不错。
希望它有帮助 - 很想听听你最终选择了什么。
| 归档时间: |
|
| 查看次数: |
5568 次 |
| 最近记录: |