统计架构#

警告

本规范应被视为实验性的。

原理#

统计数据对于快速查询处理非常有用。许多查询引擎使用统计数据来优化其查询计划。

Apache Arrow 格式本身没有统计数据,但其他可以读取为 Apache Arrow 数据的文件格式可能包含统计数据。例如,Apache Parquet C++ 实现可以将 Apache Parquet 文件读取为 Apache Arrow 数据,而 Apache Parquet 文件可能包含统计数据。

我们将统计数据的表示标准化为 Apache Arrow 数组,以便于交换。

用例#

Arrow C 流接口 的用例之一如下:

  1. 模块 A 将 Apache Parquet 文件读取为 Apache Arrow 数据。

  2. 模块 A 通过 Arrow C 流接口将读取到的 Apache Arrow 数据传递给模块 B。

  3. 模块 B 处理传递的 Apache Arrow 数据。

如果模块 A 可以将与 Apache Parquet 文件关联的统计数据传递给模块 B,则模块 B 可以使用这些统计数据来优化其查询计划。

例如,DuckDB 使用了这种方法,但 DuckDB 无法使用统计数据,因为没有标准化的方式来表示 Apache Arrow 数据的统计数据。

目标#

  • 建立将统计数据表示为 Apache Arrow 数组的标准方式。

非目标#

  • 建立传递表示统计数据的 Apache Arrow 数组的标准方式。

  • 建立将统计数据嵌入到 Apache Arrow 数组本身的标准方式。

架构#

本规范仅提供统计数据的架构。这是将 Apache Arrow 数据集统计数据表示为 Apache Arrow 数据的规范架构。

以下是统计数据架构的概要:

struct<
  column: int32,
  statistics: map<
    key: dictionary<values: utf8, indices: int32>,
    items: dense_union<...all needed types...>
  >
>

以下是顶级 struct 的详细信息:

姓名

数据类型

可为空

备注

column

int32

true

基于零的列索引,如果统计数据描述的是整个表或记录批次,则为 null。

列索引的计算方式与 记录批次消息 使用的规则相同。

statistics

映射型 (map)

false

目标列、表或记录批次的统计数据。详情请参阅下面的单独表格。

以下是 statisticsmap 的详细信息:

键或项

数据类型

可为空

备注

key

dictionary<values: utf8, indices: int32>

false

字符串键是统计数据的名称。由于相同的统计数据可能会为不同的列重复出现,因此使用字典编码以提高效率。为精确和近似的统计值分配不同的键。每个统计数据都有以下单独的描述。

items

dense_union

false

统计值是密集联合。它至少包含基于键中统计种类所需的所有类型。例如,当您有一个 int64 的不同计数统计和一个 float64 的平均字节宽度统计时,您至少需要 int64float64 类型。请参阅以下每个统计数据的描述。

密集联合数组有每个字段的名称,但我们不对这些字段名称进行标准化,因为我们可以通过类型代码而不是字段名称来访问正确的字段。因此,我们可以为字段使用任何有效的名称。

标准统计数据#

每种统计数据都具有一个名称,该名称作为统计数据映射中的键出现在每列或整个表中。dictionary<values: utf8, indices: int32> 用于编码名称以节省空间。

我们为相同统计数据的变体分配不同的名称,而不是使用标志。例如,我们为“不同计数”统计的精确值和近似值分配不同的统计名称。

冒号符号 : 将用作命名空间分隔符,例如 自定义应用程序元数据。它可以在名称中多次使用。

ARROW 前缀是此规范当前和未来版本中预定义统计数据名称的保留命名空间。用户定义的统计数据不得使用它。例如,您可以使用您的产品名称作为命名空间,例如 MY_PRODUCT:my_statistics:exact

以下是预定义的统计名称:

姓名

数据类型

备注

ARROW:average_byte_width:exact

float64

目标列中行的平均大小(以字节为单位)。(精确)

