写点什么

开源云原生数据仓库 ByConity 实测,开启开启数据仓库的新篇章

作者:轻口味
  • 2024-12-19
    北京
  • 本文字数:5727 字

    阅读完需:约 19 分钟

开源云原生数据仓库 ByConity实测,开启开启数据仓库的新篇章


背景

在信息时代时代,数据量呈指数级增长,高效的数据处理和分析能力已经成为企业竞争力的关键。在实际业务中,用户会基于不同的产品分别构建实时数仓和离线数仓。其中,实时数仓强调数据能够快速入库,且在入库的第一时间就可以进行分析,低时延的返回分析结果。而离线数仓强调复杂任务能够稳定的执行完,需要更好的内存管理。


在联机分析处理(OLAP)领域,ClickHouse 是一个成熟开源的列式数据库管理系统(DBMS),它有着高性能、可扩展性、数据压缩、SQL 支持的特征,广泛应用于数据分析商业智能、日志分析等场景。


但是 ClickHouse 也有它自身的局限性,比如扩缩容成本高、复杂查询性能受限,鉴于这个原因,字节跳动基于 ClickHouse 内核开发并开源了 ByConity。


ClickHouse 从设计之初是面向 OLAP(在线分析)场景,无论是列存、索引还是执行向量化的优化,他们都有效地应对大宽表的聚合计算。


针对复杂查询,尤其是数据仓库中典型的 ETL 任务来说,ClickHouse 则并不擅长。结构复杂、耗时较长的数据加工作业,通常需要复杂的调优过程。典型的问题如下:


  • 重试成本高:对于运行时长在分钟级甚至小时级的 ETL 作业,如果运行过程中出现失败,ClickHouse 只能进行 query 级别的重试。从头重试不仅造成大量的资源浪费,也对加工任务的 SLA 提出了挑战。

  • 资源占用巨大:由于缺少迭代计算和有效的任务拆分,在查询数据量大、计算复杂的情况下,通常要求节点有充足的内存进行处理。

  • 并发控制:当多个查询同时运行时,ClickHouse 并不会根据资源的使用情况进行调度。任务之间相互挤压会导致失败(通常是 Memory limit 错误)。叠加重试机制的缺乏,通常会引起雪崩效应。


针对以上问题,ByConity 在 ClickHouse 高性能计算框架的基础上,增加了对 bsp 模式的支持:可以进行 task 级别的容错;更细粒度的调度;支持资源感知的调度。带来的收益有:


  • 当 query 运行中遇到错误时,可以自动重试当前的 task,而不是从头进行重试。大大减少重试成本。

  • 当 query 需要的内存巨大,甚至大于单机的内存时,可以通过增加并行度来减少单位时间内内存的占用。只需要调大并行度参数即可,理论上是可以无限扩展的。

  • 可以根据集群资源使用情况有序调度并发 ETL 任务,从而减少资源的挤占,避免频繁失败。


ByConity 官网:https://byconity.github.io/zh-cn/docs/introduction/what-is-byconity

ByConity 测试

测试环境


测试准备

  1. 打开终端,输入ssh -p 23 <提供的用户名>@<ECS服务器IP地址>,输入密码;

  2. 为避免使用时超时自动断开连接,请运行tmux new -s $user_id(如 tmux new -s user0001)命令创建一个新的 tmux 会话,其中$user_id是可以自定义的会话名称。(后续重新登录时,使用 tmux a -t $user_id)。

  3. 执行 clickhouse client --port 9010命令进入客户端。如果后续输入 SQL 会被截断,在此处可以执行clickhouse client --port 9010 -mn,此后 SQL 后需要加;作为结束。

  4. 使用测试用数据库 test_elt:use test_elt

  5. 由于 TPC-DS 定义的查询语法为标准 SQL,设置数据库会话的方言类型为 ANSI:set dialect_type = 'ANSI'


查询测试

基础联合查询

从数据表中找出在 2000 年有退货记录、退货总金额超过同店铺其他客户平均退货金额 1.2 倍且店铺位于田纳西州的前 100 个客户的编号,并按客户编号升序排列返回结果。SQL 语句如下:



with customer_total_return as( select sr_customer_sk as ctr_customer_sk, sr_store_sk as ctr_store_sk ,sum(sr_return_amt) as ctr_total_return from store_returns, date_dim where sr_returned_date_sk = d_date_sk and d_year = 2000 group by sr_customer_sk,sr_store_sk)select c_customer_idfrom customer_total_return ctr1, store, customerwhere ctr1.ctr_total_return > ( select avg(ctr_total_return) *1.2 from customer_total_return ctr2 where ctr1.ctr_store_sk = ctr2.ctr_store_sk)and s_store_sk = ctr1.ctr_store_skand s_state = 'TN'and ctr1.ctr_customer_sk = c_customer_skorder by c_customer_idlimit 100;
复制代码



执行效果如下:


查询速度还是很快的。

