暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

StarRocks 实战指南:100+ 大型企业背后的最佳实践经验

StarRocks 2024-04-19
708


PART/ 01 部署


1

容量规划

【建议】参考 StarRocks 集群配置推荐[1] 做容量规划

2

基础环境配置

【必须】参考检查环境配置 | StarRocks[2],尤其关注 swap 关闭、overcommit 设置为1、ulimit 配置合理

3

机器配置

  • FE 节点
    • 【建议】 8C32GB
    • 【必须】数据盘>=200GB,建议 SSD

  • BE 节点

    • 【建议】CPU:内存比,1:4,生产最小配置必须是 8C32GB+

    • 【建议】单节点磁盘容量建议 10TB,数据盘建议最大单盘 2TB,建议 SSD 或者 NVME(如果是 HDD,建议吞吐>150MB/s,IOPS>500)

    • 【建议】集群中节点同构(机器规格一样,避免木桶效应)

4

部署方案

  • 【必须】生产环境必须最小集群规模 3FE+3BE(建议 FE 和 BE 独立部署),如果混合部署,必须配置 be.conf 中的 mem_limit 为减去其他服务后剩余内存量,例如机器内存 40G,上面已经部署了 FE,理论上限会用 8G,那么配置下mem_limit=34G (40-8-2),2G 作为系统预留
  • 【必须】生产必须 FE 高可用部署,1 Leader + 2 Follower,如果需要提高读并发,可以扩容 Observer 节点
  • 【必须】生产必须使用负载均衡器连接集群进行读写,一般常用 Nginx、Haproxy、F5 等


PART/ 02 建模


1

建表规范

  • 仅支持 UTF8 编码
  • 不支持修改表中的列名(即将支持)
  • VARCHAR 最大长度 1048576
  • KEY 列不能使用 FLOAT、DOUBLE 类型
  • 数据目录名、数据库名、表名、视图名、用户名、角色名大小写敏感,列名和分区名大小写不敏感
  • 主键模型中,主键长度不超过 128 字节

2

模型选择

  • 如果想要保留明细,建议使用明细模型
  • 如果有明确主键,主键非空,写少读多,非主键列要利用索引,建议使用主键模型
  • 如果有明确主键,主键可能为空,写多读少,建议使用更新模型
  • 如果只想保留聚合数据,建议使用聚合模型

3

排序列和前缀索引选择

DUPLICATE KEY、AGGREGATE KEY、UNIQUE KEY 中指定的列,StarRocks 3.0 以前版本,主键模型中排序列通过 PRIMARY KEY 指定,StarRocks 3.0 以后版本,主键模型中排序列通过 ORDER BY 指定。

前缀索引是在排序列基础上引入的稀疏索引,能进一步提升查询效率。为了实现最佳性能,前缀索引将全部加载在内存中。在使用前缀索引时需注意:
  • 经常作为查询条件的列,建议选为排序列,例如经常用 user_id 过滤,where user_id=234,可以把 user_id 放在第一列
  • 排序列建议选择 3-5 列,过多会增大排序开销,降低导入效率
  • 前缀索引不超过 36 字节,不能超过 3 列,遇到 VARCHAR 会截断,前缀索引中不能包含 FLOAT 或 DOUBLE 类型的列

因此可以结合实际业务查询场景,在确定 Key 列以及字段顺序时,要充分考虑前缀索引带来的优势。尽可能将经常需要查询的 Key 列字段,放置在前面,字段数据类型尽量选择 date 日期类型或者 int 等整数类型。

