在每个函数调用上导入 Redshift Python UDF

Posted

技术标签:

【中文标题】在每个函数调用上导入 Redshift Python UDF【英文标题】:Redshift Python UDFs Import on Every Function Call 【发布时间】:2016-08-12 19:29:44 【问题描述】:

我开始学习 Redshift 中的 Python 用户定义函数,我有几个问题需要澄清。假设我已经定义了以下函数:

CREATE OR REPLACE FUNCTION f_parse_url_query_string(url VARCHAR(MAX))
RETURNS varchar(max)
STABLE
AS $$
    from urlparse import urlparse, parse_qsl
    import json
    return json.dumps(dict(parse_qsl(urlparse(url)[4])))
$$ LANGUAGE plpythonu;

这是每次调用函数时都会运行imports,还是由 Redshift 编译并仅导入一次?

我的第二个问题是是否有办法返回可变数据类型。例如,如果我想创建一个获取嵌套 json 字段值的函数,则结果可以是从字符串到整数或布尔值的任何内容。有没有办法在函数返回类型上创建自动检测?

【问题讨论】:

【参考方案1】:

执行

是的,import 每次都会执行。

避免这种情况的一种方法是使用IMMUTABLE 作为函数的易变性。这允许 Redshift 缓存给定输入值的函数输出,避免将来需要为相同的输入值运行 Python 函数。

返回值

返回值的数据类型是固定的,不能更改。 可以为不同的函数名或不同的输入类型定义不同的返回值(例如定义一个函数,它接受一个整数并返回一个整数,然后是另一个具有相同名称但输入类型为字符串的函数,该函数返回一个字符串作为输出)。

此外,使用返回不同输出数据类型的函数将非常困难——调用 UDF 的 SQL 语句需要特定的数据类型,而不是更改的数据类型。

【讨论】:

【参考方案2】:

进口

是和不是。 Redshift 在语句中重用 udf 执行环境(甚至可能在整个事务中,但我还没有测试过)。虽然确实每次在处理导入语句时调用函数时都会处理导入语句,但 cpython 会快速检查模块是否已导入,如果已导入,则使用已导入的模块。像这样的函数本地(后期)导入经常用于解决循环依赖问题,因此必须具有高性能。我还使用它来解决 udf 中缺少全局初始化的问题,方法是执行以下操作:

if '_cache' not in globals(): import thing globals()['_cache'] = thing.build_cache() return _cache.get(arg)

除了 udf 之外,我不会在任何地方做任何事情,但这并不是完全通用的代码。

关于 udf 执行环境,它显然是 impl-detail,不应过分依赖,但实际上它不太可能很快发生重大变化。无法保证任何特定进程的寿命有多长/它将处理多少行,但只要他们可以保留它们符合他们的利益,因为 cpython 进程(和容器)的创建并不便宜 - 当然对每一行来说太重了。他们依赖 lxc 进行隔离,并为您提供一个真实的(如果是沙盒)Linux 环境来执行(如果您考虑一下,这对于用户安装的本机扩展确实是必要的)。文件系统上甚至还有一些文档可供那些愿意挖掘的人使用:)

返回值

虽然返回值的数据类型确实无法更改,但 redshift(现在?)支持参数和返回类型的 ANYELEMENT 数据类型。如上一个答案所述,它仍然需要一个明确的类型化参数,因为仍然不支持返回类型多态性,但它至少可以省去为要返回的每种类型创建单独函数的麻烦。

【讨论】:

以上是关于在每个函数调用上导入 Redshift Python UDF的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中,我可以调用导入模块的 main() 吗?

python 模块导入

简单时间序列数据的 Redshift 性能

使用 NodeJS 将 RabbitMQ 导入 AWS Redshift

Redshift:DateDiff 调用上的 SqlAlchemy 错误

如何设计在 Lambda 函数上运行的可扩展 ETL