Apache Doris & SelectDB 技术能力全面解析

Apache Doris 是一款开源的 MPP 数据库,以其优异的分析性能著称,被各行各业广泛应用在实时数据分析、湖仓融合分析、日志与可观测性分析、湖仓构建等场景。Apache Doris 目前被 5000 多家中大型的企业深度应用在生产系统中,包含互联网、金融、制造、电信、能源、物流、政务等行业。目前项目已在 GitHub 获得超过 13000 Star,汇集 600 多名社区开发者,月度活跃贡献者数量连续数月位居全球大数据开源项目榜首,成为全球大数据领域最活跃的开源项目之一。

大数据领域最活跃的开源项目之一.png

SelectDB 是北京飞轮数据科技有限公司基于 Apache Doris 研发的现代化实时数据仓库,提供包括面向私有化部署的 SelectDB Enterprise 和云原生存算分离的 SelectDB Cloud 云数仓服务。SelectDB 兼容 Apache Doris 的所有能力和接口,相比开源自建在安全、稳定、资源弹性等方面有明显优势。本文将对 Doris & SelectDB 适合的分析场景和技术能力进行概述解析。

1. Doris & SelectDB 典型分析场景

1. Doris & SelectDB 典型分析场景.jpeg

SelectDB & Doris 在如下的分析场景中通常是最优选择:

  • 实时分析
    • 面向内部的实时报表和 Dashboard。
    • Ad-Hoc 分析和自助式 BI。
    • 面向终端客户 / 用户的高并发分析(Customer / User Facing Analysis):比如电商平台面向数百万广告主 / 店铺的数据分析、面向数十万快递员的分析,这类分析通常需要非常高的并发和较低的延时。
    • 用户行为和画像分析(CDP,Customer Data Platform):分析用户参与、留存、转化等行为,A/B Testing,支持人群洞察和人群圈选等画像分析,实现精细化的运营和精准的营销。
  • 湖仓融合分析
    • 湖仓查询和计算加速:作为查询引擎直接查询 Iceberg, Hudi, Paimon, DeltaLake, Hive 等湖仓中的数据,在不移动数据的情况下,实现查询分析的数倍加速。
    • 多源联邦分析:作为统一的查询网关,支持跨多个数据源查询位于数据湖、数据仓库、数据库中的数据,实现联邦查询,简化架构并消除数据孤岛。
  • 可观测性和日志分析
    • 实现对日志、事件、traces 的高效存储和分析,替换 Elasticsearch, Loki, ClickHouse 等方案,实现数倍的性价比提升。
  • 湖仓构建
    • 对数据进行 ETL / ELT 实现数据的加工和建模,数据可以统一在 SelectDB & Doris 中存储管理,也可以将加工处理过的数据回写到 Iceberg,Hudi,Paimon,DeltaLake,Hive 等 Data LakeHouse 中,实现湖仓融合构建。

2. Doris & SelectDB 技术能力解析

2.1 极速

Doris & SelectDB 的查询分析性能非常优异,在宽表聚合分析和复杂多表关联场景中均表现突出:

  • 在宽表聚合场景下,使用 SSB-FLAT benchmark 测试,相同资源下,是 ClickHouse 的 3.4 倍,是 Presto 的 92 倍,是业界标杆产品 Snowflake 的 6 倍。
  • 在多表关联场景下,使用 TPC-H benchmark 测试,相同资源下,其性能可达到 Redshift 的 1.5 倍,ClickHouse 的 49 倍,是业界标杆产品 Snowflake 的 2.5 倍,是 Greenplum 和 Presto 的 15 倍。

Doris & SelectDB 如此卓越的性能主要得益于以下技术加持:

2.1.1 列式存储

Doris & SelectDB 默认使用列式存储组织数据,在数据分析场景下,相比行存储通常有 5-10 倍的性能提升,这是因为:

  • 只需要读取 SQL 查询涉及的列,不相关的列无需读取和解压过滤,大大降低了 IO 和 CPU 开销。
  • 同一列的数据类型一致,方便进行高效的数据编码和压缩。数值类型采用 RLE 编码,字符串使用字典编码。编码后使用 LZ4 或者 ZSTD 等压缩算法压缩,大幅减少了数据存储空间,也节省了读取 IO。

列式存储.jpeg

2.1.2 丰富的索引

