ClickHouse 的 Query 会尽可能用尽集群的 CPU、内存资源,在集群并发较高时会导致执行失败,影响用户体验。
集群可以通过设置资源组(Resource Group)的方式限制查询对资源的消耗,实现多租户之间的资源隔离与合理利用。避免少数的大查询耗尽系统资源,进而影响系统稳定性。
通过将集群划分为若干个资源组 (Resource Group),系统在执行相应资源组的查询任务时,会按照为该资源组划分的资源配额(目前支持内存,后续支持 CPU)分配查询资源。
资源组可以支持以下条件的限制:
soft_max_memory_usage
: 单台机器使用内存上限;
min_query_memory_usage
: 默认单个查询语句使用内存下限;
max_concurrent_queries:
最大并发查询语句数;
在配置文件中,需要添加 <resource_group>
标签,其下每一个子标签代表配置一个资源组。
示例如下:
<resource_groups> <resource_group1> <name>group1</name> <soft_max_memory_usage>12000000</soft_max_memory_usage> <max_concurrent_queries>100</max_concurrent_queries> <max_queued>100</max_queued> <priority>1</priority> </resource_group1> </resource_groups>
配置参数:
name
: 资源组名称;
soft_max_memory_usage
: 单台机器使用内存上限。默认 0,即不限制;
min_query_memory_usage
: 默认单个查询使用内存下限,单位 ?。默认 512 MB;
max_concurrent_queries
: 最大并发查询数,默认 0,不限制;
max_queued
: 等待队列大小,默认 0,不限制;
max_queued_waiting_ms
: 等待队列最长等待时间,默认 5s;
priority
: 优先级,在有多级资源组时使用(见例 3)。
parent_resource_group
:父资源组(见例 3)
通过<case>
标签,可将查询分配到不同资源组,示例如下(即 select 类型查询分配到资源组 adhoc,insert类型查询分配到资源组 etl):
<resource_groups> <resource_group1> <name>adhoc</name> ... </resource_group1> <resource_group2> <name>etl</name> ... </resource_group2> <case1> <query_type>select</query_type> <resource_group>adhoc</resource_group> </case1> <case2> <query_type>insert</query_type> <resource_group>etl</resource_group> </case2> </resource_groups>
查询可以通过以下方式分类:
user
: 用户名;
query_id
: 查询语句 id 的正则表达式,即特定格式的 query_id 可以被单独区分;
query_type
: 查询语句类型,共支持以下类型:
SELECT
: select 类型
DDL
: create / drop / alter column
DATA
: insert / delete / alter partition
OTHER
: 其他
资源组可为多层结构。通过配置<parent_resource_group>
标签,资源组可以配置其父资源组。
多层资源组的判断逻辑如下:
判断资源组是否可以继续处理查询时,需同时判断其父资源组是否可以,如果不可以,则也无法进入执行状态。
子资源组的参数可以大于父资源组,即允许子资源组之间共享和抢占资源。
多层资源组的等待队列(waiting queue)只有一个,其仅维护在根资源组(root resource group,没有父资源组的资源组,如下例子中的all
资源组)上。该队列根据子资源组的优先级确定进入队列的顺序。在以下示例讲解中,会详细介绍优先级的概念。
示例如下:
<resource_groups> <resource_group1> <name>all</name> <soft_max_memory_usage>12000000</soft_max_memory_usage> <max_concurrent_queries>100</max_concurrent_queries> <max_queued>100</max_queued> <priority>1</priority> </resource_group1> <resource_group2> <name>adhoc</name> <soft_max_memory_usage>10000000</soft_max_memory_usage> <min_query_memory_usage>200000</min_query_memory_usage> <max_concurrent_queries>100</max_concurrent_queries> <max_queued>100</max_queued> <max_queued_waiting_ms>10000</max_queued_waiting_ms> <priority>1</priority> <parent_resource_group>all</parent_resource_group> </resource_group2> <resource_group3> <name>etl</name> <soft_max_memory_usage>6000000</soft_max_memory_usage> <max_concurrent_queries>10</max_concurrent_queries> <max_queued>10</max_queued> <priority>0</priority> <parent_resource_group>all</parent_resource_group> </resource_group3> <case1> <query_type>select</query_type> <resource_group>adhoc</resource_group> </case1> <case2> <query_type>insert</query_type> <resource_group>etl</resource_group> </case2> </resource_groups>
在上述示例中,资源组 etl 和 资源组 adhoc 均是资源组 all 的子资源组。
在 select 类查询被执行时,同时需要符合 adhoc 资源组 和 all 资源组的参数条件。
etl 资源组优先级为 0,adhoc 资源组优先级为 1,则匹配 etl 资源组的查询(insert 类查询)始终更靠前。