在PostgreSQL中表示稀疏数据

时间:2010-04-07 14:27:09

标签: sql postgresql relational-database sparse-matrix

在PostgreSQL中表示稀疏数据矩阵的最佳方法是什么?我看到的两个明显的方法是:

  1. 将数据存储在一个表中,每个表都包含一个单独的列(可能是数百万个),但未使用的功能的默认值为NULL。这在概念上非常简单,但我知道在大多数RDMS实现中,这通常是非常低效的,因为NULL值通常占用一些空间。但是,我读了一篇文章(遗憾的是找不到它的链接),声称PG没有占用NULL值的数据,使其更适合存储稀疏数据。

  2. 创建单独的“行”和“列”表,以及用于链接它们并在该行存储列值的中间表。我相信这是更传统的RDMS解决方案,但与之相关的复杂性和开销更多。

  3. 我还发现了PostgreDynamic,它声称可以更好地支持稀疏数据,但我不想仅仅为了这个功能而将整个数据库服务器切换到PG分支。

    还有其他解决方案吗?我应该使用哪一个?

4 个答案:

答案 0 :(得分:13)

我假设您正在考虑数学上下文中的稀疏矩阵: http://en.wikipedia.org/wiki/Sparse_matrix(这里描述的存储技术用于存储器存储(快速算术运算),而不是持久存储(低磁盘使用)。)

由于人们通常在客户端而不是服务器端对此矩阵进行操作,因此SQL-ARRAY []是最佳选择!

问题是如何利用矩阵的稀疏性?这是一些调查结果。

设定:

  • Postgres 8.4
  • 具有双精度(8字节)的400 * 400个元素的矩阵 - >每个矩阵1.28MiB原始尺寸
  • 33%非零元素 - >每矩阵427kiB有效尺寸
  • 使用~1000个不同的随机填充矩阵进行平均

竞争方法:

  • 依靠SET STORAGE MAIN或EXTENDED的自动服务器端压缩
  • 仅存储非零元素加上位图bit varying(xx)),描述矩阵中非零元素的位置。 (一个双精度比一位大64倍。理论上(忽略开销)如果< = 98%非零,这个方法应该是改进;-)。)服务器端压缩被激活。
  • 替换矩阵中的零。 (RDBMS在存储NULL方面非常有效。)服务器端压缩已激活。

(使用第二个索引-ARRAY []索引非零元素并不是很有希望,因此没有经过测试。)

结果:

  • 自动压缩
    • 没有额外的实施工作
    • 没有减少的网络流量
    • 最小压缩开销
    • 持久存储=原始大小的39%
  • 位图
    • 可接受的实施工作
    • 网络流量略有下降;依赖于稀疏性
    • 持久存储=原始大小的33.9%
  • 用NULL替换零
    • 一些实现工作(API需要知道在构造INSERT查询时在ARRAY []中设置NULL的位置和方式)
    • 网络流量无变化
    • 持久存储=原始大小的35%

结论: 从EXTENDED / MAIN存储参数开始。如果您有空闲时间调查您的数据并使用我的测试设置与稀疏级别。但效果可能低于您的预期。

我建议总是使用矩阵序列化(例如行主要顺序)加上两个整数列作为矩阵维度NxM。由于大多数API使用文本SQL,因此您可以为嵌套的“ARRAY [ARRAY [..],ARRAY [..],ARRAY [..],ARRAY [..],..]”节省大量网络流量和客户端内存。 !!!

Tebas


CREATE TABLE _testschema.matrix_dense
(
  matdata double precision[]
);
ALTER TABLE _testschema.matrix_dense ALTER COLUMN matdata SET STORAGE EXTERN;


CREATE TABLE _testschema.matrix_sparse_autocompressed
(
  matdata double precision[]
);

CREATE TABLE _testschema.matrix_sparse_bitmap
(
  matdata double precision[]
  bitmap bit varying(8000000)
);

将相同的矩阵插入所有表格。具体数据取决于特定表。 由于未使用但已分配的页面,请勿更改服务器端的数据。或做一个VACUUM。

SELECT 
pg_total_relation_size('_testschema.matrix_dense') AS dense, 
pg_total_relation_size('_testschema.matrix_sparse_autocompressed') AS autocompressed, 
pg_total_relation_size('_testschema.matrix_sparse_bitmap') AS bitmap;

答案 1 :(得分:7)

我想到了一些解决方案,

1)将您的功能分成通常设置在一起的组,为每个组创建一个表,与主数据具有一对一的外键关系,只在查询时需要加入表

2)使用EAV反模式,使用主表中的外键字段以及字段名和值列创建“功能”表,并将功能存储为该表中的行而不是属性在主表中

3)与PostgreDynamic的工作方式类似,为主表中的每个“列”创建一个表(它们为这些表使用单独的命名空间),并创建函数以简化(以及有效索引)访问和更新这些表格中的数据

4)使用XML或VARCHAR在主数据中创建一个列,并在其中存储一些表示数据的结构化文本格式,使用功能索引创建数据索引,编写更新数据的函数(或使用XML函数,如果您使用该格式)

5)使用contrib / hstore模块创建一个类型为hstore的列,该列可以保存键值对,并且可以被索引和更新

6)住着很多空地

答案 2 :(得分:2)

当NULL值为NULL时,NULL值将不占用空间。它将在元组头中的位图中占用一位,但无论如何都会存在。

但是,系统无法处理数百万列,期间。理论上最多有一千多个,IIRC,但你真的不想走那么远。

如果你真的需要那么多,在一张桌子里,你需要采用EAV方法,这基本上就是你在(2)中所说的。

如果每个条目只有相对较少的密钥,我建议您查看“hstore”contrib模块,它可以非常有效地存储此类数据,作为第三种选择。它在即将推出的9.0版本中得到了进一步的增强,因此,如果您远离生产部署,您可能需要直接查看该版本。但是,它在8.4中也是值得的。它确实支持一些非常有效的基于索引的查找。绝对值得研究。

答案 3 :(得分:2)

我知道这是一个旧线程,但MadLib为Postgres提供稀疏矢量类型,以及几种机器学习和统计方法。