举例:
    CREATE TABLE site_access
    (
    site_id BIGINT DEFAULT '10',
    city_code INT,
    site_name VARCHAR(50),
    pv BIGINT DEFAULT '0'
    )
    DUPLICATE KEY(site_id,city_code,site_name)
    DISTRIBUTED BY HASH(site_id);

    在 site_access 表中,前缀索引为 site_id( 8 Bytes ) + city_code( 4 Bytes ) + site_name(前 24 Bytes)

    • 如果查询条件只包含 site_id
       和 city_code
       两列,如下所示,则可以大幅减少查询过程中需要扫描的数据行:


      select sum(pv) from site_access where site_id = 123 and city_code = 2;
      • 如果查询条件只包含 site_id
        一列,如下所示,可以定位到只包含 site_id

      的数据行:

        select sum(pv) from site_access where site_id = 123;
        • 如果查询条件只包含 city_code
           一列,如下所示,则需要扫描所有数据行,排序效果大打折扣:

          select sum(pv) from site_access where city_code = 2;
          • 如果 site_id 和 city_code 在联合查询和单独 city_code 的查询占比不相上下,可以考虑创建同步物化视图调整列顺序来达到查询性能提升。在这种情况下,我们将物化视图中的 city_code 列放置在第一列。


            create materialized view site_access_city_code_mv as
            select
            city_code,
            site_id,
            site_name,
            pv
            from
            site_access;

            Bad case


              CREATE TABLE site_access_bad
              (
              site_name VARCHAR(20),
              site_id BIGINT DEFAULT '10',
              city_code INT,
              pv BIGINT DEFAULT '0'
              )
              PRIMARY KEY(site_id)
              DISTRIBUTED BY HASH(site_id)
              ORDER BY(site_id,city_code);
              在 site_access_bad 表中,前缀索引只有 site_name

              3

              分区选择

              • 【建议】值不会变化的时间列经常用于 WHERE 过滤,使用该列创建分区
              • 【建议】有数据淘汰需求的场景建议选择动态分区
              • 【必须】数据更新有明显的冷热特征的,必须创建分区,例如经常更新最近一周的数据,可以按天分区
              • 【必须】单个分区数据量必须不超过 100GB
              • 【必须】超过 50G 或者 5KW 的表建议创建分区
              • 【建议】按需创建分区,不要提前创建大量空分区,避免元数据太多占用 FE 的内存
              • 当前支持时间类型(Range 分区、表达式分区)、字符串(List 分区)、数字(Range 分区、List 分区)
              • 默认最大支持 1024 个分区,可以通过参数调整,不过一般情况下不需要调整


              4

              分桶选择

              1. 生产必须使用 3 副本
              2. 分桶个数判断
              • 【必须】单个桶按照 1GB 预估,原始数据按照 10GB(导入 StarRocks 后,压缩比 7:1~10:1)预估。
              • 当按照以上策略估算出来的分桶个数小于 BE 个数的时候,最终分桶个数以 BE 个数为准,例如 6 个 BE 节点,按照 1GB 每个桶预估分桶个数为 1,最终分桶个数取 6
              • 【必须】非分区表不要使用动态分桶,按照实际数据量估算分桶个数
              • 【必须】如果分区表的各个分区的数据差异很大,建议不要使用动态分桶策略
              3. 分桶裁剪和数据倾斜如何抉择?
              • 【建议】如果分桶列是 WHERE 中经常用到的列,且分桶列的重复度比较低(例如用户 ID、事物 ID 等),则可以利用该列作为分桶列

              • 【建议】当查询条件包含 city_id 和 site_id 时,若 city_id 的取值仅有几十个,简单地只使用 city_id 作为分桶可能导致某些桶数据量过大,引发数据倾斜问题。在这种情况下,可以考虑将 city_id 和 site_id 联合作为分桶字段。不过这样做的缺点是当查询条件中只包含 city_id 时,无法利用分桶进行数据裁剪。

              • 【建议】如果没有合适的字段作为分桶字段打散数据,可以利用 Random 分桶,不过这样的话没办法利用分桶裁剪的属性

              • 【必须】2 个或多个超过 KW 行以上的表 Join,建议使用 Colocate,具体参考 Colocate Join | StarRocks[3]

              5

              字段类型

              【建议】不要使用 null 属性

              【必须】确保时间类型和数字类型的列选择正确的数据类型。若使用不正确的数据类型,计算开销会大大增加。例如,时间类型的数据如 "2024-01-01 00:00:00" 不应该使用 VARCHAR 存储,这样做将无法利用 StarRocks 内部的 Zonemap 索引,也无法加速过滤操作。

              6

              索引选择

              Bitmap 索引

              • 适合基数在 10000-100000 左右的列
              • 适合等值条件 (=) 查询或 [NOT] IN 范围查询的列
              • 不支持为 FLOAT、DOUBLE、BOOLEAN 和 DECIMAL 类型的列创建 Bitmap 索引
              • 城市、性别这些基数在 255 以下的列不需要创建 Bitmap 索引,因为 StarRocks 内部有低基数字典,会针对这些 case 自动创建低基数字典用于加速
              • 明细模型和主键模型,所有列可以创建 Bitmap 索引;聚合模型和更新模型,只有 Key 列支持创建 Bitmap 索引

              Bloom filter 索引
              • 适合基数在 100000+ 的列,列的重复度很低

              • 适合 in
                 和 =
                 过滤条件的查询

              • 不支持为 TINYINT、FLOAT、DOUBLE 和 DECIMAL 类型的列创建 Bloom filter 索引

              • 主键模型和明细模型中所有列都可以创建 Bloom filter 索引;聚合模型和更新模型中,只有维度列(即 Key 列)支持创建 Bloom filter 索引

              PART/ 03 导入


              1

              使用建议

              • 【必须】生产禁止使用 insert into values() 导数据
              • 【必须】建议导入批次间隔 5s+,也就是攒批写入,尤其是实时场景
              • 【建议】主键模型更新场景,建议开启索引落盘,磁盘强制 SSD、NVME 或者更高性能的磁盘
              • 【建议】比较多 ETL(insert into select)的场景,建议开启 Spill 落盘功能,避免内存超过限制

              2

              数据生命周期

              • 【建议】使用 truncate 删除数据,不要使用 delete
              • 【必须】完整的 update 语法只能用于 3.0 版本以后的主键模型,禁止高并发 update,建议每次 update 操作需要间隔分钟以上
              • 【必须】如果使用 delete 删除数据,需要带上 where 条件,并且禁止并发执行 delete,例如要删除 id=1,2,3,4,……1000,禁止 delete xxx from tbl1 where id=1 这样的语句执行1000条,建议 delete xxx from tbl1 where id in (1,2,3...,1000)
              • 【必须】drop 操作默认会进入 FE 回收站,并保留 86400 秒(即 1 天),在这段时间内可以 recover 进行恢复,以防误操作。此行为受 catalog_trash_expire_second 参数控制。超过 1 天后,文件会移至 BE 的 trash 目录,默认保留 259200 秒(即 3 天)。

                版本 2.5.17、3.0.9 和 3.1.6 之后版本开始,BE 的默认保留时间已调整为86400 秒( 1 天),这一设置受 trash_file_expire_time_sec 参数影响。如果需要在 drop 操作后迅速释放磁盘空间,可以适当减少 FE 和 BE 的 trash 保留时间。

              PART/ 04 查询


              1

              高并发场景

              • 【建议】尽可能利用分区分桶裁剪,具体参考上文的分区和分桶选择部分
              • 【必须】调大客户的并发限制,可以设置为 1000,默认 100,SET PROPERTY FOR 'jack' 'max_user_connections' = '1000';
              • 【必须】开启 Page Cache、Query Cache

              2

              数据精度

              • 【必须】如果需要精确结果的,强制使用 DECIMA 类型,不要使用 FLOATDOUBLE 类型


              3

              SQL 查询

              • 【必须】避免 select *,建议指定需要查询的列,例如 select col0,col1 from tb1
              • 【必须】避免全表扫描,建议增加过滤的谓词,例如 select col0,col1 from tb1 where id=123,select col0,col1 from tb1 where dt>'2024-01-01'
              • 【必须】为防止大量数据的一次性下载,建议强制采用分页查询。例如,使用以下分页查询语句来限制结果集中的列数和记录数:SELECT col0, col1, col2, ..., col50 FROM tb ORDER BY id LIMIT 0, 50000。这样可以有效地管理和减少单次查询返回的数据量
              • 【必须】分页操作需要加上 order by,要不然是无序的
              • 【建议】避免使用一些不必要的函数或者表达式
              • 谓词中含 cast, 可以移除

                • -- Q1
                  select l_tax
                  from lineitem
                  where cast(l_shipdate as varchar) > substr('1990-01-02 12:30:31',1,10);


                  -- Q2
                  select l_tax
                  from lineitem
                  where l_shipdate > '1990-01-02';
                • 过度使用函数处理表达式

                  • -- Q1 bad case
                    select count(1)
                    from lineitem
                    where l_shipdate >= regexp_extract("TIME:1996-01-02 20:00:00", "(\\d{4}-\\d{2}-\\d{2})", 1);


                    -- Q2
                    select count(1)
                    from lineitem
                    where l_shipdate >= "1996-01-02"

                      -- Q1 bad case
                      select count(1)
                      from lineitem
                      where DATE_FORMAT(l_shipdate,'%Y-%m-%d') >= "1996-01-02"


                      -- Q2 good case
                      select count(1)
                      from lineitem
                      where l_shipdate >= "1996-01-02"
                      • Join
                      • 【必须】关联的字段类型匹配,虽然 StarRocks 已经在内部做了隐式转换来达到最优的性能,不过建议大家使用类型一致的字段 Join,避免使用 FLOATDOUBLE 类型 Join,可能会导致结果不准确
                      • 【必须】关联字段建议不要使用函数或者表达式,例如 join on DATE_FORMAT(tb1.col1,'%Y-%m-%d')=DATE_FORMAT(tb2.col1,'%Y-%m-%d')
                      • 【必须】2 个或多个 KW 行以上的表 Join,推荐 Colocate Join
                      • 【建议】避免笛卡尔积
                        • 查询多个表需要指定连接条件

                          • -- bad case
                            SELECT *
                            FROM table1, table2;


                            -- good case
                            SELECT *
                            FROM table1, table2 ON table1.column1 = table2.column1;

                          • 正确关联子查询

                            在子查询中,确保外部查询和子查询之间的列有明确的关联

                            • -- bad case
                              SELECT *
                              FROM table1
                              WHERE column1 IN (SELECT column2 FROM table2);


                              -- good case
                              SELECT *
                              FROM table1
                              WHERE column1 IN (SELECT column2 FROM table2 WHERE


                        4

                        使用物化视图加速查询

                        • 精确去重
                        以下示例基于一张广告业务相关的明细表 advertiser_view_record
                        ,其中记录了点击日期 click_time
                        、广告代码 advertiser
                        、点击渠道 channel
                         以及点击用户 ID user_id
                          CREATE TABLE advertiser_view_record(
                          click_time DATE,
                          advertiser VARCHAR(10),
                          channel VARCHAR(10),
                          user_id INT) distributed BY hash(click_time);
                          该场景需要频繁使用如下语句查询点击广告的 UV。
                            SELECT advertiser, channel, count(distinct user_id)FROM advertiser_view_record
                            GROUP BY advertiser, channel;
                            如需实现精确去重查询加速,您可以基于该明细表创建一张物化视图,并使用 bitmap_union() 函数预先聚合数据。
                              CREATE MATERIALIZED VIEW advertiser_uv AS SELECT advertiser, channel, bitmap_union(to_bitmap(user_id))FROM advertiser_view_record
                              GROUP BY advertiser, channel;
                              物化视图创建完成后,后续查询语句中的子查询 count(distinct user_id)
                               会被自动改写为 bitmap_union_count (to_bitmap(user_id))
                               以便查询命中物化视图。
                              • 异步物化视图最多支持 3 层嵌套

                              5

                              利用 Cache 加速查询

                              • 【建议】Page Cache:建议开启,可以加速数据扫描场景,如果内存有冗余,可以尽可能调大限制,默认是 mem_limit*20%
                              • 【建议】Query Cache,建议开启,可以加速单表或多表 Join 的聚合场景
                                • 查询中不能包含 rand
                                  random
                                  uuid
                                   和 sleep
                                   等不确定性 (Nondeterminstic) 函数
                              • 【建议】Data Cache,用于存算分离和湖分析场景,建议这两个场景下默认开启

                              PART/ 05 监控



                              1. 【必须】通过审计插件把 fe.audit.log 的数据导入一个表方便进行分析慢查询。

                              通过 Audit Loader 管理 StarRocks 中的审计日志 @ audit_loader[4]

                              1. 【必须】参考 “https://docs.starrocks.io/zh/docs/2.5/administration/Monitor_and_Alert/ ” 部署 prometheus+grafana
                              2. 【建议】利用资源隔离大查询熔断,小查询保底

                                # shortquery_group 资源组用于核心业务重保
                                CREATE RESOURCE GROUP shortquery_group
                                TO
                                (user='rg1_user1', role='rg1_role1', db='db1', query_type in ('select'), source_ip='192.168.x.x/24'),
                                WITH (
                                'type' = 'short_query',
                                'cpu_core_limit' = '10',
                                'mem_limit' = '20%'
                                );


                                # bigquery_group 用于大查询熔断,避免大查询将集群资源打满
                                CREATE RESOURCE GROUP bigquery_group
                                TO
                                (user='rg1_user2', role='rg1_role1', query_type in ('select')),
                                WITH (
                                "type" = 'normal',
                                'cpu_core_limit' = '10',
                                'mem_limit' = '20%',
                                'big_query_cpu_second_limit' = '100',
                                'big_query_scan_rows_limit' = '100000',
                                'big_query_mem_limit' = '1073741824'
                                );
                                1. 大查询定位

                                查看当前 FE 上正在运行的查询 SQL 命令:

                                  show proc '/current_queries'
                                  返回结果包括以下几列:
                                  • QueryId
                                  • ConnectionId
                                  • Database:当前查询的 DB
                                  • User:用户
                                  • ScanBytes:当前已扫描的数据量,单位 Bytes
                                  • ProcessRow:当前已扫描的数据行数
                                  • CPUCostSeconds:当前查询已使用的 CPU 时间,单位秒。此为多个线程累加的 CPU 时间,举个例子,如果有两个线程分别占用 1 秒和 2 秒的 CPU 时间,那么累加起来的 CPU 时间为 3 秒
                                  • MemoryUsageBytes:当前占用的内存。如果查询涉及到多个 BE 节点,此值即为该查询在所有 BE 节点上占用的内存之和
                                  • ExecTime:查询从发起到现在的时长,单位为毫秒
                                    mysql> show proc '/current_queries';
                                    +--------------------------------------+--------------+------------+------+-----------+----------------+----------------+------------------+----------+
                                    | QueryId | ConnectionId | Database | User | ScanBytes | ProcessRows | CPUCostSeconds | MemoryUsageBytes | ExecTime |
                                    +--------------------------------------+--------------+------------+------+-----------+----------------+----------------+------------------+----------+
                                    | 7c56495f-ae8b-11ed-8ebf-00163e00accc | 4 | tpcds_100g | root | 37.88 MB | 1075769 Rows | 11.13 Seconds | 146.70 MB | 3804 |
                                    | 7d543160-ae8b-11ed-8ebf-00163e00accc | 6 | tpcds_100g | root | 13.02 GB | 487873176 Rows | 81.23 Seconds | 6.37 GB | 2090 |
                                    +--------------------------------------+--------------+------------+------+-----------+----------------+----------------+------------------+----------+
                                    2 rows in set (0.01 sec)
                                    查看某个查询在每个 BE 节点上的资源消耗 SQL 命令:
                                      show proc '/current_queries/${query_id}/hosts'
                                      返回结果有多行,每行描述该查询在对应 BE 节点上的执行信息,包括以下几列:
                                      • Host:BE 节点信息
                                      • ScanBytes:已经扫描的数据量,单位 Bytes
                                      • ScanRows:已经扫描的数据行数
                                      • CPUCostSeconds:已使用的 CPU 时间
                                      • MemUsageBytes:当前占用的内存


                                        mysql> show proc '/current_queries/7c56495f-ae8b-11ed-8ebf-00163e00accc/hosts';
                                        +--------------------+-----------+-------------+----------------+---------------+
                                        | Host | ScanBytes | ScanRows | CpuCostSeconds | MemUsageBytes |
                                        +--------------------+-----------+-------------+----------------+---------------+
                                        | 172.26.34.185:8060 | 11.61 MB | 356252 Rows | 52.93 Seconds | 51.14 MB |
                                        | 172.26.34.186:8060 | 14.66 MB | 362646 Rows | 52.89 Seconds | 50.44 MB |
                                        | 172.26.34.187:8060 | 11.60 MB | 356871 Rows | 52.91 Seconds | 48.95 MB |
                                        +--------------------+-----------+-------------+----------------+---------------+
                                        3 rows in set (0.00 sec)

                                        引用链接:
                                        [1]https://forum.mirrorship.cn/t/topic/567 [2]https://docs.starrocks.io/zh/docs/deployment/environment_configurations/
                                        [3]https://docs.starrocks.io/zh/docs/using_starrocks/Colocate_join/ 
                                        [4]https://docs.starrocks.io/zh/docs/administration/management/audit_loader/


                                        关于 StarRocks 

                                        Linux 基金会项目 StarRocks 是新一代极速全场景 MPP 数据库,遵循 Apache 2.0 开源协议。
                                        面世三年来,StarRocks 致力于帮助企业构建极速统一的湖仓分析新范式,是实现数字化转型和降本增效的关键基础设施。目前,全球 360 家以上市值超过 70 亿元人民币的顶尖企业选择用 StarRocks 来构建新一代数据分析能力,这些企业包括腾讯、携程、平安银行、中原银行、中信建投、招商证券、大润发、百草味、顺丰、京东物流、TCL、OPPO 等。StarRocks 也已经和全球云计算领导者亚马逊云、阿里云、腾讯云等达成战略合作关系。

                                        StarRocks 全球开源社区也正飞速成长。目前,StarRocks 的 GitHub star 数已达 7600,吸引了超过 330 位贡献者和数十家国内外行业头部企业参与共建,用户社区也有过万人的规模。凭借其卓越的表现,StarRocks 荣获了全球著名科技媒体 InfoWorld 颁发的 2023 BOSSIE Award 最佳开源软件奖项。

                                        金融:中信建投中原银行 | 申万宏源 | 平安银行 | 中欧财富


                                        互联网:微信|小红书|网易邮箱|滴滴|美团餐饮SaaS | B站|携程 | 同程旅行|36058同城|芒果TV得物 |贝壳|汽车之家欢聚集团腾讯


                                        游戏:腾讯游戏波克城市37手游 | 游族网络


                                        新经济:蔚来汽车理想汽车顺丰京东物流跨越速运 | 大润发华润万家TCL |万物新生 | 百草味 | 多点 DMALL 酷开科技


                                        StarRocks 技术内幕:极速湖仓神器:物化视图存算分离,兼顾降本与增效   |实时更新与极速查询如何兼得Query Cache,一招搞定高并发资源隔离大数据自动管理查询原理浅析易用性全面提升

                                        文章转载自StarRocks,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                        评论