Doris & SelectDB 支持丰富的索引机制,通过索引机制,可以大幅减少对不相关数据的读取和处理,从而能够大幅提升性能。从加速的查询和原理来看,Doris & SelectDB 的索引分为点查索引和跳数索引两大类

  • 点查索引:常用于加速点查。 其原理是通过索引定位到满足 WHERE 条件的位置,直接读取其所在行。点查索引在满足条件的行数较少时,效果尤为显著。其点查索引包括前缀索引和倒排索引。

    • 前缀索引:按照排序键以有序的方式存储数据,并每隔 1024 行数据创建一个稀疏前缀索引。索引中的 Key 是当前 1024 行中第一行中排序列的值。如果查询涉及已排序列,系统将找到相关 1024 行组的第一行,并从该行开始扫描。
    • 倒排索引:对创建了倒排索引的列,建立每个值到对应行号集合的倒排表。对于等值查询,先从倒排表中查到行号集合,然后直接读取对应行的数据,而无需逐行扫描匹配数据,从而减少 I/O 、加速查询。倒排索引还能加速范围过滤、文本关键词匹配,算法更加复杂但基本原理类似。

    丰富的索引.jpeg

  • **跳数索引:常用于加速分析。**原理是通过索引确定不满足 WHERE 条件的数据块,跳过这些不满足条件的数据块,只读取可能满足条件的数据块并再进行一次逐行过滤,最终得到满足条件的行。跳数索引在满足条件的行比较多时效果较好。其跳数索引包括 ZoneMap 索引、BloomFilter 索引、NGram BloomFilter 索引。

    • ZoneMap 索引:自动维护每一列的统计信息,为每一个数据文件(Segment)和数据块(Page)记录最大值、最小值、是否有 NULL、Sum。对于等值查询、范围查询、IS NULL,可以通过最大值、最小值、是否有 NULL 来判断数据文件和数据块是否可以包含满足条件的数据,如果没有则跳过不读对应的文件或数据块减少 I/O 加速查询。比如下方 SQL 所示,有一个过滤条件 a<='100101',总共有三个数据块,第一个数据块通过读 ZoneMap 索引发现,最大值是 100101,整个数据块都满足条件,那么直接从 ZoneMap 中取得 sum 结果作为第一个数据块的聚合结果,