ARROW:average_byte_width:approximate

float64

目标列中行的平均大小(以字节为单位)。(近似)

ARROW:distinct_count:exact

int64

目标列中不同值的数量。(精确)

ARROW:distinct_count:approximate

float64

目标列中不同值的数量。(近似)

ARROW:max_byte_width:exact

int64

目标列中行的最大大小(以字节为单位)。(精确)

ARROW:max_byte_width:approximate

float64

目标列中行的最大大小(以字节为单位)。(近似)

ARROW:max_value:exact

取决于目标

目标列中的最大值。(精确)

ARROW:max_value:approximate

取决于目标

目标列中的最大值。(近似)

ARROW:min_value:exact

取决于目标

目标列中的最小值。(精确)

ARROW:min_value:approximate

取决于目标

目标列中的最小值。(近似)

ARROW:null_count:exact

int64

目标列中空值的数量。(精确)

ARROW:null_count:approximate

float64

目标列中空值的数量。(近似)

ARROW:row_count:exact

int64

目标表、记录批次或数组中的行数。(精确)

ARROW:row_count:approximate

float64

目标表、记录批次或数组中的行数。(近似)

如果您发现对多个系统可能有用且有益的统计数据,请在 Apache Arrow 开发邮件列表上提出。

当统计数据的生产者和消费者遵循预先商定的统计规范时,互操作性会得到改善。

示例#

以下是一些帮助您理解的示例。

简单记录批次#

Schema

vendor_id: int32
passenger_count: int64

数据

vendor_id:       [5, 1, 5, 1, 5]
passenger_count: [1, 1, 2, 0, null]

统计数据

目标

姓名

记录批次

行数

5

vendor_id

空值数量

0

不同值数量

2

最大值

5

最小值

1

passenger_count

空值数量

1

不同值数量

3

最大值

2

最小值

0

列索引

索引

目标

0

vendor_id

1

passenger_count

统计模式

struct<
  column: int32,
  statistics: map<
    key: dictionary<values: utf8, indices: int32>,
    items: dense_union<0: int64>
  >
>

统计数组

column: [
  null, # record batch
  0,    # vendor_id
  1,    # passenger_count
]
statistics:
  offsets: [
    0,
    1, # record batch: 1 value: [0]
    5, # vendor_id: 4 values: [1, 2, 3, 4]
    9, # passenger_count: 4 values: [5, 6, 7, 8]
  ]
  key:
    values: [
      "ARROW:row_count:exact",
      "ARROW:null_count:exact",
      "ARROW:distinct_count:exact",
      "ARROW:max_value:exact",
      "ARROW:min_value:exact",
    ]
    indices: [
      0, # "ARROW:row_count:exact"
      1, # "ARROW:null_count:exact"
      2, # "ARROW:distinct_count:exact"
      3, # "ARROW:max_value:exact"
      4, # "ARROW:min_value:exact"
      1, # "ARROW:null_count:exact"
      2, # "ARROW:distinct_count:exact"
      3, # "ARROW:max_value:exact"
      4, # "ARROW:min_value:exact"
    ]
  items:
    children:
      0: [ # int64
        5, # record batch: "ARROW:row_count:exact"
        0, # vendor_id: "ARROW:null_count:exact"
        2, # vendor_id: "ARROW:distinct_count:exact"
        5, # vendor_id: "ARROW:max_value:exact"
        1, # vendor_id: "ARROW:min_value:exact"
        1, # passenger_count: "ARROW:null_count:exact"
        3, # passenger_count: "ARROW:distinct_count:exact"
        2, # passenger_count: "ARROW:max_value:exact"
        0, # passenger_count: "ARROW:min_value:exact"
      ]
    types: [ # all values are int64
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
      0,
    ]
    offsets: [
      0,
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
    ]

复杂记录批次#

这使用了嵌套类型。

Schema

col1: struct<a: int32, b: list<item: int64>, c: float64>
col2: utf8

