slug: /zh/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout machine_translated: true machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd sidebar_position: 41

sidebar_label: “\u5728\u5185\u5B58\u4E2D\u5B58\u50A8\u5B57\u5178”

在内存中存储字典 {#dicts-external-dicts-dict-layout}

有多种方法可以将字典存储在内存中。

我们建议 , 散列complex_key_hashed. 其提供最佳的处理速度。

不建议使用缓存,因为性能可能较差,并且难以选择最佳参数。 阅读更多的部分 “缓存”.

有几种方法可以提高字典性能:

  • 调用该函数以使用后的字典 GROUP BY.
  • 将要提取的属性标记为”注射”。 如果不同的属性值对应于不同的键,则称为注射属性。 所以当 GROUP BY 使用由键获取属性值的函数,此函数会自动取出 GROUP BY.

ClickHouse为字典中的错误生成异常。 错误示例:

  • 无法加载正在访问的字典。
  • 查询错误 cached 字典

您可以查看外部字典的列表及其状态 system.dictionaries 桌子

配置如下所示:

  1. <clickhouse>
  2. <dictionary>
  3. ...
  4. <layout>
  5. <layout_type>
  6. <!-- layout settings -->
  7. </layout_type>
  8. </layout>
  9. ...
  10. </dictionary>
  11. </clickhouse>

相应的 DDL-查询:

  1. CREATE DICTIONARY (...)
  2. ...
  3. LAYOUT(LAYOUT_TYPE(param value)) -- layout settings
  4. ...

在内存中存储字典的方法 {#ways-to-store-dictionaries-in-memory}

平 {#flat}

字典以平面数组的形式完全存储在内存中。 字典使用多少内存? 量与最大键的大小(在使用的空间中)成正比。

字典键具有 UInt64 类型和值限制为500,000。 如果在创建字典时发现较大的键,ClickHouse将引发异常,不会创建字典。

支持所有类型的来源。 更新时,数据(来自文件或表)将完整读取。

此方法在存储字典的所有可用方法中提供了最佳性能。

配置示例:

  1. <layout>
  2. <flat />
  3. </layout>

  1. LAYOUT(FLAT())

散列 {#dicts-external_dicts_dict_layout-hashed}

该字典以哈希表的形式完全存储在内存中。 字典中可以包含任意数量的带有任意标识符的元素,在实践中,键的数量可以达到数千万项。

支持所有类型的来源。 更新时,数据(来自文件或表)将完整读取。

配置示例:

  1. <layout>
  2. <hashed />
  3. </layout>

  1. LAYOUT(HASHED())

sparse_hashed {#dicts-external_dicts_dict_layout-sparse_hashed}

类似于 hashed,但使用更少的内存,有利于更多的CPU使用率。

配置示例:

  1. <layout>
  2. <sparse_hashed />
  3. </layout>
  1. LAYOUT(SPARSE_HASHED())

complex_key_hashed {#complex-key-hashed}

这种类型的存储是用于复合 . 类似于 hashed.

配置示例:

  1. <layout>
  2. <complex_key_hashed />
  3. </layout>
  1. LAYOUT(COMPLEX_KEY_HASHED())

range_hashed {#range-hashed}

字典以哈希表的形式存储在内存中,其中包含有序范围及其相应值的数组。

此存储方法的工作方式与散列方式相同,除了键之外,还允许使用日期/时间(任意数字类型)范围。

示例:该表格包含每个广告客户的折扣,格式为:

  1. +---------|-------------|-------------|------+
  2. | advertiser id | discount start date | discount end date | amount |
  3. +===============+=====================+===================+========+
  4. | 123 | 2015-01-01 | 2015-01-15 | 0.15 |
  5. +---------|-------------|-------------|------+
  6. | 123 | 2015-01-16 | 2015-01-31 | 0.25 |
  7. +---------|-------------|-------------|------+
  8. | 456 | 2015-01-01 | 2015-01-15 | 0.05 |
  9. +---------|-------------|-------------|------+

要对日期范围使用示例,请定义 range_minrange_max 中的元素 结构. 这些元素必须包含元素 nametype (如果 type 如果没有指定,则默认类型将使用-Date)。 type 可以是任何数字类型(Date/DateTime/UInt64/Int32/others)。

示例:

  1. <structure>
  2. <id>
  3. <name>Id</name>
  4. </id>
  5. <range_min>
  6. <name>first</name>
  7. <type>Date</type>
  8. </range_min>
  9. <range_max>
  10. <name>last</name>
  11. <type>Date</type>
  12. </range_max>
  13. ...

  1. CREATE DICTIONARY somedict (
  2. id UInt64,
  3. first Date,
  4. last Date
  5. )
  6. PRIMARY KEY id
  7. LAYOUT(RANGE_HASHED())
  8. RANGE(MIN first MAX last)

要使用这些字典,您需要将附加参数传递给 dictGetT 函数,为其选择一个范围:

  1. dictGetT('dict_name', 'attr_name', id, date)

此函数返回指定的值 ids和包含传递日期的日期范围。

算法的详细信息:

  • 如果 id 未找到或范围未找到 id,它返回字典的默认值。
  • 如果存在重叠范围,则可以使用任意范围。
  • 如果范围分隔符是 NULL 或无效日期(如1900-01-01或2039-01-01),范围保持打开状态。 范围可以在两侧打开。

配置示例:

  1. <clickhouse>
  2. <dictionary>
  3. ...
  4. <layout>
  5. <range_hashed />
  6. </layout>
  7. <structure>
  8. <id>
  9. <name>Abcdef</name>
  10. </id>
  11. <range_min>
  12. <name>StartTimeStamp</name>
  13. <type>UInt64</type>
  14. </range_min>
  15. <range_max>
  16. <name>EndTimeStamp</name>
  17. <type>UInt64</type>
  18. </range_max>
  19. <attribute>
  20. <name>XXXType</name>
  21. <type>String</type>
  22. <null_value />
  23. </attribute>
  24. </structure>
  25. </dictionary>
  26. </clickhouse>

  1. CREATE DICTIONARY somedict(
  2. Abcdef UInt64,
  3. StartTimeStamp UInt64,
  4. EndTimeStamp UInt64,
  5. XXXType String DEFAULT ''
  6. )
  7. PRIMARY KEY Abcdef
  8. RANGE(MIN StartTimeStamp MAX EndTimeStamp)

缓存 {#cache}

字典存储在具有固定数量的单元格的缓存中。 这些单元格包含经常使用的元素。

搜索字典时,首先搜索缓存。 对于每个数据块,所有在缓存中找不到或过期的密钥都从源请求,使用 SELECT attrs... FROM db.table WHERE id IN (k1, k2, ...). 然后将接收到的数据写入高速缓存。

对于缓存字典,过期 使用寿命 可以设置高速缓存中的数据。 如果更多的时间比 lifetime 自从在单元格中加载数据以来,单元格的值不被使用,并且在下次需要使用时重新请求它。 这是存储字典的所有方法中最不有效的。 缓存的速度在很大程度上取决于正确的设置和使用场景。 缓存类型字典只有在命中率足够高(推荐99%或更高)时才能表现良好。 您可以查看平均命中率 system.dictionaries 桌子

要提高缓存性能,请使用以下子查询 LIMIT,并从外部调用字典函数。

支持 来源:MySQL的,ClickHouse的,可执行文件,HTTP.

设置示例:

  1. <layout>
  2. <cache>
  3. <!-- The size of the cache, in number of cells. Rounded up to a power of two. -->
  4. <size_in_cells>1000000000</size_in_cells>
  5. </cache>
  6. </layout>

  1. LAYOUT(CACHE(SIZE_IN_CELLS 1000000000))

设置足够大的缓存大小。 你需要尝试选择细胞的数量:

  1. 设置一些值。
  2. 运行查询,直到缓存完全满。
  3. 使用评估内存消耗 system.dictionaries 桌子
  4. 增加或减少单元数,直到达到所需的内存消耗。

!!! warning “警告” 不要使用ClickHouse作为源,因为处理随机读取的查询速度很慢。

complex_key_cache {#complex-key-cache}

这种类型的存储是用于复合 . 类似于 cache.

直接 {#direct}

字典不存储在内存中,并且在处理请求期间直接转到源。

字典键具有 UInt64 类型。

所有类型的 来源,除了本地文件,支持。

配置示例:

  1. <layout>
  2. <direct />
  3. </layout>

  1. LAYOUT(DIRECT())

ip_trie {#ip-trie}

这种类型的存储用于将网络前缀(IP地址)映射到ASN等元数据。

示例:该表包含网络前缀及其对应的AS号码和国家代码:

  1. +-----------|-----|------+
  2. | prefix | asn | cca2 |
  3. +=================+=======+========+
  4. | 202.79.32.0/20 | 17501 | NP |
  5. +-----------|-----|------+
  6. | 2620:0:870::/48 | 3856 | US |
  7. +-----------|-----|------+
  8. | 2a02:6b8:1::/48 | 13238 | RU |
  9. +-----------|-----|------+
  10. | 2001:db8::/32 | 65536 | ZZ |
  11. +-----------|-----|------+

使用此类布局时,结构必须具有复合键。

示例:

  1. <structure>
  2. <key>
  3. <attribute>
  4. <name>prefix</name>
  5. <type>String</type>
  6. </attribute>
  7. </key>
  8. <attribute>
  9. <name>asn</name>
  10. <type>UInt32</type>
  11. <null_value />
  12. </attribute>
  13. <attribute>
  14. <name>cca2</name>
  15. <type>String</type>
  16. <null_value>??</null_value>
  17. </attribute>
  18. ...
  19. </structure>
  20. <layout>
  21. <ip_trie>
  22. <access_to_key_from_attributes>true</access_to_key_from_attributes>
  23. </ip_trie>
  24. </layout>

  1. CREATE DICTIONARY somedict (
  2. prefix String,
  3. asn UInt32,
  4. cca2 String DEFAULT '??'
  5. )
  6. PRIMARY KEY prefix

该键必须只有一个包含允许的IP前缀的字符串类型属性。 还不支持其他类型。

对于查询,必须使用相同的函数 (dictGetT 与元组)至于具有复合键的字典:

  1. dictGetT('dict_name', 'attr_name', tuple(ip))

该函数采用任一 UInt32 对于IPv4,或 FixedString(16) 碌莽禄Ipv6拢IPv6:

  1. dictGetString('prefix', 'asn', tuple(IPv6StringToNum('2001:db8::1')))

还不支持其他类型。 该函数返回与此IP地址对应的前缀的属性。 如果有重叠的前缀,则返回最具体的前缀。

数据存储在一个 trie. 它必须完全适合RAM。

原始文章