&lt;think&gt;我们正在使用Apache Doris 2.0.15版本,该版本支持JSON数据的解析结构化存储。根据引用[2][3][4],Doris提供了多种方式来处理JSON数据,包括使用`curl`命令通过Stream Load导入JSON数据,并在表结构中定义JSON列或解析JSON成结构化列。 主要步骤: 1. 创建表:根据JSON数据结构设计表结构,可以选择将整个JSON存储为JSON类型的列,或者解析成各个结构化列。 2. 导入数据:使用Stream Load等方式导入JSON文件,并指定相应的格式解析参数。 根据引用[4],Doris支持JSON数据类型,可以直接创建JSON类型的列。同时,在导入时可以通过`jsonpaths``strip_outer_array`等参数来提取所需字段。 另外,引用[2]提到,Doris 2.1版本开始默认使用MoW(Merge-on-Write)存储方式,但我们在2.0.15版本中也可以使用类似的功能。不过,注意引用[2]中讨论的是更新能力,而我们的需求是导入存储。 具体操作: 假设我们有一个JSON文件,内容如下(引用[4]中的示例): ```json [ {&quot;id&quot;: 1, &quot;c_json&quot;: {&quot;name&quot;: &quot;tom&quot;, &quot;age&quot;: 35}}, {&quot;id&quot;: 2, &quot;c_json&quot;: {&quot;name&quot;: null, &quot;age&quot;: 28}}, {&quot;id&quot;: 3, &quot;c_json&quot;: {&quot;name&quot;: &quot;micheal&quot;, &quot;age&quot;: null}}, {&quot;id&quot;: 4, &quot;c_json&quot;: {&quot;name&quot;: null, &quot;age&quot;: null}}, {&quot;id&quot;: 5, &quot;c_json&quot;: null} ] ``` 我们想要将数据存储到Doris表中,有两种方式: 方式一:将整个JSON对象存储为一个JSON类型的列(适合灵活的JSON结构,但查询时需要解析) 方式二:将JSON中的字段解析成表的各个列(适合固定结构,查询效率高) 这里以方式二为例,将`c_json`中的`name``age`解析成单独的列。 步骤1:创建表 ```sql CREATE DATABASE IF NOT EXISTS example_db; USE example_db; CREATE TABLE IF NOT EXISTS json_table ( id INT, name VARCHAR(50), age INT ) ENGINE=OLAP DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 10 PROPERTIES ( &quot;replication_num&quot; = &quot;1&quot; ); ``` 步骤2:导入数据(使用Stream Load) 在命令行中使用curl(如引用[3]所示): ```bash curl --location-trusted -u root:password -T test_json.json -H &quot;format:json&quot; -H &quot;strip_outer_array:true&quot; -H &quot;columns: id, c_json, name=c_json.name, age=c_json.age&quot; http://fe_host:fe_http_port/api/example_db/json_table/_stream_load ``` 注意: - `-T`指定JSON文件路径 - `-H &quot;format:json&quot;`指定格式为JSON - `-H &quot;strip_outer_array:true&quot;`表示去除最外层的数组(因为示例JSON是一个数组) - `-H &quot;columns: id, c_json, name=c_json.name, age=c_json.age&quot;`:这里需要解释一下: 实际上,在导入过程中,我们可以通过`columns`参数来指定如何解析。但是,由于我们的JSON数据中,顶层有`id``c_json`两个字段,而`c_json`是一个对象。我们想要提取`c_json`中的`name``age`,并映射到表的列`name``age`。 因此,我们可以在`columns`中指定: id: 对应JSON中的id c_json: 对应JSON中的c_json(虽然我们不需要存储整个c_json,但是我们需要先提取它,然后才能获取其内部字段) name=c_json.name:从c_json字段中提取name age=c_json.age:从c_json字段中提取age 但是,上述方式在Doris中并不直接支持(因为c_json是一个对象,而Doris的Stream Load在解析时支持通过JSONPath提取嵌套字段)。实际上,我们可以使用`jsonpaths`参数来指定要提取的字段路径,然后通过`columns`进行重命名。 另一种更常见的做法是使用`jsonpaths``json_root`(如果需要指定根节点)来提取字段,然后通过`columns`映射到表的列名。 例如,我们可以这样写: ```bash curl --location-trusted -u root:password -T test_json.json -H &quot;format:json&quot; -H &quot;strip_outer_array:true&quot; -H &quot;jsonpaths: [\&quot;$.id\&quot;, \&quot;$.c_json.name\&quot;, \&quot;$.c_json.age\&quot;]&quot; -H &quot;columns: id, name, age&quot; http://fe_host:fe_http_port/api/example_db/json_table/_stream_load ``` 这里: - `jsonpaths`指定了三个路径:`id`(顶层),`c_json.name``c_json.age`。 - `columns`指定了表中三个列的顺序:id, name, age(与jsonpaths中的三个字段一一对应)。 注意:如果JSON中的字段名与表中的列名一致,也可以不指定`jsonpaths`,但这里我们需要提取嵌套字段,所以必须使用`jsonpaths`。 但是,上述方法在Doris 2.0.15中是否支持?根据引用[3][4],Doris支持使用`jsonpaths`来指定要提取的字段。因此,我们可以使用这种方式。 如果导入过程中遇到问题,可以查看返回结果,或者查看Doris的日志。 另外,如果JSON结构复杂或者需要更灵活的处理,可以考虑使用Doris的JSON函数在查询时解析(即方式一:将整个JSON存储为一个JSON列,然后使用`JSON_QUERY`等函数查询),但这种方式在存储时不需要解析,而在查询时解析可能会影响性能。 方式一建表示例: ```sql CREATE TABLE IF NOT EXISTS json_table_raw ( id INT, c_json JSON ) ENGINE=OLAP DUPLICATE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 10 PROPERTIES ( &quot;replication_num&quot; = &quot;1&quot; ); ``` 导入命令(不提取嵌套字段,直接导入整个JSON对象): ```bash curl --location-trusted -u root:password -T test_json.json -H &quot;format:json&quot; -H &quot;strip_outer_array:true&quot; http://fe_host:fe_http_port/api/example_db/json_table_raw/_stream_load ``` 然后,在查询时使用: ```sql SELECT id, c_json-&gt;&#39;name&#39; as name, c_json-&gt;&#39;age&#39; as age FROM json_table_raw; ``` 根据需求选择合适的方式。 总结: 在Doris 2.0.15中,解析JSON并存储为结构化数据可以通过以下步骤实现: 1. 创建表,定义好结构化列(或使用JSON类型列)。 2. 使用Stream Load导入,配合`jsonpaths``columns`参数来提取映射嵌套字段(对于结构化存储),或者直接导入整个JSON对象(使用JSON类型列)。 注意:在导入嵌套JSON时,确保路径正确,并且数据类型匹配。 如果遇到问题,请检查: - JSON格式是否正确(例如,是否有多余的逗号,字符串是否用双引号) - 路径是否正确 - 表结构是否与导入的数据匹配(数据类型、列名等) 最后,引用[1]提到2.0.15版本提升了性能稳定性,因此使用该版本可以更好地处理JSON数据。 相关问题: 1. 在Doris中,如何查询JSON类型字段中的特定属性? 2. 使用Stream Load导入JSON数据时,常见的错误有哪些?如何解决? 3. Doris支持哪些JSON函数?请举例说明。&lt;/think&gt;### 解析JSON数据并结构化存储到Doris 2.0.15 在Apache Doris 2.0.15中,可通过以下步骤实现JSON数据的解析结构化存储: --- #### 1. **创建表结构** 根据JSON数据结构设计表,建议使用**Merge-on-Write (MoW)** 存储引擎(Doris 2.1+默认)[^2]: ```sql CREATE TABLE json_data ( id INT NOT NULL, block_hash VARCHAR(255), amount_raw DECIMAL(20, 6), token_price DECIMAL(20, 6) ) ENGINE=OLAP UNIQUE KEY(id) DISTRIBUTED BY HASH(id) BUCKETS 10 PROPERTIES ( &quot;storage_format&quot; = &quot;V2&quot;, -- 启用MoW存储 &quot;enable_unique_key_merge_on_write&quot; = &quot;true&quot; ); ``` &gt; **说明**: &gt; - `UNIQUE KEY` 支持高效更新(如后续数据变更)[^2] &gt; - `storage_format = &quot;V2&quot;` 启用MoW优化写入性能 --- #### 2. **导入JSON数据** 使用`curl`通过**Stream Load**导入,需指定关键参数[^3][^4]: ```bash curl --location-trusted \ -u root:your_password \ -T /path/to/json_data.json \ -H &quot;format: json&quot; \ -H &quot;strip_outer_array: true&quot; \ -H &quot;jsonpaths: [\&quot;$.id\&quot;, \&quot;$.block_hash\&quot;, \&quot;$.amount_raw\&quot;, \&quot;$.token_price\&quot;]&quot; \ http://FE_HOST:FE_HTTP_PORT/api/DB_NAME/json_data/_stream_load ``` **关键参数解析**: - `strip_outer_array: true`:剥离JSON最外层数组(如`[...]`格式)[^4] - `jsonpaths`:显式指定JSON字段与表列的映射关系 - 若JSON嵌套复杂,可用`json_root`指定根路径(如`$.records`) --- #### 3. **验证数据** 查询导入结果: ```sql SELECT * FROM json_data LIMIT 5; ``` 若部分字段解析失败,检查: 1. JSON路径是否与`jsonpaths`匹配 2. 数据类型是否兼容(如字符串误导入数值列) --- #### 4. **高级场景处理** ##### (1) **动态JSON字段** 使用`JSON`类型列存储灵活结构[^4]: ```sql ALTER TABLE json_data ADD COLUMN attributes JSON DEFAULT NULL; ``` 查询时解析: ```sql SELECT id, attributes-&gt;&#39;$.detail.sender&#39; AS sender FROM json_data; ``` ##### (2) **批量更新** 通过`UNIQUE KEY`模型支持批量更新[^2]: ```sql INSERT INTO json_data (id, token_price) VALUES (101, 35.2) ON DUPLICATE KEY UPDATE token_price = VALUES(token_price); ``` --- ### 常见问题解决 | 问题现象 | 解决方案 | |---------|----------| | 字段类型不匹配 | 检查表列定义与JSON数据类型,必要时用`CAST`转换 | | 嵌套JSON解析失败 | 使用`jsonpaths`精确指定路径(如`$.parent.child`) | | 导入超时 | 增大`timeout`参数:`-H &quot;timeout: 600&quot;` | &gt; **性能提示**: &gt; 对频繁查询的JSON字段,建议提前解析成独立列,避免查询时实时解析开销[^2]。 --- ### 相关问题 1. 如何在Doris中高效更新JSON数据的特定字段? 2. Stream Load导入JSON时如何跳过错误行? 3. Doris的JSON类型与传统关系型数据库有何性能差异? [^1]: Apache Doris 2.0.15 版本发布 [^2]: Doris 2.1 - Updating Data 学习笔记 [^3]: Doris导入json数据实例 [^4]: Doris:JSON文档示例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SelectDB技术团队

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值