2015-10-02 18 views
1

我有一个棘手的问题,试图找到一种有效的方式来排序包含大量(〜500万)索引数据点数的对象集(〜1000行)。在我的情况下,我需要一个查询,允许我通过特定的数据点排序表。每个数据点是一个16位无符号整数。Postgres在大型数据集中按元素排序表

对象表:

id serial NOT NULL, 
    category_id integer, 
    description text, 
    name character varying(255), 
    created_at timestamp without time zone NOT NULL, 
    updated_at timestamp without time zone NOT NULL, 
    data integer[], 

GIST指数:

CREATE INDEX object_rdtree_idx 
    ON object 
    USING gist 
    (data gist__intbig_ops) 

该指数目前不

我目前使用的大型阵列解决这个问题当我做选择查询时使用,我不确定它是否有问题无论如何帮助。

每一天的阵列场与一组新的〜500万个值

的更新,我有一个需要列出了一个特定的数据点的值排序的所有对象web服务器:

例如查询:

SELECT name, data[3916863] as weight FROM object ORDER BY weight DESC 

目前,大约需要2.5秒来执行此查询。

问题: 有没有更好的方法?我很高兴插入端在后台发生时很慢,但我需要选择查询的速度尽可能快。说到这一点,插入可以持续多长时间是有限制的。

我已经考虑创建一个查找表,其中每个值都有它自己的行 - 但我不确定插入/查找时间将如何受此方法的影响,并且我怀疑输入了1000多个记录,约有500万个数据点因为单个行太慢了。

当前插入一行需要大约30秒,现在可以接受。

最终,我仍然在寻找基础问题的可扩展解决方案,但现在我需要此解决方案来工作,所以此解决方案无需进一步扩展。

更新: 我错了解雇有一个巨大的表,而不是一个数组,而插入时间大量地增加,查询时间被减少到只有几毫秒。

我现在正在改变我的生成算法,只保存一个数据,如果它非零并且从以前的更新更改。这已经将插入减少到几十万个仅需要几秒钟的值。

新表:

CREATE TABLE data 
(
    object_id integer, 
    data_index integer, 
    value integer, 
) 

CREATE INDEX index_data_on_data_index 
    ON data 
    USING btree 
    ("data_index"); 

新查询:

SELECT name, coalesce(value,0) as weight FROM objects LEFT OUTER JOIN data on data.object_id = objects.id AND data_index = 7731363 ORDER BY weight DESC 

插入时间:15,000条记录/秒

查询时间:17MS

回答

3

首先,做y你真的需要一个关系数据库吗?您似乎并不是某些数据与某些其他数据相关联。使用平面文件格式可能会更好。

其次,您在data上的索引对您显示的查询没有用处。您正在查询数据(数组中的一个位置),而索引是建立在数组中的值上的。删除索引将使插入速度更快。

如果你为了其他原因(更大的数据模型,MVCC,安全性)必须留在PostgreSQL中,那么我建议你改变你的数据模型和ALTER COLUMN data SET TYPE bytea STORAGE external。由于data列的大小约为4 x 5百万= 20MB,因此无论如何都会将其存储在非线性列表中,但如果您明确地设置了它,那么您完全知道自己拥有了什么。

然后在C中使用PG_GETARG_BYTEA_P_SLICE()宏“直接”获取数据值的自定义函数,这看起来有点像这样(我不是一个非常成功的PG C程序员,所以请原谅我的任何错误,但这应该有所帮助你对你的方式):

// Function get_data_value() -- Get a 4-byte value from a bytea 
// Arg 0: bytea* The data 
// Arg 1: int32 The position of the element in the data, 1-based 

PG_FUNCTION_INFO_V1(get_data_value); 
Datum 
get_data_value(PG_FUNCTION_ARGS) 
{ 
    int32 element = PG_GETARG_INT32_P(1) - 1;  // second argument, make 0-based 
    bytea *data = PG_GETARG_BYTEA_P_SLICE(0,  // first argument 
        element * sizeof(int32),  // offset into data 
        sizeof(int32));    // get just the required 4 bytes 
    PG_RETURN_INT32_P((int32*)data); 
} 

PG_GETARG_BYTEA_P_SLICE()宏检索只能从磁盘数据的切片,因此是非常有效的。

docs中有一些创建自定义C函数的示例。

您的查询现在变成:

SELECT name, get_data_value(data, 3916863) AS weight FROM object ORDER BY weight DESC; 
+0

非常酷的答案 - 用PG是在一个平面文件只是因为系统设置的方式可能仍然首选。不幸的是,我不会检查它,因为我已经找到了一个更好的解决方案(稍后会发布一个编辑)。尽管将来肯定会记住这一点。 –