1

我有一个棘手的问题,试图找到一种有效的方法来订购一组包含大量(约 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[],

要点索引:

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

查询时间:17ms

4

1 回答 1

3

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

其次,您的索引对data您显示的查询毫无用处。您正在查询一个数据(数组中的一个位置),而索引是基于数组中的值构建的。删除索引将使插入速度大大加快。

如果您因为其他原因(更大的数据模型、MVCC、安全性)而不得不继续使用 PostgreSQL,那么我建议您更改数据模型和ALTER COLUMN data SET TYPE bytea STORAGE external. 由于该data列大约为 4 x 500 万 = 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()宏仅从磁盘中检索一部分数据,因此非常有效。

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

您的查询现在变为:

SELECT name, get_data_value(data, 3916863) AS weight FROM object ORDER BY weight DESC;
于 2015-10-03T03:09:58.553 回答