服务器之家:专注于服务器技术及软件下载分享
分类导航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|数据库技术|

服务器之家 - 数据库 - PostgreSQL - 在PostgreSQL中使用数组时值得注意的一些地方

在PostgreSQL中使用数组时值得注意的一些地方

2021-10-22 15:55PostgreSQL教程网 PostgreSQL

这篇文章主要介绍了在PostgreSQL中使用数组时值得注意的一些地方,包括如何提高输入性能,需要的朋友可以参考下

在heap中,我们依靠postgresql支撑大多数后端繁重的任务,我们存储每个事件为一个 blob,我们为每个跟踪的用户维护一个已完成事件的postgresql数组,并将这些事件按时间排序。 hstore能够让我们以灵活的方式附加属性到事件中,而且事件数组赋予了我们强大的性能,特别是对于漏斗查询,在这些查询中我们计算不同转化渠道步骤间的输出。

在这篇文章中,我们看看那些意外接受大量输入的postgresql函数,然后以高效,惯用的方式重写它。

你的第一反应可能是将postgresql中的数组看做像c语言中对等的类似物。你之前可能用过变换阵列位置或切片来操纵数据。不过要小心,在postgresql中不要有这样的想法,特别是数组类型是变长的时,比如json、文本或是hstore。如果你通过位置来访问postgresql数组,你会进入一个意想不到的性能暴跌的境地。


这种情况几星期前在heap出现了。我们在heap为每个跟踪用户维护一个事件数组,在这个数组中我们用一个hstore datum代表每个事件。我们有一个导入管道来追加新事件到对应的数组。为了使这一导入管道是幂等的,我们给每个事件设定一个event_id,我们通过一个功能函数重复运行我们的事件数组。如果我们要更新附加到事件的属性的话,我们只需使用相同的event_id转储一个新的事件到管道中。

所以,我们需要一个功能函数来处理hstores数组,并且,如果两个事件具有相同的event_id时应该使用数组中最近出现的那个。刚开始尝试这个函数是这样写的:
 

?
1
2
3
4
5
6
7
8
9
10
11
-- this is slow, and you don't want to use it!
--
-- filter an array of events such that there is only one event with each event_id.
-- when more than one event with the same event_id is present, take the latest one.
create or replace function dedupe_events_1(events hstore[]) returns hstore[] as $$
 select array_agg(event)
 from (
  -- filter for rank = 1, i.e. select the latest event for any collisions on event_id.
  select event
  from (
   -- rank elements with the same event_id by position in the array, descending.

这个查询在拥有2.4ghz的i7cpu及16gb ram的macbook pro上测得,运行脚本为:https://gist.github.com/drob/9180760。


在这边究竟发生了什么呢? 关键在于postgresql存贮了一个系列的hstores作为数组的值, 而不是指向值的指针. 一个包含了三个hstores的数组看起来像

?
1
{“event_id=>1,data=>foo”, “event_id=>2,data=>bar”, “event_id=>3,data=>baz”}

相反的是

?
1
{[pointer], [pointer], [pointer]}

 

对于那些长度不一的变量, 举个例子. hstores, json blobs, varchars,或者是 text fields, postgresql 必须去找到每一个变量的长度. 对于evaluateevents[2], postgresql 解析从左侧读取的事件直到读取到第二次读取的数据. 然后就是 forevents[3], 她再一次的从第一个索引处开始扫描,直到读到第三次的数据! 所以, evaluatingevents[sub]是 o(sub), 并且 evaluatingevents[sub]对于在数组中的每一个索引都是 o(n2), n是数组的长度.

postgresql能得到更加恰当的解析结果,  它可以在这样的情况下分析该数组一次. 真正的答案是可变长度的元素与指针来实现,以数组的值, 以至于,我们总能够处理 evaluateevents[i]在不变的时间内.


即便如此,我们也不应该让postgresql来处理,因为这不是一个地道的查询。除了generate_subscripts我们可以用unnest,它解析数组并返回一组条目。这样一来,我们就不需要在数组中显式加入索引了。
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- filter an array of events such that there is only one event with each event_id.
-- when more than one event with the same event_id, is present, take the latest one.
create or replace function dedupe_events_2(events hstore[]) returns hstore[] as $$
 select array_agg(event)
 from (
  -- filter for rank = 1, i.e. select the latest event for any collisions on event_id.
  select event
  from (
   -- rank elements with the same event_id by position in the array, descending.
   select event, row_number as index, rank()
   over (partition by (event -> 'event_id')::bigint order by row_number desc)
   from (
    -- use unnest instead of generate_subscripts to turn an array into a set.
    select event, row_number()
    over (order by event -> 'time')
    from unnest(events) as event
   ) unnested_data
  ) deduped_events
  where rank = 1
  order by index asc
 ) to_agg;
$$ language sql immutable;

结果是有效的,它花费的时间跟输入数组的大小呈线性关系。对于100k个元素的输入它需要大约半秒,而之前的实现需要40秒。

这实现了我们的需求:

  •     一次解析数组,不需要unnest。
  •     按event_id划分。
  •     对每个event_id采用最新出现的。
  •     按输入索引排序。

教训:如果你需要访问postgresql数组的特定位置,考虑使用unnest代替。 

?
1
2
3
4
5
6
7
8
   select events[sub] as event, sub, rank()
   over (partition by (events[sub] -> 'event_id')::bigint order by sub desc)
   from generate_subscripts(events, 1) as sub
  ) deduped_events
  where rank = 1
  order by sub asc
 ) to_agg;