数据

col1: [
        {a: 1, b: [20, 30, 40], c: 2.9},
        {a: 2, b: null,         c: -2.9},
        {a: 3, b: [99],         c: null},
      ]
col2: ["x", null, "z"]

统计数据

目标

姓名

记录批次

行数

3

col1

空值数量

0

col1.a

空值数量

0

不同值数量

3

近似最大值

5

近似最小值

0

col1.b

空值数量

1

col1.b.item

最大值

99

最小值

20

col1.c

空值数量

1

近似最大值

3.0

近似最小值

-3.0

col2

空值数量

1

不同值数量

2

列索引

索引

目标

0

col1

1

col1.a

2

col1.b

3

col1.b.item

4

col1.c

5

col2

另请参阅 记录批次消息,了解如何计算列索引。

统计模式

struct<
  column: int32,
  statistics: map<
    key: dictionary<values: utf8, indices: int32>,
    items: dense_union<
      # For the number of rows, the number of nulls and so on.
      0: int64,
      # For the max/min values of col1.c.
      1: float64
    >
  >
>

统计数组

column: [
  null, # record batch
  0,    # col1
  1,    # col1.a
  2,    # col1.b
  3,    # col1.b.item
  4,    # col1.c
  5,    # col2
]
statistics:
  offsets: [
    0,
    1,  # record batch: 1 value: [0]
    2,  # col1: 1 value: [1]
    6,  # col1.a: 4 values: [2, 3, 4, 5]
    7,  # col1.b: 1 value: [6]
    9,  # col1.b.item: 2 values: [7, 8]
    12, # col1.c: 3 values: [9, 10, 11]
    14, # col2: 2 values: [12, 13]
  ]
  key:
    values: [
      "ARROW:row_count:exact",
      "ARROW:null_count:exact",
      "ARROW:distinct_count:exact",
      "ARROW:max_value:approximate",
      "ARROW:min_value:approximate",
      "ARROW:max_value:exact",
      "ARROW:min_value:exact",
    ]
    indices: [
      0, # "ARROW:row_count:exact"
      1, # "ARROW:null_count:exact"
      1, # "ARROW:null_count:exact"
      2, # "ARROW:distinct_count:exact"
      3, # "ARROW:max_value:approximate"
      4, # "ARROW:min_value:approximate"
      1, # "ARROW:null_count:exact"
      5, # "ARROW:max_value:exact"
      6, # "ARROW:min_value:exact"
      1, # "ARROW:null_count:exact"
      3, # "ARROW:max_value:approximate"
      4, # "ARROW:min_value:approximate"
      1, # "ARROW:null_count:exact"
      2, # "ARROW:distinct_count:exact"
    ]
  items:
    children:
      0: [ # int64
        3,  # record batch: "ARROW:row_count:exact"
        0,  # col1: "ARROW:null_count:exact"
        0,  # col1.a: "ARROW:null_count:exact"
        3,  # col1.a: "ARROW:distinct_count:exact"
        5,  # col1.a: "ARROW:max_value:approximate"
        0,  # col1.a: "ARROW:min_value:approximate"
        1,  # col1.b: "ARROW:null_count:exact"
        99, # col1.b.item: "ARROW:max_value:exact"
        20, # col1.b.item: "ARROW:min_value:exact"
        1,  # col1.c: "ARROW:null_count:exact"
        1,  # col2: "ARROW:null_count:exact"
        2,  # col2: "ARROW:distinct_count:exact"
      ]
      1: [ # float64
        3.0,  # col1.c: "ARROW:max_value:approximate"
        -3.0, # col1.c: "ARROW:min_value:approximate"
      ]
    types: [
      0, # int64: record batch: "ARROW:row_count:exact"
      0, # int64: col1: "ARROW:null_count:exact"
      0, # int64: col1.a: "ARROW:null_count:exact"
      0, # int64: col1.a: "ARROW:distinct_count:exact"
      0, # int64: col1.a: "ARROW:max_value:approximate"
      0, # int64: col1.a: "ARROW:min_value:approximate"
      0, # int64: col1.b: "ARROW:null_count:exact"
      0, # int64: col1.b.item: "ARROW:max_value:exact"
      0, # int64: col1.b.item: "ARROW:min_value:exact"
      0, # int64: col1.c: "ARROW:null_count:exact"
      1, # float64: col1.c: "ARROW:max_value:approximate"
      1, # float64: col1.c: "ARROW:min_value:approximate"
      0, # int64: col2: "ARROW:null_count:exact"
      0, # int64: col2: "ARROW:distinct_count:exact"
    ]
    offsets: [
      0,  # int64: record batch: "ARROW:row_count:exact"
      1,  # int64: col1: "ARROW:null_count:exact"
      2,  # int64: col1.a: "ARROW:null_count:exact"
      3,  # int64: col1.a: "ARROW:distinct_count:exact"
      4,  # int64: col1.a: "ARROW:max_value:approximate"
      5,  # int64: col1.a: "ARROW:min_value:approximate"
      6,  # int64: col1.b: "ARROW:null_count:exact"
      7,  # int64: col1.b.item: "ARROW:max_value:exact"
      8,  # int64: col1.b.item: "ARROW:min_value:exact"
      9,  # int64: col1.c: "ARROW:null_count:exact"
      0,  # float64: col1.c: "ARROW:max_value:approximate"
      1,  # float64: col1.c: "ARROW:min_value:approximate"
      10, # int64: col2: "ARROW:null_count:exact"
      11, # int64: col2: "ARROW:distinct_count:exact"
    ]

