目前,远程磁盘 IO 和 CPU 可按上述方法进行调度。有关灵活的内存限制,请参见 内存 overcommit
磁盘配置
storage_configuration:
要为特定磁盘启用 I/O 调度,必须在存储配置中指定 read_resource 和/或 write_resource。这样可以告诉 ClickHouse,对于给定磁盘的每个读写请求应使用哪个资源。读取资源和写入资源可以指向同一个资源名称,这对于本地 SSD 或 HDD 很有用。多个不同的磁盘也可以指向同一个资源,这对于远程磁盘很有用:例如,如果你希望在 “production” 和 “development” 工作负载 之间公平分配网络带宽。
示例:
工作负载标记
workload 为查询打标,以区分不同的工作负载。如果未设置 workload,则使用值 “default”。请注意,也可以通过 profile 指定其他值。如果你希望某个用户的所有查询都使用固定的 workload 设置值进行标记,可以使用设置约束将 workload 固定为该值。
也可以为后台活动分配 workload 设置。合并和变更分别使用 merge_workload 和 mutation_workload 服务器设置。这些值还可以通过 merge_workload 和 mutation_workload MergeTree 设置在特定表级别进行覆盖
下面来看一个具有两种不同工作负载的系统示例:“production” 和 “development”。
资源调度层级
inflight_limit(约束) - 如果正在进行的并发请求数超过max_requests``,或其总成本超过max_cost`,则会阻塞;必须且只能有一个子节点。bandwidth_limit(约束) - 如果当前带宽超过max_speed(0 表示不限制) ,或突发流量超过max_burst(默认等于max_speed) ,则会阻塞;必须且只能有一个子节点。fair(策略) - 按照 max-min 公平性从其某个子节点中选择下一个要处理的请求;子节点可指定weight(默认为 1) 。priority(策略) - 按照静态优先级从其某个子节点中选择下一个要处理的请求 (值越小,优先级越高) ;子节点可指定priority(默认为 0) 。fifo(队列) - 层级结构中的叶子节点,可保存超出资源容量的请求。
inflight_limit。请注意,max_requests 或 max_cost 过低可能导致资源无法充分利用,而数值过高则可能导致调度器内部队列为空,进而使子树中的策略失效 (出现不公平或忽略优先级的情况) 。另一方面,如果你希望防止资源利用率过高,则应使用 bandwidth_limit。当 duration 秒内消耗的资源量超过 max_burst + max_speed * duration 字节时,它会进行限速。在同一资源上使用两个 bandwidth_limit 节点,可以分别限制短时间间隔内的峰值带宽和较长时间内的平均带宽。
下面的示例展示了如何定义图中所示的 IO 调度层级:
工作负载分类器
workload 映射到特定资源应使用的叶子队列。目前,工作负载分类较为简单,仅支持静态映射。
示例:
工作负载层级结构
CREATE RESOURCE 创建的资源都具有相同的层级结构,但某些细节可能有所不同。每个通过 CREATE WORKLOAD 创建的工作负载,都会为每种资源保留几个自动创建的调度节点。子工作负载可以在另一个父工作负载下创建。下面的示例定义了一个与上述 XML 配置完全相同的层级结构:
SETTINGS workload = 'name' 中使用。
如需自定义工作负载,可以使用以下设置:
priority- 同级工作负载按静态优先级值提供服务 (值越小,优先级越高) 。weight- 具有相同静态优先级的同级工作负载按权重共享资源。max_io_requests- 此工作负载中并发 IO 请求数量的上限。max_bytes_inflight- 此工作负载中并发请求的在途总字节数上限。max_bytes_per_second- 此工作负载的字节读取或写入速率上限。max_burst_bytes- 该工作负载在不被限流的情况下可处理的最大字节数 (对每种资源分别独立计算) 。max_concurrent_threads- 此工作负载中查询可使用的线程数量上限。max_concurrent_threads_ratio_to_cores- 与max_concurrent_threads相同,但会按可用 CPU 核心数进行归一化。max_cpus- 为此工作负载中的查询提供服务时可使用的 CPU 核心数量上限。max_cpu_share- 与max_cpus相同,但会按可用 CPU 核心数进行归一化。max_burst_cpu_seconds- 该工作负载在不因max_cpus而被限流的情况下可消耗的最大 CPU 秒数。
max_bytes_per_second = 10485760,则对每个读取和写入资源都会分别施加 10 MB/s 的带宽限制。如果需要对读取和写入施加统一限制,请考虑让 READ 和 WRITE 访问使用同一资源。
无法为不同资源指定不同的工作负载层级结构。但可以为特定资源指定不同的工作负载设置值:
CREATE OR REPLACE WORKLOAD 查询。
工作负载设置会转换为一组合适的调度节点。有关底层细节,请参阅调度节点类型和选项的说明。
CPU 调度
- Master thread — 开始执行查询或 merge、变更等后台活动的第一个线程。
- Worker thread — 可由 master 派生出的额外线程,用于执行 CPU 密集型任务。
max_threads 的值较高时,大量工作线程很容易独占 CPU 资源。这样一来,新到达的查询就只能阻塞,等待为其 master 线程分配一个 CPU 插槽后才能开始执行。为避免这种情况,可以使用以下配置:
cpu_slot_preemption 服务器设置启用抢占。如果启用,每个线程都会定期续用其 CPU 插槽 (根据 cpu_slot_quantum_ns 服务器设置) 。如果 CPU 过载,这种续用可能会阻塞执行。当执行被阻塞较长时间时 (参见 cpu_slot_preemption_timeout_ms 服务器设置) ,查询会缩容,并且并发运行的线程数会动态减少。请注意,工作负载之间的 CPU 时间公平性可以得到保证,但在同一工作负载内部,不同查询之间的公平性在某些边缘情况下可能无法保证。
声明 CPU 资源后,
concurrent_threads_soft_limit_num 和 concurrent_threads_soft_limit_ratio_to_cores 设置将不再生效。此时,系统会改用工作负载设置 max_concurrent_threads 来限制分配给特定工作负载的 CPU 数量。若要实现之前的行为,请仅创建 WORKER THREAD 资源,将工作负载 all 的 max_concurrent_threads 设置为与 concurrent_threads_soft_limit_num 相同的值,并使用 workload = "all" 查询设置。此配置等同于将 concurrent_threads_scheduler 设置为 “fair_round_robin”。线程与 CPU
- 线程数限制:
max_concurrent_threads和max_concurrent_threads_ratio_to_cores - CPU 限流:
max_cpus、max_cpu_share和max_burst_cpu_seconds
max_threads 所指定的上限。第二种方式则使用令牌桶算法对工作负载的 CPU 消耗进行限流。它不会直接影响线程数,而是限制该工作负载中所有线程的 CPU 总消耗。
使用 max_cpus 和 max_burst_cpu_seconds 的令牌桶限流含义如下:在任意长度为 delta 秒的时间间隔内,工作负载中所有查询的 CPU 总消耗都不能超过 max_cpus * delta + max_burst_cpu_seconds 个 CPU 秒。从长期来看,它将平均消耗限制在 max_cpus,但短期内可以超过这一限制。例如,给定 max_burst_cpu_seconds = 60 和 max_cpus=0.001,则可以在不被限流的情况下运行 1 个线程 60 秒、2 个线程 30 秒,或 60 个线程 1 秒。max_burst_cpu_seconds 的默认值为 1 秒。在并发线程很多时,较小的值可能会导致允许的 max_cpus 核心无法得到充分利用。
线程在持有 CPU 插槽时,可能处于以下三种主要状态之一:
- Running: 实际占用 CPU 资源。处于此状态的时间会计入 CPU 限流。
- Ready: 等待 CPU 可用。不计入 CPU 限流。
- Blocked: 正在执行 IO 操作或其他阻塞式系统调用 (例如等待互斥锁) 。不计入 CPU 限流。
max_cpu_share,其总 CPU 资源上限为 70%。而 ingestion 至少有 0.8 * 0.25 = 20% 的保障份额,同时没有上限。
如果你希望最大化 ClickHouse server 的 CPU 利用率,请避免对根工作负载
all 使用 max_cpus 和 max_cpu_share。相反,应将 max_concurrent_threads 设置得更高。例如,在一个具有 8 个 CPU 的系统上,可设置 max_concurrent_threads = 16。这样一来,8 个线程可以运行 CPU 任务,同时另外 8 个线程可以处理 I/O 操作。额外的线程会制造 CPU 压力,从而确保调度规则得以执行。相比之下,设置 max_cpus = 8 永远不会产生 CPU 压力,因为 server 无法超过可用的 8 个 CPU。查询槽位调度
max_concurrent_queries 用于限制给定工作负载可同时运行的并发查询数。它相当于查询设置 max_concurrent_queries_for_all_users 和服务器设置 max_concurrent_queries。异步 insert 查询以及某些特定查询 (如 KILL) 不计入此限制。
工作负载设置 max_queries_per_second 和 max_burst_queries 通过令牌桶限流器限制该工作负载的查询数量。它保证在任意时间间隔 T 内,新开始执行的查询数不会超过 max_queries_per_second * T + max_burst_queries。
工作负载设置 max_waiting_queries 用于限制该工作负载的等待中查询数。达到该限制时,服务器会返回错误 SERVER_OVERLOADED。
被阻塞的查询会无限期等待,并且在所有约束条件都满足之前,不会出现在
SHOW PROCESSLIST 中。工作负载和资源存储
CREATE WORKLOAD 和 CREATE RESOURCE 查询的形式持久化存储在磁盘上的 workload_path 或 ZooKeeper 中的 workload_zookeeper_path。为确保节点之间的一致性,建议使用 ZooKeeper 存储。或者,也可以将 ON CLUSTER 子句与磁盘存储配合使用。
基于配置的工作负载和资源
配置格式
CREATE WORKLOAD 和 CREATE RESOURCE 语句相同的 SQL 语法。所有查询都必须是有效的。
使用建议
- 在配置中定义根工作负载和网络 IO 资源,以设置基础设施限制
- 设置
throw_on_unknown_workload以强制执行这些限制 - 创建
CREATE WORKLOAD default IN all,以自动将限制应用于所有查询 (因为workload查询设置的默认值是 ‘default’) - 允许用户在已配置的层级结构内创建其他工作负载
严格资源访问
throw_on_unknown_workload。如果将其设置为 true,则每个查询都必须使用有效的 workload 查询设置,否则会抛出 RESOURCE_ACCESS_DENIED 异常。如果将其设置为 false,这类查询就不会使用资源调度器,也就是说,它将获得对任意 RESOURCE 的无限制访问。查询设置 use_concurrency_control = 0 允许查询绕过 CPU 调度器,并获得对 CPU 的无限制访问。要强制启用 CPU 调度,请创建一个设置约束,使 use_concurrency_control 保持为只读常量值。
除非已执行
CREATE WORKLOAD default,否则不要将 throw_on_unknown_workload 设置为 true。如果在启动期间执行了未显式设置 workload 的查询,可能会导致服务器启动问题。另请参阅
- system.scheduler
- system.workloads
- system.resources
- merge_workload MergeTree 设置
- merge_workload 全局服务器设置
- mutation_workload MergeTree 设置
- mutation_workload 全局服务器设置
- workload_path 全局服务器设置
- workload_zookeeper_path 全局服务器设置
- cpu_slot_preemption 全局服务器设置
- cpu_slot_quantum_ns 全局服务器设置
- cpu_slot_preemption_timeout_ms 全局服务器设置