跳到主要内容

概述

添加新的 PRESIGN SQL 语句,以便用户可以生成用于上传或下载的预签名 URL。

动机

Databend 支持通过内部 Stage 加载数据:

  • 调用 HTTP API upload_to_stage 上传文件:curl -H "x-databend-stage-name:my_int_stage" -F "upload=@./books.csv" -XPUT http://localhost:8000/v1/upload_to_stage
  • 调用 COPY INTO 复制数据:COPY INTO books FROM '@my_int_stage'

此工作流程的吞吐量受限于 databend 的 HTTP API:upload_to_stage。我们可以通过允许用户直接上传到我们的后端存储来提高吞吐量。例如,我们可以使用 AWS 认证请求:使用查询参数 生成预签名 URL。这样,用户可以直接将内容上传到 AWS s3,而无需通过 databend。

此外,PRESIGN 可以减少网络开销。所有流量都直接从用户端到 s3,无需额外的 databend 实例成本。

指南级解释

用户可以生成用于读取的 URL:

MySQL [(none)]> PRESIGN @my_stage/books.csv
+--------+---------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+---------+---------------------------------------------------------------------------------+
| GET | [] | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+---------+---------------------------------------------------------------------------------+

默认情况下,预签名 URL 将在 1 小时后过期。用户可以指定过期时间为 2 小时,如下所示:

MySQL [(none)]> PRESIGN @my_stage/books.csv EXPIRE=7200
+--------+---------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+---------+---------------------------------------------------------------------------------+
| GET | {} | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+---------+---------------------------------------------------------------------------------+

默认情况下,预签名 URL 是为 download 操作生成的。用户可以创建用于 upload 的 URL,如下所示:

MySQL [(none)]> PRESIGN UPLOAD @my_stage/books.csv EXPIRE=7200
+--------+---------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+---------+---------------------------------------------------------------------------------+
| PUT | {} | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+---------+---------------------------------------------------------------------------------+

如果 presign 返回的 headers 不为空,用户应在实际请求中包含它们。

MySQL [(none)]> PRESIGN UPLOAD @my_stage/books.csv
+--------+--------------------------+---------------------------------------------------------------------------------+
| method | headers | url |
+--------+--------------------------+---------------------------------------------------------------------------------+
| PUT | {'x-amz-key': 'value'} | https://example.s3.amazonaws.com/books.csv?X-Amz-Algorithm=AWS4-HMAC-SHA256&... |
+--------+--------------------------+---------------------------------------------------------------------------------+

参考级解释

PRESIGN 将作为语句而不是函数实现,以便我们可以同时返回 HTTP 方法、headers 和 URL。

大部分工作已经通过 Apache OpenDAL presign 完成。

语法将是:

PRESIGN [(DOWNLOAD | UPLOAD)] <location> [EXPIRE = <SECONDS>]

在 databend 中,我们将:

  • 在解析器中添加 PRESIGN(仅在新规划器中)
  • 实现 presign 解释器
  • 为 presign 添加状态测试
  • 添加关于 presign 的文档

缺点

无。

基本原理和替代方案

Snowflake GET_PRESIGNED_URL

Snowflake 有一个名为 GET_PRESIGNED_URL 的 SQL 函数。

GET_PRESIGNED_URL( @<stage_name> , '<relative_file_path>' , [ <expiration_time> ] )

用户只能获取用于下载的 Stage 中文件的预签名 URL:

select get_presigned_url(@images_stage, 'us/yosemite/half_dome.jpg', 3600);

+================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================-------+
| GET_PRESIGNED_URL(@IMAGES_STAGE, 'US/YOSEMITE/HALF_DOME.JPG', 3600) |
|================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================-------|
| http://myaccount.s3.amazonaws.com/national_parks/us/yosemite/half_dome.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxAus-west-xxxxxxxxxaws1_request&X-Amz-Date=20200625T162738Z&X-Amz-Expires=3600&X-Amz-Security-Token=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-Amz-SignedHeaders=host&X-Amz-Signature=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |
+================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================================-------+

Snowflake 不允许通过 SQL 生成用于上传的预签名 URL。相反,他们在 SDK 中实现了类似的功能。

以 snowflake golang SDK 为例:

他们 构建传输客户端 使用短期 s3 令牌:

func (util *snowflakeS3Client) createClient(info *execResponseStageInfo, useAccelerateEndpoint bool) (cloudClient, error) {
stageCredentials := info.Creds
var resolver s3.EndpointResolver
if info.EndPoint != "" {
resolver = s3.EndpointResolverFromURL("https://" + info.EndPoint) // FIPS endpoint
}

return s3.New(s3.Options{
Region: info.Region,
Credentials: aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(
stageCredentials.AwsKeyID,
stageCredentials.AwsSecretKey,
stageCredentials.AwsToken)),
EndpointResolver: resolver,
UseAccelerate: useAccelerateEndpoint,
}), nil
}

execResponseStageInfo 通过 内部 API 获取:

jsonBody, err := json.Marshal(req)
if err != nil {
return nil, err
}

data, err := sc.rest.FuncPostQuery(ctx, sc.rest, &url.Values{}, headers,
jsonBody, sc.rest.RequestTimeout, requestID, sc.cfg)
if err != nil {
return data, err
}
code := -1
if data.Code != "" {
code, err = strconv.Atoi(data.Code)
if err != nil {
return data, err
}
}

Databend 倾向于在内核中实现相关功能,而不是在 SDK 中。

未解决的问题

无。

未来的可能性

扩展对位置的支持

我们可以扩展对 COPY 类似位置的支持:

PRESIGN 's3://bucket/books.csv'

多部分支持

我们可以生成多部分相关操作,以允许上传单个 10TB 文件:

PRESIGN UPLOAD_PART 's3://bucket/books.csv.zst'
开始使用 Databend Cloud
低成本
快速分析
多种数据源
弹性扩展
注册