本机真实在空闲状态下建立hot_threads的0%基线,随后用painless脚本制造CPU压力时先撞上Painless自带的语句数上限保护机制,降到10万次循环后让6个查询并发执行,在负载期间抓到的hot_threads真实捕获到3个search线程跑满102%CPU,调用栈精确指向PainlessScript执行和ScriptFieldsPhase这两处,验证了hot_threads能把CPU飙高的排查范围直接收窄到具体代码行。
代码GET /_nodes/hot_threads?threads=3
0.0% [cpu=0.0%, other=0.0%] cpu usage by thread 'elasticsearch[node-1][inference_utility][T#1]' 0.0% [cpu=0.0%, other=0.0%] cpu usage by thread 'ticker-schedule-trigger-engine'
这是一份干净的基线:集群没有任何真实的查询或写入压力时,hot_threads这个接口真实查到的busiest线程CPU占用都接近0,后面制造真实CPU压力之后的对比才有意义。
代码GET /ht_test/_search {"script_fields":{"heavy":{"script":{ "lang":"painless", "source":"double x=0; for(int i=0;i<2000000;i++){x+=Math.sqrt(i)*Math.sin(i);} return x;" }}}}
→ 400 painless_error: "The maximum number of statements that can be executed in a loop has been reached."
代码GET /ht_test/_search {"size":5000,"script_fields":{"heavy":{"script":{ "lang":"painless", "source":"double x=0; for(int i=0;i<100000;i++){x+=Math.sqrt(i)*Math.sin(i);} return x;" }}}}
真实耗时: 3.042秒, took字段: 3016(ms), 返回hits: 5000
代码真实并发发起6个同样的查询 真实在它们还在跑的时候立刻: GET /_nodes/hot_threads?threads=3
102.3% [cpu=102.3%, other=0.0%] cpu usage by thread 'elasticsearch[node-1][search][T#4]' 101.9% [cpu=101.9%, other=0.0%] cpu usage by thread 'elasticsearch[node-1][search][T#1]' 101.7% [cpu=101.7%, other=0.0%] cpu usage by thread 'elasticsearch[node-1][search][T#15]' 真实的调用栈(节选): → org.elasticsearch.painless.PainlessScript$Script.execute(...) → org.elasticsearch.search.fetch.subphase.ScriptFieldsPhase$1.process(...) → org.elasticsearch.search.fetch.FetchPhase.execute(...)
代码DELETE /ht_test
0.0% [cpu=0.0%, other=0.0%] cpu usage by thread 'elasticsearch[node-1][inference_utility][T#1]'
每一步都是本文上面在本机真实的Elasticsearch 8.15.0上跑出来的原始结果。
_nodes/hot_threads API 与 painless 脚本压力测试