简单数组#

Schema

int64

数据

[1, 1, 2, 0, null]

统计数据

目标

姓名

数组

行数

5

空值数量

1

不同值数量

3

最大值

2

最小值

0

列索引

索引

目标

0

数组

统计模式

struct<
  column: int32,
  statistics: map<
    key: dictionary<values: utf8, indices: int32>,
    items: dense_union<0: int64>
  >
>

统计数组

column: [
  0, # array
]
statistics:
  offsets: [
    0,
    5, # array: 5 values: [0, 1, 2, 3, 4]
  ]
  key:
    values: [
      "ARROW:row_count:exact",
      "ARROW:null_count:exact",
      "ARROW:distinct_count:exact",
      "ARROW:max_value:exact",
      "ARROW:min_value:exact",
    ]
    indices: [
      0, # "ARROW:row_count:exact"
      1, # "ARROW:null_count:exact"
      2, # "ARROW:distinct_count:exact"
      3, # "ARROW:max_value:exact"
      4, # "ARROW:min_value:exact"
    ]
  items:
    children:
      0: [ # int64
        5, # array: "ARROW:row_count:exact"
        1, # array: "ARROW:null_count:exact"
        3, # array: "ARROW:distinct_count:exact"
        2, # array: "ARROW:max_value:exact"
        0, # array: "ARROW:min_value:exact"
      ]
    types: [ # all values are int64
      0,
      0,
      0,
      0,
      0,
    ]
    offsets: [
      0,
      1,
      2,
      3,
      4,
    ]

复杂数组#

这使用了嵌套类型。

Schema

struct<a: int32, b: list<item: int64>, c: float64>

数据

[
  {a: 1, b: [20, 30, 40], c: 2.9},
  {a: 2, b: null,         c: -2.9},
  {a: 3, b: [99],         c: null},
]

统计数据

目标

姓名

数组

行数

3

空值数量

0

a

空值数量

0

不同值数量

3

近似最大值

5

近似最小值

0

b

空值数量

1

b.item

最大值

99

最小值

20

c

空值数量

1

近似最大值

3.0

近似最小值

-3.0

列索引

索引

目标

0

数组

1

a

2

b

3

b.item

