【问题标题】:Datatype or structure for tree-search树搜索的数据类型或结构
【发布时间】:2020-03-13 15:15:48
【问题描述】:

我正在使用一个大数据结构,它通过字符串标签描述项目树,例如文件系统中的路径字符串。有 ~198,000 个树元素和 ~9,100,000 个叶元素。

实施选项:

  • data type ltree 似乎是自然而然的选择。

  • 通过规范化表结构:为每条路径等创建序列索引和递归搜索

我假设在实施后,我可以通过EXPLAIN ANALYZE 比较两者。但是,在实施之前,是否可以预测差异或估计(速度和额外磁盘消耗)性能?


笔记和测试

关于查询(需求),它是开放的:“分层搜索引擎”必须具有表现力,当您用“字符串”表示分层信息时,ltree、正则表达式和有时 LIKE 运算符都具有表现力路径”。

使用具有 ~198,000 个树元素的 Guide 的类似 CREATE TABLE test (path ltree) 进行测试:

    CREATE INDEX path_gist_idx ON test USING GIST (path)
    ; -- consumes ~3G
    SELECT count(*) n 
    FROM test WHERE path ~ 'first.second.*.etc_etc_etc.*'
    ; -- n= 149068
    EXPLAIN ANALYSE 
      SELECT count(*) n 
      FROM test WHERE path ~ 'first.second.*.etc_etc_etc.*'
    ;  -- Planning Time: 0.075 ms
       -- Execution Time: 1317.443 ms

【问题讨论】:

  • 猜测性能是看手相。
  • 嗨@LaurenzAlbe,谢谢....所以,我的猜测是正确的,EXPLAIN ANALYZE(在开发替代解决方案之后)是唯一的方法。那么:删除问题还是等待“掌上阅读器”?
  • 如果您有路径字符串数据,ltree 绝对是最佳选择。除了数据量之外,您还期望什么波动,以及您要运行什么样的查询?
  • 谢谢@Bergi,我编辑了关于这个问题的注释......但是上下文是开放的,“数据挖掘”和“BI分析”,如此强大(最具表现力的分层运算符集)更好.

标签: postgresql performance


【解决方案1】:

规范化表的最佳解决方案似乎是“闭包表”,正如this question/solution 所推荐的那样。

在其他索引策略上,我们可以使用this question/answer的线索,建议尝试pg_trgm模块。另见https://www.postgresql.org/docs/current/pgtrgm.html

让我们检查所有解决方案。


蛮力(索引)

“蛮力”的优点是不需要更改数据结构,只需添加一个索引,就是在大数据上下文中我们有一些额外的磁盘空间。所以,只测试了索引,没有拆分成真正的树和指针。

测试ltree搜索

查看问题主体的测试。执行时间:~1300 毫秒。

良好的层次搜索运算符。唯一的问题是索引需要一个新列,当必须保留字符串以供其他用途时,或者不是ltree-ingestion 格式。我使用了转换

trim(
  replace( 
   regexp_replace(text_path,'[^A-Z0-9_/]+','_','ig'), -- clean
   '/',  -- the path separator at text_path
   '.'   -- the ltree separator
  ),
  '.'  -- clean
)::ltree

因此,当您转换其他问题时,它不是 100% 可逆的,以供演员 ltree2text() 重用。

测试pg_trgm 使用 LIKE 运算符进行搜索

这是更快的方法:~200 ms

CREATE EXTENSION pg_trgm;
CREATE INDEX textpath_gist_idx  ON t2_text USING gin (txt_path gin_trgm_ops);
EXPLAIN ANALYSE 
   SELECT count(*) n
   FROM   t2_text  
   WHERE  txt_path LIKE 'first/second/%/etc_etc_etc/%'
; --  Planning Time: 0.133 ms
  --  Execution Time: 195.524 ms
  -- ... BUT, returns zero on COUNT result!

索引USING gin (colName gin_trgm_ops) 使用更少的磁盘空间,~2G。

这里的问题不是性能而是它不起作用(!),而不是“两个%”的问题,因为在类似的查询中工作正常,所以它不可靠

  • COUNT WHERE txt_path LIKE 'first/second/%/etc_etc_etc/%' 为零。
  • COUNT WHERE txt_path LIKE '%etc_etc_etc%' 正确。

似乎是 PostgreSQL v12 错误(其他版本也一样?)。

测试pg_trgm用正则表达式搜索

执行时间约为 1000 毫秒,它比 ltree最可靠 tham LIKE 运算符快一点(并且磁盘消耗更少)。

EXPLAIN ANALYSE 
   SELECT count(*) n
   FROM   t2_text  
   WHERE  txt_path ~ '^first/second/.+/etc_etc_etc/'
--  Timing: Generation 2.312 ms, ..., Total 27.853 ms
--  Execution Time: 1017.293 ms

闭包表

如果不存在则需要添加一个id bigserial 和一个新表,其中ancestordescendant 指向原始表。它占用更多磁盘空间并且...我可以使用此模型进行所有查询吗?

它也消耗更多的程序员时间(!),没有自动化的方式(它是一个模板表,但 PostgreSQL 不提供''标准解决方案'')来创建额外表.对于查询,没有简单的运算符

...所以我没有添加测试,它是一个 Wiki:如果您在此处编辑某些内容,我可以添加测试以显示差异。

CREATE TABLE t2_treeclosure (
   ancestor bigint NOT NULL,
   descendant bigint NOT NULL,
   PRIMARY KEY (ancestor,descendant),
   FOREIGN KEY (ancestor) REFERENCES t2_text(id),
   FOREIGN KEY (descendant) REFERENCES t2_text(id)
 );

...有时可以减少原始t2_text table 减少文本的磁盘消耗,在滑过它之后。需要一个函数来重建路径等,但如果“表关闭”比“蛮力”方法更快。

【讨论】:

  • "没有自动创建闭包表的方法" - 你可以使用物化视图或触发器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 1970-01-01
  • 2020-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多