大内存查询
with ws as        (select d_year AS ws_sold_year, ws_item_sk,        ws_bill_customer_sk ws_customer_sk,        sum(ws_quantity) ws_qty,        sum(ws_wholesale_cost) ws_wc,        sum(ws_sales_price) ws_sp        from web_sales        left join web_returns on wr_order_number=ws_order_number and ws_item_sk=wr_item_sk        join date_dim on ws_sold_date_sk = d_date_sk        where wr_order_number is null        group by d_year, ws_item_sk, ws_bill_customer_sk        ),        cs as        (select d_year AS cs_sold_year, cs_item_sk,        cs_bill_customer_sk cs_customer_sk,        sum(cs_quantity) cs_qty,        sum(cs_wholesale_cost) cs_wc,        sum(cs_sales_price) cs_sp        from catalog_sales        left join catalog_returns on cr_order_number=cs_order_number and cs_item_sk=cr_item_sk        join date_dim on cs_sold_date_sk = d_date_sk        where cr_order_number is null        group by d_year, cs_item_sk, cs_bill_customer_sk        ),        ss as        (select d_year AS ss_sold_year, ss_item_sk,        ss_customer_sk,        sum(ss_quantity) ss_qty,        sum(ss_wholesale_cost) ss_wc,        sum(ss_sales_price) ss_sp        from store_sales        left join store_returns on sr_ticket_number=ss_ticket_number and ss_item_sk=sr_item_sk        join date_dim on ss_sold_date_sk = d_date_sk        where sr_ticket_number is null        group by d_year, ss_item_sk, ss_customer_sk        )        select        ss_sold_year, ss_item_sk, ss_customer_sk,        round(ss_qty/(coalesce(ws_qty,0)+coalesce(cs_qty,0)),2) ratio,        ss_qty store_qty, ss_wc store_wholesale_cost, ss_sp store_sales_price,        coalesce(ws_qty,0)+coalesce(cs_qty,0) other_chan_qty,        coalesce(ws_wc,0)+coalesce(cs_wc,0) other_chan_wholesale_cost,        coalesce(ws_sp,0)+coalesce(cs_sp,0) other_chan_sales_price        from ss        left join ws on (ws_sold_year=ss_sold_year and ws_item_sk=ss_item_sk and ws_customer_sk=ss_customer_sk)        left join cs on (cs_sold_year=ss_sold_year and cs_item_sk=ss_item_sk and cs_customer_sk=ss_customer_sk)        where (coalesce(ws_qty,0)>0 or coalesce(cs_qty, 0)>0) and ss_sold_year=2000        order by        ss_sold_year, ss_item_sk, ss_customer_sk,        ss_qty desc, ss_wc desc, ss_sp desc,        other_chan_qty,        other_chan_wholesale_cost,        other_chan_sales_price,        ratio        LIMIT 100;
复制代码


综合分析 2000 年店铺销售(store_sales)与网络销售(web_sales)、目录销售(catalog_sales)之间的销售数量、成本、价格等数据关系,筛选出有对应其他渠道销售记录(网络或目录)的店铺销售记录,并按照特定顺序展示前 100 条结果,帮助了解不同销售渠道在该年份针对相同商品和客户的业务情况对比。


执行时报错:


错误原因是内存超限。接下来利用 ByConity 的 bsp 模式执行,Sql 语句后面增加下面语句:


SETTINGS bsp_mode = 1, distributed_max_parallel_size = 12;
复制代码



执行结果如下:


ByConity 增加了 bsp 模式:可以进行 task 级别的容错;更细粒度的调度;基于资源感知的调度。通过 bsp 能力,把数据加工(T)的过程转移到 ByConity 内部,能够一站式完成数据接入、加工和分析。


这就是 bsp 的魅力,当 query 需要的内存巨大,甚至大于单机的内存时,可以通过增加并行度来减少单位时间内内存的占用。只需要调大并行度参数即可,理论上是可以无限扩展的。