4

c

另请参阅 记录批次消息,了解如何计算列索引。

统计模式

struct<
  column: int32,
  statistics: map<
    key: dictionary<values: utf8, indices: int32>,
    items: dense_union<
      # For the number of rows, the number of nulls and so on.
      0: int64,
      # For the max/min values of c.
      1: float64
    >
  >
>

统计数组

column: [
  0, # array
  1, # a
  2, # b
  3, # b.item
  4, # c
]
statistics:
  offsets: [
    0,
    2,  # array: 2 values: [0, 1]
    6,  # a: 4 values: [2, 3, 4, 5]
    7,  # b: 1 value: [6]
    9,  # b.item: 2 values: [7, 8]
    12, # c: 3 values: [9, 10, 11]
  ]
  key:
    values: [
      "ARROW:row_count:exact",
      "ARROW:null_count:exact",
      "ARROW:distinct_count:exact",
      "ARROW:max_value:approximate",
      "ARROW:min_value:approximate",
      "ARROW:max_value:exact",
      "ARROW:min_value:exact",
    ]
    indices: [
      0, # "ARROW:row_count:exact"
      1, # "ARROW:null_count:exact"
      1, # "ARROW:null_count:exact"
      2, # "ARROW:distinct_count:exact"
      3, # "ARROW:max_value:approximate"
      4, # "ARROW:min_value:approximate"
      1, # "ARROW:null_count:exact"
      5, # "ARROW:max_value:exact"
      6, # "ARROW:min_value:exact"
      1, # "ARROW:null_count:exact"
      3, # "ARROW:max_value:approximate"
      4, # "ARROW:min_value:approximate"
    ]
  items:
    children:
      0: [ # int64
        3,  # array: "ARROW:row_count:exact"
        0,  # array: "ARROW:null_count:exact"
        0,  # a: "ARROW:null_count:exact"
        3,  # a: "ARROW:distinct_count:exact"
        5,  # a: "ARROW:max_value:approximate"
        0,  # a: "ARROW:min_value:approximate"
        1,  # b: "ARROW:null_count:exact"
        99, # b.item: "ARROW:max_value:exact"
        20, # b.item: "ARROW:min_value:exact"
        1,  # c: "ARROW:null_count:exact"
      ]
      1: [ # float64
        3.0,  # c: "ARROW:max_value:approximate"
        -3.0, # c: "ARROW:min_value:approximate"
      ]
    types: [
      0, # int64: array: "ARROW:row_count:exact"
      0, # int64: array: "ARROW:null_count:exact"
      0, # int64: a: "ARROW:null_count:exact"
      0, # int64: a: "ARROW:distinct_count:exact"
      0, # int64: a: "ARROW:max_value:approximate"
      0, # int64: a: "ARROW:min_value:approximate"
      0, # int64: b: "ARROW:null_count:exact"
      0, # int64: b.item: "ARROW:max_value:exact"
      0, # int64: b.item: "ARROW:min_value:exact"
      0, # int64: c: "ARROW:null_count:exact"
      1, # float64: c: "ARROW:max_value:approximate"
      1, # float64: c: "ARROW:min_value:approximate"
    ]
    offsets: [
      0, # int64: array: "ARROW:row_count:exact"
      1, # int64: array: "ARROW:null_count:exact"
      2, # int64: a: "ARROW:null_count:exact"
      3, # int64: a: "ARROW:distinct_count:exact"
      4, # int64: a: "ARROW:max_value:approximate"
      5, # int64: a: "ARROW:min_value:approximate"
      6, # int64: b: "ARROW:null_count:exact"
      7, # int64: b.item: "ARROW:max_value:exact"
      8, # int64: b.item: "ARROW:min_value:exact"
      9, # int64: c: "ARROW:null_count:exact"
      0, # float64: c: "ARROW:max_value:approximate"
      1, # float64: c: "ARROW:min_value:approximate"
    ]