Postgres按大数据集中的元素排序

时间:2015-10-02 21:55:36

标签: arrays performance postgresql select gist

我有一个棘手的问题,试图找到一种有效的方法来排序一组包含大量(约500万)索引数据点的对象(~1000行)。在我的情况下,我需要一个允许我按特定数据点对表进行排序的查询。每个数据点都是一个16位无符号整数。

我目前正在使用大型数组解决此问题:

对象表:

  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万个值来更新数组字段

我有一个网络服务器,需要列出按特定数据点的值排序的所有对象:

示例查询:

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条记录/秒

查询时间:17毫秒

1 个答案:

答案 0 :(得分:3)

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

其次,data上的索引对您显示的查询没用。您正在查询 datum (数组中的位置),而索引是基于数组中的值构建的。删除索引将使插入更快。

如果您因其他原因(更大的数据模型,MVCC,安全性)而不得不继续使用PostgreSQL,那么我建议您更改数据模型和ALTER COLUMN data SET TYPE bytea STORAGE external。由于data列约为4 x 5百万= 20 MB,因此无论如何都会存储在线外,但如果您明确设置它,那么您就可以确切知道自己拥有的内容。

然后在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;