模拟内存限制问题
with cs_ui as        (select cs_item_sk        ,sum(cs_ext_list_price) as sale,sum(cr_refunded_cash+cr_reversed_charge+cr_store_credit) as refund        from catalog_sales        ,catalog_returns        where cs_item_sk = cr_item_sk        and cs_order_number = cr_order_number        group by cs_item_sk        having sum(cs_ext_list_price)>2*sum(cr_refunded_cash+cr_reversed_charge+cr_store_credit)),        cross_sales as        (select i_product_name product_name        ,i_item_sk item_sk        ,s_store_name store_name        ,s_zip store_zip        ,ad1.ca_street_number b_street_number        ,ad1.ca_street_name b_street_name        ,ad1.ca_city b_city        ,ad1.ca_zip b_zip        ,ad2.ca_street_number c_street_number        ,ad2.ca_street_name c_street_name        ,ad2.ca_city c_city        ,ad2.ca_zip c_zip        ,d1.d_year as syear        ,d2.d_year as fsyear        ,d3.d_year s2year        ,count(*) cnt        ,sum(ss_wholesale_cost) s1        ,sum(ss_list_price) s2        ,sum(ss_coupon_amt) s3        FROM   store_sales        ,store_returns        ,cs_ui        ,date_dim d1        ,date_dim d2        ,date_dim d3        ,store        ,customer        ,customer_demographics cd1        ,customer_demographics cd2        ,promotion        ,household_demographics hd1        ,household_demographics hd2        ,customer_address ad1        ,customer_address ad2        ,income_band ib1        ,income_band ib2        ,item        WHERE  ss_store_sk = s_store_sk AND        ss_sold_date_sk = d1.d_date_sk AND        ss_customer_sk = c_customer_sk AND        ss_cdemo_sk= cd1.cd_demo_sk AND        ss_hdemo_sk = hd1.hd_demo_sk AND        ss_addr_sk = ad1.ca_address_sk and        ss_item_sk = i_item_sk and        ss_item_sk = sr_item_sk and        ss_ticket_number = sr_ticket_number and        ss_item_sk = cs_ui.cs_item_sk and        c_current_cdemo_sk = cd2.cd_demo_sk AND        c_current_hdemo_sk = hd2.hd_demo_sk AND        c_current_addr_sk = ad2.ca_address_sk and        c_first_sales_date_sk = d2.d_date_sk and        c_first_shipto_date_sk = d3.d_date_sk and        ss_promo_sk = p_promo_sk and        hd1.hd_income_band_sk = ib1.ib_income_band_sk and        hd2.hd_income_band_sk = ib2.ib_income_band_sk and        cd1.cd_marital_status <> cd2.cd_marital_status and        i_color in ('purple','burlywood','indian','spring','floral','medium') and        i_current_price between 64 and 64 + 10 and        i_current_price between 64 + 1 and 64 + 15        group by i_product_name        ,i_item_sk        ,s_store_name        ,s_zip        ,ad1.ca_street_number        ,ad1.ca_street_name        ,ad1.ca_city        ,ad1.ca_zip        ,ad2.ca_street_number        ,ad2.ca_street_name        ,ad2.ca_city        ,ad2.ca_zip        ,d1.d_year        ,d2.d_year        ,d3.d_year        )        select cs1.product_name        ,cs1.store_name        ,cs1.store_zip        ,cs1.b_street_number        ,cs1.b_street_name        ,cs1.b_city        ,cs1.b_zip        ,cs1.c_street_number        ,cs1.c_street_name        ,cs1.c_city        ,cs1.c_zip        ,cs1.syear        ,cs1.cnt        ,cs1.s1 as s11        ,cs1.s2 as s21        ,cs1.s3 as s31        ,cs2.s1 as s12        ,cs2.s2 as s22        ,cs2.s3 as s32        ,cs2.syear        ,cs2.cnt        from cross_sales cs1,cross_sales cs2        where cs1.item_sk=cs2.item_sk and        cs1.syear = 1999 and        cs2.syear = 1999 + 1 and        cs2.cnt <= cs1.cnt and        cs1.store_name = cs2.store_name and        cs1.store_zip = cs2.store_zip        order by cs1.product_name        ,cs1.store_name        ,cs2.cnt        ,cs1.s1        ,cs2.s1;
复制代码


这段 SQL 语句先是通过公共表达式定义了两个具有特定筛选和聚合逻辑的数据子集(cs_ui 和 cross_sales),然后在主查询中基于 cross_sales 表进行自连接并再次筛选,最终选取特定字段并按照一定顺序排序,目的可能是对比分析同商品在相邻年份(1999 年和 2000 年)、同店铺下的各项业务指标数据变化情况以及它们之间的相互关系,帮助进行业务决策或者数据分析等相关工作。


这是一段复杂查询,但是可以正常查询出结果:


这里我们给 sql 增加SETTINGS max_memory_usage=40000000000;限制,模拟内存问题,当我们限制到40000000000的 70%,即 28000000000 后查询出错:


继续配置 bsp 模式后:


查询出正确结果:


总结

ByConity 增加的 BSP(Bulk Synchronous Parallel)模式是一个重要的功能更新,非常好用:


  • 通过 distributed_max_parallel_size 参数控制分布式查询中表扫描的并行度。通过调整这个参数,用户可以根据集群的资源情况和查询的需求来优化查询性能。

  • max_memory_usage 参数用于限制单个查询在执行过程中可以使用的最大内存量。通过设置这个参数,可以防止单个查询占用过多内存资源,影响其他查询的执行和系统的稳定性。


通过合理调整 distributed_max_parallel_size 和 max_memory_usage 的值,用户可以在保证查询性能的同时,避免资源过度消耗和查询失败的风险。ByConity 非常推荐,原生数据仓库搭建,ByConity 你值得拥有。

发布于: 刚刚阅读数: 2
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017-10-17 加入

Android、音视频、AI相关领域从业者。 欢迎加我微信wodekouwei拉您进InfoQ音视频沟通群 邮箱:qingkouwei@gmail.com

评论

发布
暂无评论
开源云原生数据仓库 ByConity实测,开启开启数据仓库的新篇章_OLAP_轻口味_InfoQ写作社区