我有一个棘手的问题,试图找到一种有效的方法来排序一组包含大量(约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毫秒
答案 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;