$$ language sql immutable;

这样奏效,但大输入是性能下降了。这是二次的,在输入数组有100k各元素时它需要大约40秒!

在PostgreSQL中使用数组时值得注意的一些地方

这个查询在拥有2.4ghz的i7cpu及16gb ram的macbook pro上测得,运行脚本为:https://gist.github.com/drob/9180760。


在这边究竟发生了什么呢? 关键在于postgresql存贮了一个系列的hstores作为数组的值, 而不是指向值的指针. 一个包含了三个hstores的数组看起来像

?
1
{“event_id=>1,data=>foo”, “event_id=>2,data=>bar”, “event_id=>3,data=>baz”}

相反的是

?
1
{[pointer], [pointer], [pointer]}

 

对于那些长度不一的变量, 举个例子. hstores, json blobs, varchars,或者是 text fields, postgresql 必须去找到每一个变量的长度. 对于evaluateevents[2], postgresql 解析从左侧读取的事件直到读取到第二次读取的数据. 然后就是 forevents[3], 她再一次的从第一个索引处开始扫描,直到读到第三次的数据! 所以, evaluatingevents[sub]是 o(sub), 并且 evaluatingevents[sub]对于在数组中的每一个索引都是 o(n2), n是数组的长度.

postgresql能得到更加恰当的解析结果,  它可以在这样的情况下分析该数组一次. 真正的答案是可变长度的元素与指针来实现,以数组的值, 以至于,我们总能够处理 evaluateevents[i]在不变的时间内.


即便如此,我们也不应该让postgresql来处理,因为这不是一个地道的查询。除了generate_subscripts我们可以用unnest,它解析数组并返回一组条目。这样一来,我们就不需要在数组中显式加入索引了。
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
-- filter an array of events such that there is only one event with each event_id.
-- when more than one event with the same event_id, is present, take the latest one.
create or replace function dedupe_events_2(events hstore[]) returns hstore[] as $$
 select array_agg(event)
 from (
  -- filter for rank = 1, i.e. select the latest event for any collisions on event_id.
  select event
  from (
   -- rank elements with the same event_id by position in the array, descending.
   select event, row_number as index, rank()
   over (partition by (event -> 'event_id')::bigint order by row_number desc)
   from (
    -- use unnest instead of generate_subscripts to turn an array into a set.
    select event, row_number()
    over (order by event -> 'time')
    from unnest(events) as event
   ) unnested_data
  ) deduped_events
  where rank = 1
  order by index asc
 ) to_agg;
$$ language sql immutable;

结果是有效的,它花费的时间跟输入数组的大小呈线性关系。对于100k个元素的输入它需要大约半秒,而之前的实现需要40秒。

这实现了我们的需求:

  •     一次解析数组,不需要unnest。
  •     按event_id划分。
  •     对每个event_id采用最新出现的。
  •     按输入索引排序。

教训:如果你需要访问postgresql数组的特定位置,考虑使用unnest代替。

延伸 · 阅读

精彩推荐
  • PostgreSQLpostgresql 数据库中的数据转换

    postgresql 数据库中的数据转换

    postgres8.3以后,字段数据之间的默认转换取消了。如果需要进行数据变换的话,在postgresql数据库中,我们可以用"::"来进行字段数据的类型转换。...

    postgresql教程网12482021-10-08
  • PostgreSQL深入理解PostgreSQL的MVCC并发处理方式

    深入理解PostgreSQL的MVCC并发处理方式

    这篇文章主要介绍了深入理解PostgreSQL的MVCC并发处理方式,文中同时介绍了MVCC的缺点,需要的朋友可以参考下 ...

    PostgreSQL教程网3622020-04-25
  • PostgreSQLRDS PostgreSQL一键大版本升级技术解密

    RDS PostgreSQL一键大版本升级技术解密

    一、PostgreSQL行业位置 (一)行业位置 在讨论PostgreSQL(下面简称为PG)在整个数据库行业的位置之前,我们先看一下阿里云数据库在全球的数据库行业里的...

    未知1192023-05-07
  • PostgreSQLPostgresql查询效率计算初探

    Postgresql查询效率计算初探

    这篇文章主要给大家介绍了关于Postgresql查询效率计算的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Postgresql具有一定的参考学习价...

    轨迹4622020-05-03
  • PostgreSQLpostgresql 中的to_char()常用操作

    postgresql 中的to_char()常用操作

    这篇文章主要介绍了postgresql 中的to_char()常用操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    J符离13432021-04-12
  • PostgreSQL分布式 PostgreSQL之Citus 架构

    分布式 PostgreSQL之Citus 架构

    节点 Citus 是一种 PostgreSQL 扩展,它允许数据库服务器(称为节点)在“无共享(shared nothing)”架构中相互协调。这些节点形成一个集群,允许 PostgreSQL 保存比单...

    未知802023-05-07
  • PostgreSQLPostgreSQL标准建表语句分享

    PostgreSQL标准建表语句分享

    这篇文章主要介绍了PostgreSQL标准建表语句分享,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    码上得天下7962021-02-27
  • PostgreSQLPostgresql开启远程访问的步骤全纪录

    Postgresql开启远程访问的步骤全纪录

    postgre一般默认为本地连接,不支持远程访问,所以如果要开启远程访问,需要更改安装文件的配置。下面这篇文章主要给大家介绍了关于Postgresql开启远程...

    我勒个去6812020-04-30