如何最好地将PostgreSQL数据库用作简单的键值存储?

24

我被要求使用PostgreSQL数据库,用它来代替我的当前使用的BerkeleyDB。尽管我意识到这不是一个理想的情况,但这超出了我的控制。

那么问题来了...如果你需要将PostgreSQL变成一个键值存储,你会如何做,并使其尽可能高效?

我的值是字节数组,我的键是字符串,我可以对这些字符串的长度施加一些限制。

我认为我应该使用blob作为我的值和主键列来保存键,但由于我刚刚开始这段旅程,我很好奇在Stack Overflow社区中是否有人已经这样做过,或者是否有任何特定的“陷阱”我应该注意。


这些字节数组实际上代表什么?文件内容?序列化对象?还是其他东西? - BalusC
它们实际上是序列化的ActionScript对象发送到服务器...但服务器代码不知道或关心其中的内容。是的,我同意这有点荒谬,使用关系型数据库作为键值存储。但仍然保留了使用数据库的一些优势,例如高效的文件IO、加密、用户访问限制等等...那么这真的很疯狂吗? - dennisjtaylor
是的。http://www.metabrew.com/article/anti-rdbms-a-list-of-distributed-key-value-stores/ - Ignacio Vazquez-Abrams
谢谢提供链接...如果可能的话,我肯定想使用真正的键值存储。我不确定这些中是否有任何一个被批准用于国防部项目,而且试图获得批准是一个问题,因为这不是一个及时的任务。我会再搜索一下。 - dennisjtaylor
5个回答

31
PostgreSQL中用于正确执行此操作的扩展名为hstore。 它与其他键值存储系统类似。只需加载扩展即可。 语法是独特的,但如果您以前使用过redis或mongo,您很快就能理解。不要让它变得更难。我知道我们经常不能选择工具,并且必须做出努力。
这是文档页面:

http://www.postgresql.org/docs/9.1/static/hstore.html


3
以下是如何加载扩展的方法。你需要成为DBA或超级用户。 创建扩展HSTORE; - Stradas

8
另一种选择是在键上使用唯一哈希索引,使用JSON或JSONB进行操作。
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE key_values (
    key uuid DEFAULT uuid_generate_v4(),
    value jsonb
);

CREATE INDEX idx_key_values ON key_values USING hash (key);

一些查询

SELECT * FROM key_values WHERE key = '1cfc4dbf-a1b9-46b3-8c15-a03f51dde891';
Time: 0.514 ms
postgres=# SELECT * FROM key_values WHERE key = '1cfc4dbf-a1b9-46b3-8c15-a03f51dde890';
Time: 1.747 ms

postgres=# do $$
begin
for r in 1..1000 loop
INSERT INTO key_values (value)
VALUES ('{"somelarge_json": "bla"}');
end loop;
end;
$$;
DO
Time: 58.327 ms

你不能像使用B树那样运行高效的范围查询,但应该具有更好的读写性能。索引应该会小约60%。


4
如果你被迫使用关系型数据库,我建议尝试在数据中找到结构以利用这个优势,因为你放弃了使用非结构化数据和键值存储所获得的速度优势。你发现的结构越多,你就能更好地利用你的困境。即使只在键中找到结构也可以。此外,还要考虑你是否只需要顺序或随机访问数据,以及在哪种比例和结构下满足此要求。例如,你是否要按类型查询值?每个问题都可能对你如何构建数据库产生影响。
关于PostgreSQL中的BLOB,有一个特定的考虑因素,它们在内部表示为pg_largetable (loid:oid,pageno:int4,data:bytea)。块的大小由LOBBLKSIZE定义,但通常为2k。因此,如果你可以在表格中使用字节数组而不是BLOB,并将你的值/键对的大小限制在块大小以下,你就可以避免通过第二个表格进行间接引用。如果你可以访问数据库的配置,你也可以增加块的大小。
我建议去寻找数据中的结构和数据访问模式,然后再提出更详细的问题。

0
你需要存储什么样的值呢?字符串?整数?对象(例如,序列化的Java对象)。一个简单的实现可以使用一个三列表,看起来像这样:
NAME(VARCHAR)   TYPE(VARCHAR)   VALUE(VARCHAR)

(也许类型是某个枚举)。但是上面的方法对于二进制数据(如序列化对象)不起作用,也许你需要一个BLOB。

或者(可能是一个更好的主意),你见过Apache Commons Configuration吗?你可以通过JDBC将其与数据库连接,并存储属性,以便这样检索它们:

// get a property called 'number'
Double double = config.getDouble("number");
Integer integer = config.getInteger("number");

这可能会在实现方面为您节省很多麻烦。您可能会遇到保存二进制数据的问题,因为您需要在插入和检索之前对其进行序列化。但是我过去曾使用它来存储int、double和通过XStream序列化的Java对象,所以我可以确认它运行良好。


0

这实际上应该取决于键是什么。 如果它总是一个字符串,长度小于255个字符,则使用Varchar作为您的PK,然后对于值使用blob(假设是大值)。如果它总是一个数字,使用int等。

换句话说,需要更多信息才能真正给出好的答案 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接