You need to enable JavaScript to run this app.
导航
MAP 类型
最近更新时间:2023.07.13 17:25:34首次发布时间:2022.12.15 10:08:27

类型简介

ByteHouse 支持 3 种 Map:

  • 自研 Map 类型:ByteMap,包含两种子 Map 类型:
    • 多隐式列 Map(Implicit Byte Map)
    • 单隐式列 Map(Implicit Compact Byte Map)
  • 社区 Map 类型:KV Map;

存储形式

ByteHouse 支持 3 种 Map差异如下:

  • 多隐式列:查询高效。但产生文件多,会占用大量 innode,且存储效率低。
  • 单隐式列:查询高效,能缓解占用磁盘大量 innode 的问题。但存储效率较低,不支持部分操作(主要是 ALTER TABLE database.table {(UPDATE) | (DELETE))。
  • KV Map:存储高效,能缓解占用磁盘大量 innode 的问题。但查询效率较低。

通常,所有 map key 总和的数量不多(<3000),推荐使用多隐式列 Map。
Key 数量很多时,根据功能和效率的权衡,可使用 KV Map 或 单隐式列 Map。

下面以一个例子介绍 3 种类型的存储形式:
有一个map列,列名为m,key和value的类型均为String,写入以下三行数据:
{'k1': 'v1', 'k2': 'v2', 'k3': 'v3'}
{'k1': 'v4', 'k3': 'v5'}
{'k2': 'v6'}

多隐式列 Map

图片
Implicit Map 会把 map 中的每个key都写入一个数据文件中,类型 Nullable(ValueType),处理起来和普通列一样,但由于对用户不透明,因此称为多隐式列 Map。
这种情况下,因为每个 key 占一列,根据 key 查询 value 的效率会非常高。

单隐式列 Map

图片
Implicit Map 由于每个 key 都会以隐式列的形式形成独立的文件,因此当 key 数量过多时可能会引发过多的 小文件对磁盘空间浪费的问题以及 inode 的大量消耗问题。为了解决这个问题,开发了单隐式列(Implicit Compact Map),基本思想是将同一个 map 的所有隐式列写入同一个文件中,checksum 记录的信息还是各个隐式列,只需要额外记录了各个文件的 offset 信息。
但使用 Implicit Compact Map 类型后,不能使用以下功能:

  1. 使用的 Map Value 类型为 LowCardinality。
  2. 使用ALTER TABLE database.table {(UPDATE) | (DELETE)} ... 这些会 rewrite all part的 mutations 操作。

KV Map

图片
通过隐式列的方式存储的优点是可以提高查询某个 key 的效率,直接查询对应的隐式列数据即可。但是由于每个隐式列的数据写入时都需要对不包含这些key的行进行填充 null。因此在key总量远远高于平均每行的key数量的情况下,会存在数据膨胀的问题,即隐式列中填充了大量的null值,带来存储低效的问题。
为了解决这个问题,引入了KV map,思路是将所有的key和所有的value都单独写在一个文件中,并将size信息写入单独的文件中表示每行数据的大小。
查询某个key时,KV map 需要将所有的map数据都读取出来后再逐行进行过滤,因此查询特定 key 的效率较低。

子类型支持

Key 类型

Integers, Floats, Date, DateTime, String, FixedString, LowCardinality, UUID, Enums,不支持Nullable。

Value 类型

KV Map 的 Value 类型没有限制;
单隐式 Map和 多隐式 Map 的 value 类型限制如下:Integers, Floats, Date, DateTime, String, FixedString, LowCardinality(Nullable()), Array, 不支持类型为Nullable。

使用举例
-- 创建Byte Map, enable_compact_map_data默认值为0
CREATE TABLE map_table (id UInt32, m Map(String, String)) ENGINE MergeTree() order by id [settings enable_compact_map_data = 0];
CREATE TABLE map_table (id UInt32, m Map(String, String) BYTE) ENGINE MergeTree() order by id [settings enable_compact_map_data = 0];

-- 创建Byte Compact Map
CREATE TABLE map_table (id UInt32, m Map(String, String)) ENGINE MergeTree() order by id settings enable_compact_map_data = 1;
CREATE TABLE map_table (id UInt32, m Map(String, String) BYTE) ENGINE MergeTree() order by id settings enable_compact_map_data = 1;

-- 创建KV Map
CREATE TABLE map_table (id UInt32, m Map(String, String) KV) ENGINE MergeTree() order by id;

-- 写入数据
INSERT INTO map_table VALUES (1, {'k1': 'v1', 'k2': 'v2'[, ...]});
INSERT INTO map_table VALUES (1, map('k1', 'v1', 'k2', 'v2'[, ...]));
INSERT INTO map_table FORMAT JSONEachRow {"id": "2", "m": {"k2": "v20", "k3": "v3"}};

-- 查询Map
select m from map_table;

┌─m──────────────────────┐
│ {'k1':'v1','k2':'v2'}  │
│ {'k2':'v20','k3':'v3'} │
└────────────────────────┘

-- 查询Implicit Map和Implicit Compact Map的Key
---- 方法一:{}查询
---- 从result header中可以看出,查询的是隐式列,缺省值填充Null
select m{'k1'} from map_table;
select mapElement(m, 'k1') from map_table;

┌─__m__'k1'─┐
│ v1        │
│ ᴺᵁᴸᴸ      │
└───────────┘
---- 方法二:[]查询
---- 从result header中可以看出,查询的是Map整列m,然后再筛选出特定的key 'k1',缺省值填充默认值(String类型为空字符串)
select m['k1'] from map_table;
select arrayElement(m, 'k1') from map_table;

┌─arrayElement(m, 'k1')─┐
│ v1                    │
│                       │
└───────────────────────┘

-- 查询KV Map的Key
---- 方法一: {}查询
---- 从result header中可以看出,查询的是Map整列m,然后再筛选出特定的key 'k1',缺省值填充Null
select m{'k1'} from map_table;
select mapElement(m, 'k1') from map_table;

┌─mapElement(m, 'k1')─┐
│ v1                  │
│ ᴺᵁᴸᴸ                │
└─────────────────────┘
---- 方法二: []查询
---- 从result header中可以看出,查询的是Map整列m,然后再筛选出特定的key 'k1',缺省值填充默认值(String类型为空字符串)
select m['k1'] from map_table;
select arrayElement(m, 'k1') from map_table;

┌─arrayElement(m, 'k1')─┐
│ v1                    │
│                       │
└───────────────────────┘

-- 查询Map keys
select mapKeys(m) from map_table;

┌─mapKeys(m)──┐
│ ['k1','k2'] │
│ ['k2','k3'] │
└─────────────┘

-- 查询Implicit Map和Implicit Compact Map类型的Key set
select getMapKeys('default', 'map_table', 'm');

┌─getMapKeys('default', 'map_table', 'm')─┐
│ ['k1','k2','k3']                        │
└─────────────────────────────────────────┘

相关参数

profile 配置

参数名

参数类型

无配置默认值

参数说明

max_map_key_num

UInt64

0

每张表中允许的不同map key数量限制,0表示无限制。此参数限制的是表中所有map列的不同key数量之和

allow_discard_map_key

Bool

false

超过map key数量限制后是否允许丢弃map key,参数为false会抛异常

merge_tree 中配置

参数名

参数类型

无配置默认值

参数说明

enable_compact_map_data

Bool

false

是否开启compact map

常见问题

Q: 一个 map 的 key 上限建议多少?
A:多隐式列 MAP ,建议设置 max_map_key_num 参数为 3000。单隐式列 MAP 和 KV map,可认为无需刻意限制 key 上限。