diff --git a/atune.spec b/atune.spec index 7ceab2887dedd07dfe7a98b10102bf2916edf94c..9af1b5d54ef90815bcd913c683cf59fc8a7da840 100755 --- a/atune.spec +++ b/atune.spec @@ -1,9 +1,11 @@ %define __global_requires_exclude_from /usr/libexec +%define python3_local_sitelib /usr/local/lib/python%{python3_version}/site-packages +%define local_bin /usr/local/bin Summary: AI auto tuning system Name: atune Version: 1.2.0 -Release: 7 +Release: 8 License: MulanPSL-2.0 URL: https://gitee.com/openeuler/A-Tune Source: https://gitee.com/openeuler/A-Tune/repository/archive/v%{version}.tar.gz @@ -118,16 +120,15 @@ Copilot Tune: Python helpers, configs and services integrated into A-Tune. %make_install cd "%{_builddir}/A-Tune-v%{version}/copilot-tune" -%{__python3} setup.py install -O1 --root %{buildroot} --prefix %{_prefix} -rm -rf %{buildroot}%{_bindir}/tune-mcpserver %{buildroot}%{_bindir}/tune-openapi %{buildroot}%{_unitdir}/tune-mcpserver.service %{buildroot}%{_unitdir}/tune-openapi.service +%{__python3} setup.py install -O1 --root %{buildroot} --prefix /usr/local %check %pre -n copilot-tune -[ -d /etc/copilot-tune ] || mkdir -p /etc/copilot-tune -[ -d /etc/copilot-tune/knowledge_base ] || mkdir -p /etc/copilot-tune/knowledge_base -[ -d /etc/copilot-tune/config ] || mkdir -p /etc/copilot-tune/config -[ -d /etc/copilot-tune/scripts ] || mkdir -p /etc/copilot-tune/scripts +[ -d /etc/euler-copilot-tune ] || mkdir -p /etc/euler-copilot-tune +[ -d /etc/euler-copilot-tune/knowledge_base ] || mkdir -p /etc/euler-copilot-tune/knowledge_base +[ -d /etc/euler-copilot-tune/config ] || mkdir -p /etc/euler-copilot-tune/config +[ -d /etc/euler-copilot-tune/scripts ] || mkdir -p /etc/euler-copilot-tune/scripts exit 0 %post @@ -157,6 +158,18 @@ exit 0 %postun rest %systemd_postun_with_restart atune-rest.service +%post -n copilot-tune +%systemd_post tune-mcpserver.service +%systemd_post tune-openapi.service + +%preun -n copilot-tune +%systemd_preun tune-mcpserver.service +%systemd_preun tune-openapi.service + +%postun -n copilot-tune +%systemd_postun_with_restart tune-mcpserver.service +%systemd_postun_with_restart tune-openapi.service + %files %license License/LICENSE %defattr(0640,root,root,0750) @@ -222,24 +235,27 @@ exit 0 %files -n copilot-tune %defattr(0640,root,root,0750) -%attr(0550,root,root) %{python3_sitelib}/src -%exclude %{python3_sitelib}/src/__pycache__ -%attr(0550,root,root) %{python3_sitelib}/copilot_tune-*-py*.egg-info/ -%attr(0550,root,root) %{_bindir}/copilot-tune -%attr(0750,root,root) %dir /etc/copilot-tune -%attr(0750,root,root) %dir /etc/copilot-tune/config -%attr(0750,root,root) %dir /etc/copilot-tune/scripts -%attr(0750,root,root) %dir /etc/copilot-tune/knowledge_base -%attr(0640,root,root) /etc/copilot-tune/config/* -%attr(0640,root,root) /etc/copilot-tune/scripts/* -%config(noreplace) %attr(0640,root,root) /etc/copilot-tune/config/.env.yaml -%attr(0640,root,root) /etc/copilot-tune/knowledge_base/* -%exclude %{_bindir}/tune-mcpserver -%exclude %{_bindir}/tune-openapi -%exclude %{_unitdir}/tune-mcpserver.service -%exclude %{_unitdir}/tune-openapi.service +%attr(0550,root,root) %{python3_local_sitelib}/src +%exclude %{python3_local_sitelib}/src/__pycache__ +%attr(0550,root,root) %{python3_local_sitelib}/euler_copilot_tune-*-py*.egg-info/ +%attr(0550,root,root) %{local_bin}/euler-copilot-tune +%attr(0750,root,root) %dir /etc/euler-copilot-tune +%attr(0750,root,root) %dir /etc/euler-copilot-tune/config +%attr(0750,root,root) %dir /etc/euler-copilot-tune/scripts +%attr(0750,root,root) %dir /etc/euler-copilot-tune/knowledge_base +%attr(0640,root,root) /etc/euler-copilot-tune/config/* +%attr(0640,root,root) /etc/euler-copilot-tune/scripts/* +%config(noreplace) %attr(0640,root,root) /etc/euler-copilot-tune/config/.env.yaml +%attr(0640,root,root) /etc/euler-copilot-tune/knowledge_base/* +%attr(0640,root,root) %{_unitdir}/tune-mcpserver.service +%attr(0640,root,root) %{_unitdir}/tune-openapi.service +%attr(0550,root,root) %{local_bin}/tune-mcpserver +%attr(0550,root,root) %{local_bin}/tune-openapi %changelog +* Sat Nov 08 2025 zhuo <1107893276@qq.com> - 1.2.0-8 +- update copilot tune codes + * Fri Sep 12 2025 jinsaihang - 1.2.0-7 - remove tune-mcpserver.service and tune-openapi.service diff --git a/backport-copilot-tune-codes.patch b/backport-copilot-tune-codes.patch index e5b7a079ede4feb8b9c670a48a730697bf91b356..514f5e6a84f036ffbe3d179e5e7326be36752133 100644 --- a/backport-copilot-tune-codes.patch +++ b/backport-copilot-tune-codes.patch @@ -6,15 +6,22 @@ Subject: [PATCH] copilot tune codes --- copilot-tune/FAQ.md | 230 ++ copilot-tune/LICENSE | 127 ++ - copilot-tune/README.md | 199 ++ + copilot-tune/Makefile | 78 + + copilot-tune/README.md | 337 +++ copilot-tune/__init__.py | 0 - copilot-tune/config/.env.yaml | 72 + - copilot-tune/config/app_config.yaml | 81 + + copilot-tune/config/.env.yaml | 134 ++ + copilot-tune/config/app_config.yaml | 93 + + copilot-tune/config/defaults.yaml | 234 ++ copilot-tune/config/knob_rag_config.json | 4 + - copilot-tune/config/openapi.yaml | 69 + + copilot-tune/config/openapi.yaml | 48 + copilot-tune/config/optimize_config.yaml | 4 + copilot-tune/doc/zh/README.md | 199 ++ - copilot-tune/requirements.txt | 20 + + copilot-tune/doc/zh/ceph.md | 365 ++++ + copilot-tune/doc/zh/mysql.md | 228 ++ + copilot-tune/doc/zh/nginx.md | 276 +++ + copilot-tune/doc/zh/oceanbase.md | 128 ++ + copilot-tune/doc/zh/spark.md | 209 ++ + copilot-tune/requirements.txt | 23 + copilot-tune/scripts/ceph/benchmark.sh | 2 + copilot-tune/scripts/ceph/parse_benchmark.sh | 7 + copilot-tune/scripts/flink/benchmark.sh | 4 + @@ -25,6 +32,10 @@ Subject: [PATCH] copilot tune codes copilot-tune/scripts/mysql/parse_benchmark.sh | 6 + copilot-tune/scripts/nginx/benchmark.sh | 5 + copilot-tune/scripts/nginx/parse_benchmark.sh | 12 + + copilot-tune/scripts/oceanbase/benchmark.sh | 2 + + copilot-tune/scripts/oceanbase/get_param.sh | 54 + + .../scripts/oceanbase/parse_benchmark.sh | 6 + + copilot-tune/scripts/oceanbase/set_param.sh | 55 + copilot-tune/scripts/postgresql/benchmark.sh | 4 + .../scripts/postgresql/parse_benchmark.sh | 6 + copilot-tune/scripts/redis/benchmark.sh | 17 + @@ -33,18 +44,26 @@ Subject: [PATCH] copilot tune codes copilot-tune/scripts/spark/parse_benchmark.sh | 14 + copilot-tune/service/tune-mcpserver.service | 15 + copilot-tune/service/tune-openapi.service | 15 + - copilot-tune/setup.py | 68 + - copilot-tune/src/__init__.py | 11 + + copilot-tune/setup.py | 91 + + copilot-tune/src/__init__.py | 13 + + .../01_check_collect_static_metrics.py | 10 + + .../02_check_collect_runtime_metrics.py | 10 + + .../03_check_analyze_performance.py | 9 + + ...04_check_describe_param_background_knob.py | 11 + + .../check_tests/05_check_param_recommender.py | 9 + + .../src/check_tests/06_check_apply_params.py | 7 + + .../src/check_tests/check_tests_utils.py | 116 + copilot-tune/src/config.py | 32 + copilot-tune/src/knowledge_base/README.md | 1 + - .../src/knowledge_base/knob_params/ceph.json | 641 ++++++ - .../src/knowledge_base/knob_params/flink.json | 300 +++ + .../src/knowledge_base/knob_params/ceph.json | 615 ++++++ + .../src/knowledge_base/knob_params/flink.json | 190 ++ .../knowledge_base/knob_params/gaussdb.json | 382 ++++ .../src/knowledge_base/knob_params/mysql.json | 621 ++++++ .../src/knowledge_base/knob_params/nginx.json | 420 ++++ - .../src/knowledge_base/knob_params/pgsql.json | 448 ++++ + .../knowledge_base/knob_params/oceanbase.json | 366 ++++ + .../src/knowledge_base/knob_params/pgsql.json | 467 ++++ .../src/knowledge_base/knob_params/redis.json | 332 +++ - .../src/knowledge_base/knob_params/spark.json | 199 ++ + .../src/knowledge_base/knob_params/spark.json | 192 ++ .../knowledge_base/knob_params/system.json | 1395 ++++++++++++ .../src/knowledge_base/optimize/README.md | 1 + .../optimize/parameter/mysql.jsonl | 22 + @@ -53,48 +72,62 @@ Subject: [PATCH] copilot tune codes .../optimize/strategy/system.jsonl | 302 +++ .../knowledge_base/params/mysql_params.json | 849 ++++++++ .../knowledge_base/params/system_params.json | 1935 +++++++++++++++++ + .../src/knowledge_extractor/api_server.py | 390 ++++ + .../src/knowledge_extractor/code_extractor.py | 90 + + .../config/app_config.yaml | 35 + + .../config/llm_config.yaml | 4 + + .../knowledge_extractor/document_loaders.py | 171 ++ + copilot-tune/src/knowledge_extractor/main.py | 357 +++ + .../src/knowledge_extractor/merge_files.py | 46 + + .../src/knowledge_extractor/output.py | 58 + + .../src/knowledge_extractor/requirements.txt | 11 + + .../src/knowledge_extractor/save_knowledge.py | 53 + + .../src/knowledge_extractor/text_split.py | 161 ++ + .../src/knowledge_extractor/web_crawler.py | 55 + .../src/performance_analyzer/__init__.py | 0 - .../src/performance_analyzer/app_analyzer.py | 77 + + .../src/performance_analyzer/app_analyzer.py | 75 + .../src/performance_analyzer/base_analyzer.py | 38 + - .../src/performance_analyzer/cpu_analyzer.py | 176 ++ - .../src/performance_analyzer/disk_analyzer.py | 109 + - .../performance_analyzer/memory_analyzer.py | 72 + - .../micro_dep_analyzer.py | 95 + - .../performance_analyzer/network_analyzer.py | 103 + - .../performance_analyzer.py | 111 + + .../src/performance_analyzer/cpu_analyzer.py | 174 ++ + .../src/performance_analyzer/disk_analyzer.py | 108 + + .../performance_analyzer/memory_analyzer.py | 71 + + .../micro_dep_analyzer.py | 94 + + .../performance_analyzer/network_analyzer.py | 101 + + .../performance_analyzer.py | 110 + .../src/performance_collector/__init__.py | 0 .../performance_collector/app_collector.py | 45 + + .../application/__init__.py | 0 .../application/ceph_collector.py | 165 ++ - .../application/flink_collector.py | 269 +++ - .../application/gaussdb_collector.py | 206 ++ + .../application/flink_collector.py | 268 +++ + .../application/gaussdb_collector.py | 185 ++ .../application/mysql_collector.py | 145 ++ - .../application/nginx_collector.py | 88 + - .../application/pgsql_collector.py | 128 ++ + .../application/nginx_collector.py | 87 + + .../application/oceanbase_collector.py | 132 ++ + .../application/pgsql_collector.py | 124 ++ .../application/redis_collector.py | 107 + - .../application/spark_collector.py | 146 ++ - .../performance_collector/base_collector.py | 63 + - .../performance_collector/cpu_collector.py | 304 +++ - .../performance_collector/disk_collector.py | 117 + - .../performance_collector/memory_collector.py | 151 ++ + .../application/spark_collector.py | 148 ++ + .../performance_collector/base_collector.py | 66 + + .../performance_collector/cpu_collector.py | 302 +++ + .../performance_collector/disk_collector.py | 115 + + .../performance_collector/memory_collector.py | 147 ++ .../performance_collector/metric_collector.py | 81 + - .../micro_dep_collector.py | 453 ++++ - .../network_collector.py | 139 ++ - .../static_metric_profile_collector.py | 52 + - .../static_profile_collector.py | 246 +++ + .../micro_dep_collector.py | 461 ++++ + .../network_collector.py | 136 ++ + .../static_metric_profile_collector.py | 58 + + .../static_profile_collector.py | 248 +++ .../src/performance_optimizer/__init__.py | 0 - .../performance_optimizer/base_optimizer.py | 185 ++ - .../performance_optimizer/knob_optimizer.py | 132 ++ - .../performance_optimizer/param_knowledge.py | 117 + - .../performance_optimizer/param_optimizer.py | 215 ++ - .../param_recommender.py | 177 ++ - .../performance_optimizer/set_knob_cmd.jsonl | 64 + + .../performance_optimizer/base_optimizer.py | 181 ++ + .../performance_optimizer/knob_optimizer.py | 130 ++ + .../performance_optimizer/param_knowledge.py | 119 + + .../performance_optimizer/param_optimizer.py | 252 +++ + .../param_recommender.py | 220 ++ + .../performance_optimizer/set_knob_cmd.jsonl | 41 + .../strategy_optimizer.py | 221 ++ copilot-tune/src/performance_test/__init__.py | 0 - .../src/performance_test/pressure_test.py | 60 + - copilot-tune/src/start_mcpserver.py | 228 ++ - copilot-tune/src/start_tune.py | 167 ++ - copilot-tune/src/start_workflow.py | 193 ++ - .../src/tests/manager/task_manager.py | 47 + + .../src/performance_test/pressure_test.py | 58 + + copilot-tune/src/start_mcpserver.py | 247 +++ + copilot-tune/src/start_tune.py | 157 ++ + copilot-tune/src/start_workflow.py | 206 ++ + .../src/tests/manager/task_manager.py | 43 + .../src/tests/manager/test_trigger_signal.py | 34 + copilot-tune/src/tests/mock_ssh_client.py | 17 + .../tests/test_perf_optim/param_knowledge.py | 10 + @@ -108,33 +141,42 @@ Subject: [PATCH] copilot tune codes copilot-tune/src/utils/README.md | 169 ++ copilot-tune/src/utils/__init__.py | 0 copilot-tune/src/utils/collector/__init__.py | 0 - .../src/utils/collector/collector_trigger.py | 202 ++ - .../src/utils/collector/metric_collector.py | 229 ++ - copilot-tune/src/utils/common.py | 95 + + .../src/utils/collector/collector_trigger.py | 221 ++ + .../src/utils/collector/metric_collector.py | 224 ++ + copilot-tune/src/utils/common.py | 142 ++ copilot-tune/src/utils/config/__init__.py | 0 - copilot-tune/src/utils/config/app_config.py | 305 +++ + copilot-tune/src/utils/config/app_config.py | 307 +++ .../src/utils/config/global_config.py | 93 + copilot-tune/src/utils/constant.py | 6 + - copilot-tune/src/utils/json_repair.py | 15 + - copilot-tune/src/utils/llm.py | 33 + + copilot-tune/src/utils/json_repair.py | 49 + + copilot-tune/src/utils/llm.py | 59 + copilot-tune/src/utils/manager/__init__.py | 0 - .../src/utils/manager/task_manager.py | 185 ++ + .../src/utils/manager/task_manager.py | 180 ++ copilot-tune/src/utils/metrics.py | 16 + + copilot-tune/src/utils/prompt_instance.py | 3 + + copilot-tune/src/utils/prompt_manager.py | 356 +++ copilot-tune/src/utils/rag/__init__.py | 0 - copilot-tune/src/utils/rag/knob_rag.py | 208 ++ - copilot-tune/src/utils/shell_execute.py | 179 ++ - copilot-tune/src/utils/thread_pool.py | 236 ++ - 120 files changed, 16985 insertions(+) + copilot-tune/src/utils/rag/knob_rag.py | 205 ++ + copilot-tune/src/utils/shell_execute.py | 192 ++ + copilot-tune/src/utils/thread_pool.py | 240 ++ + 155 files changed, 21349 insertions(+) create mode 100644 copilot-tune/FAQ.md create mode 100644 copilot-tune/LICENSE + create mode 100644 copilot-tune/Makefile create mode 100644 copilot-tune/README.md create mode 100644 copilot-tune/__init__.py create mode 100644 copilot-tune/config/.env.yaml create mode 100644 copilot-tune/config/app_config.yaml + create mode 100644 copilot-tune/config/defaults.yaml create mode 100644 copilot-tune/config/knob_rag_config.json create mode 100644 copilot-tune/config/openapi.yaml create mode 100644 copilot-tune/config/optimize_config.yaml create mode 100644 copilot-tune/doc/zh/README.md + create mode 100644 copilot-tune/doc/zh/ceph.md + create mode 100644 copilot-tune/doc/zh/mysql.md + create mode 100644 copilot-tune/doc/zh/nginx.md + create mode 100644 copilot-tune/doc/zh/oceanbase.md + create mode 100644 copilot-tune/doc/zh/spark.md create mode 100644 copilot-tune/requirements.txt create mode 100644 copilot-tune/scripts/ceph/benchmark.sh create mode 100644 copilot-tune/scripts/ceph/parse_benchmark.sh @@ -146,6 +188,10 @@ Subject: [PATCH] copilot tune codes create mode 100644 copilot-tune/scripts/mysql/parse_benchmark.sh create mode 100644 copilot-tune/scripts/nginx/benchmark.sh create mode 100644 copilot-tune/scripts/nginx/parse_benchmark.sh + create mode 100644 copilot-tune/scripts/oceanbase/benchmark.sh + create mode 100644 copilot-tune/scripts/oceanbase/get_param.sh + create mode 100644 copilot-tune/scripts/oceanbase/parse_benchmark.sh + create mode 100644 copilot-tune/scripts/oceanbase/set_param.sh create mode 100644 copilot-tune/scripts/postgresql/benchmark.sh create mode 100644 copilot-tune/scripts/postgresql/parse_benchmark.sh create mode 100644 copilot-tune/scripts/redis/benchmark.sh @@ -156,6 +202,13 @@ Subject: [PATCH] copilot tune codes create mode 100644 copilot-tune/service/tune-openapi.service create mode 100644 copilot-tune/setup.py create mode 100644 copilot-tune/src/__init__.py + create mode 100644 copilot-tune/src/check_tests/01_check_collect_static_metrics.py + create mode 100644 copilot-tune/src/check_tests/02_check_collect_runtime_metrics.py + create mode 100644 copilot-tune/src/check_tests/03_check_analyze_performance.py + create mode 100644 copilot-tune/src/check_tests/04_check_describe_param_background_knob.py + create mode 100644 copilot-tune/src/check_tests/05_check_param_recommender.py + create mode 100644 copilot-tune/src/check_tests/06_check_apply_params.py + create mode 100644 copilot-tune/src/check_tests/check_tests_utils.py create mode 100644 copilot-tune/src/config.py create mode 100644 copilot-tune/src/knowledge_base/README.md create mode 100644 copilot-tune/src/knowledge_base/knob_params/ceph.json @@ -163,6 +216,7 @@ Subject: [PATCH] copilot tune codes create mode 100644 copilot-tune/src/knowledge_base/knob_params/gaussdb.json create mode 100644 copilot-tune/src/knowledge_base/knob_params/mysql.json create mode 100644 copilot-tune/src/knowledge_base/knob_params/nginx.json + create mode 100644 copilot-tune/src/knowledge_base/knob_params/oceanbase.json create mode 100644 copilot-tune/src/knowledge_base/knob_params/pgsql.json create mode 100644 copilot-tune/src/knowledge_base/knob_params/redis.json create mode 100644 copilot-tune/src/knowledge_base/knob_params/spark.json @@ -174,6 +228,18 @@ Subject: [PATCH] copilot tune codes create mode 100644 copilot-tune/src/knowledge_base/optimize/strategy/system.jsonl create mode 100644 copilot-tune/src/knowledge_base/params/mysql_params.json create mode 100644 copilot-tune/src/knowledge_base/params/system_params.json + create mode 100644 copilot-tune/src/knowledge_extractor/api_server.py + create mode 100644 copilot-tune/src/knowledge_extractor/code_extractor.py + create mode 100644 copilot-tune/src/knowledge_extractor/config/app_config.yaml + create mode 100644 copilot-tune/src/knowledge_extractor/config/llm_config.yaml + create mode 100644 copilot-tune/src/knowledge_extractor/document_loaders.py + create mode 100644 copilot-tune/src/knowledge_extractor/main.py + create mode 100644 copilot-tune/src/knowledge_extractor/merge_files.py + create mode 100644 copilot-tune/src/knowledge_extractor/output.py + create mode 100644 copilot-tune/src/knowledge_extractor/requirements.txt + create mode 100644 copilot-tune/src/knowledge_extractor/save_knowledge.py + create mode 100644 copilot-tune/src/knowledge_extractor/text_split.py + create mode 100644 copilot-tune/src/knowledge_extractor/web_crawler.py create mode 100644 copilot-tune/src/performance_analyzer/__init__.py create mode 100644 copilot-tune/src/performance_analyzer/app_analyzer.py create mode 100644 copilot-tune/src/performance_analyzer/base_analyzer.py @@ -185,11 +251,13 @@ Subject: [PATCH] copilot tune codes create mode 100644 copilot-tune/src/performance_analyzer/performance_analyzer.py create mode 100644 copilot-tune/src/performance_collector/__init__.py create mode 100644 copilot-tune/src/performance_collector/app_collector.py + create mode 100644 copilot-tune/src/performance_collector/application/__init__.py create mode 100644 copilot-tune/src/performance_collector/application/ceph_collector.py create mode 100644 copilot-tune/src/performance_collector/application/flink_collector.py create mode 100644 copilot-tune/src/performance_collector/application/gaussdb_collector.py create mode 100644 copilot-tune/src/performance_collector/application/mysql_collector.py create mode 100644 copilot-tune/src/performance_collector/application/nginx_collector.py + create mode 100644 copilot-tune/src/performance_collector/application/oceanbase_collector.py create mode 100644 copilot-tune/src/performance_collector/application/pgsql_collector.py create mode 100644 copilot-tune/src/performance_collector/application/redis_collector.py create mode 100644 copilot-tune/src/performance_collector/application/spark_collector.py @@ -241,6 +309,8 @@ Subject: [PATCH] copilot tune codes create mode 100644 copilot-tune/src/utils/manager/__init__.py create mode 100644 copilot-tune/src/utils/manager/task_manager.py create mode 100644 copilot-tune/src/utils/metrics.py + create mode 100644 copilot-tune/src/utils/prompt_instance.py + create mode 100644 copilot-tune/src/utils/prompt_manager.py create mode 100644 copilot-tune/src/utils/rag/__init__.py create mode 100644 copilot-tune/src/utils/rag/knob_rag.py create mode 100644 copilot-tune/src/utils/shell_execute.py @@ -616,78 +686,324 @@ index 0000000..a589e86 + http://license.coscl.org.cn/MulanPSL2 + THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + See the Mulan PSL v2 for more details. +diff --git a/copilot-tune/Makefile b/copilot-tune/Makefile +new file mode 100644 +index 0000000..193a5c5 +--- /dev/null ++++ b/copilot-tune/Makefile +@@ -0,0 +1,78 @@ ++NAME = euler-copilot-tune ++VERSION = 2.0.0 ++SRCVERSION = $(shell git rev-parse --short HEAD 2>/dev/null) ++COPILOTTUNEVERSION = $(VERSION)$(if $(SRCVERSION),($(SRCVERSION))) ++ ++PYTHON=$(shell which python3) ++PYTHON_SITELIB = $(shell $(PYTHON) -c 'from sysconfig import get_path; print(get_path("purelib"));') ++DESTDIR ?= / ++PREFIX ?= /usr ++BINDIR = $(DESTDIR)$(PREFIX)/bin ++LIBEXECDIR = $(DESTDIR)$(PREFIX)/libexec ++SYSTEMDDIR = $(DESTDIR)$(PREFIX)/lib/systemd/system ++TUNECONFDIR = $(DESTDIR)/etc/euler-copilot-tune ++VENV = venv ++CURDIR = $(shell pwd) ++.PHONY: all clean venv help run ++ ++all: ++ @echo "run make install to install euler copilot tune in system" ++ ++collector-install: ++ @echo "run make install to install euler copilot tune in system" ++ ++install-dirs: ++ mkdir -p $(TUNECONFDIR)/knowledge_base ++ mkdir -p $(TUNECONFDIR)/config ++ mkdir -p $(TUNECONFDIR)/scripts ++ ++install: install-dirs ++ $(PYTHON) setup.py install -O1 --root $(DESTDIR) --prefix $(PREFIX) ++ cp -r $(CURDIR)/config $(TUNECONFDIR) ++ cp -r $(CURDIR)/scripts $(TUNECONFDIR) ++ cp -r $(CURDIR)/src/knowledge_base $(TUNECONFDIR) ++ cp $(CURDIR)/service/*.service $(SYSTEMDDIR) ++ ++clean: ++ rm -rf $(PYTHON_SITELIB)/euler_copilot_tune-*-py*.egg/ ++ rm -rf $(TUNECONFDIR) ++ rm -f $(SYSTEMDDIR)/tune-mcpserver.service ++ rm -f $(SYSTEMDDIR)/tune-openapi.service ++ rm -rf $(CURDIR)/{build,dist,euler_copilot_tune*.egg-info} ++ ++rpmbuild: ++ mkdir -p ~/rpmbuild/{BUILD,RPMS,SOURCES,SPECS,SRPMS} ++ cd .. && tar -zcvf $(NAME)-$(VERSION).tar.gz $(CURDIR) ++ mv ../$(NAME)-$(VERSION).tar.gz ~/rpmbuild/SOURCES ++ rpmbuild -ba $(NAME).spec ++ ++startup: ++ systemctl daemon-reload ++ systemctl restart tune-mcpserver ++ systemctl restart tune-openapi ++ ++run: ++ PYTHONPATH="$(PYTHONPATH):`pwd`" $(VENV)/bin/python src/start_tune.py ++ ++# check: run ++# cd ${CURDIR}/tests && sh run_tests.sh ++ ++authors: ++ git shortlog --summary --numbered --email | grep -v openeuler-ci-bot | sed 's/ AUTHORS ++ ++# Help target ++help: ++ @echo "Usage: make " ++ @echo "" ++ @echo "Targets:" ++ @echo " install install euler-copilot in system" ++ @echo " test run tests" ++ @echo " clean clean euler-copilot build and installed files" ++ @echo " run run euler copilot tune" ++ ++venv: ++ @if [ ! -d "$(VENV)" ]; then $(PYTHON) -m venv $(VENV); fi ++ @echo "Created virtualenv at $(VENV)" ++ $(VENV)/bin/python -m pip install --upgrade pip setuptools wheel ++ $(VENV)/bin/python -m pip install -r requirements.txt ++ @echo "Dependencies installed." +\ No newline at end of file diff --git a/copilot-tune/README.md b/copilot-tune/README.md new file mode 100644 -index 0000000..5823fad +index 0000000..26ce711 --- /dev/null +++ b/copilot-tune/README.md -@@ -0,0 +1,199 @@ -+# EulerCopilot Tune安装使用指南 +@@ -0,0 +1,337 @@ ++# EulerCopilot Tune 安装与使用指南 + -+### 项目简介 -+EulerCopilot Tune通过采集系统、微架构、应用等维度的指标数据,结合大模型和定制化的prompt工程,针对不同应用的可调参数给出可靠的参数推荐,同时根据推荐的参数运行benchmark,与baseline做对比并计算出推荐参数对应用性能的提升值。 ++## 项目简介 ++EulerCopilot Tune 通过采集系统、微架构、应用等维度的指标数据,结合大语言模型与定制化 Prompt 工程,针对不同应用的可调参数给出可靠的参数推荐。同时,根据推荐的参数运行 benchmark 并与 baseline 进行对比,可以计算出推荐参数对应用性能的提升值。 + -+### 软件架构 -+软件架构说明 ++当前已基于云大数存四大场景的多种应用场景完成验证(环境规格为8u32g oe2403sp2): ++- mysql:QPS提升22.37%,验证场景sysbench(10张table表,每张表10000行数据,并发线程数128,随机数模式uniform,oltp_read_wrtie读写混合负载模式) ++- pgsql:QPS提升211.45%,验证场景sysbench(10张table表,每张表100000行数据,并发线程数32,随机数模式uniform,oltp_read_wrtie读写混合负载模式) ++- redis:QPS提升8.80%,验证场景redis-benchmark(单机部署应用,无持久化负载,测试过程中动态生成key-value数据,键值均为随机值,测试命令集set/get/incr/rpop/sadd/hset/lrange_600) ++- spark:time_taken(SQL执行耗时)降低27.46%,验证场景spark-sql(单节点部署,运行TPCDS测试,使用 spark-sql 执行 TPC-DS 查询) ++- flink:band_width提升6.58%,验证场景nexmark(运行模式为streaming流处理模式,持续向flink注入事件,测试场景为q0) ++- ceph:band_width提升7.82%,验证场景rados(一个主节点,三个从节点,运行bench基准测试,持续向存储池中写数据) ++- nginx:RPS提升26.40%,验证场景httpress(单机部署,默认编译参数,worker_processes=auto,并发连接数512,并行线程数7,总请求数2000万次) + -+### 安装教程 ++验证中的应用: ++- oceanbase + -+1. 下载gitee源码,gitee代码仓地址: -+https://gitee.com/openeuler/A-Tune/tree/euler-copilot-tune/ -+(注意:分支指定为euler-copilot-tune) -+2. 安装其他依赖 ++## 安装部署 ++提供三种安装方式,包括源码安装、源码服务方式安装以及RPM包安装。 ++ ++### 方法一:源码安装 ++ ++#### 1. 获取 gitee 源码 ++* 下载地址 https://gitee.com/openeuler/A-Tune/tree/euler-copilot-tune/ ++* 分支指定为 euler-copilot-tune ++```bash ++git clone https://gitee.com/openeuler/A-Tune.git ++cd A-Tune/ ++# 切换到 euler-copilot-tune 分支 ++git checkout euler-copilot-tune ++``` ++ ++#### 2. 安装系统依赖 ++* 安装 python venv 依赖(调优程序运行机器) +```bash -+#1.调优程序运行机器安装python venv依赖 +yum install python3-devel krb5-devel -+#2.目标应用所在机器安装调优依赖并重启sysstat ++``` ++* 安装调优依赖并重启 sysstat(目标应用所在机器) ++```bash +yum install sysstat perf +systemctl start sysstat +``` -+3. 调优程序运行机器安装python依赖: -+```BASH -+#1.创建并加载python venv ++ ++#### 3. 创建虚拟环境 & 安装依赖(调优程序运行机器) ++* 创建并激活虚拟环境 venv ++```bash +python3 -m venv venv +source venv/bin/activate -+ -+#2.安装python依赖包 ++``` ++* 安装python依赖包 ++```bash +pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple +``` + -+### 使用指南 ++#### 4. 修改配置文件 ++* 在项目的 config 文件夹中修改配置文件,具体内容参考[使用指南](#使用指南) + -+1. 准备env yaml,放入项目的config/.env.yaml中,具体格式如下: -+```YAML -+LLM_KEY: "YOUR_LLM_KEY" -+LLM_URL: "YOUR_LLM_URL" -+LLM_MODEL_NAME: "YOUR_LLM_MODEL_NAME" -+LLM_MAX_TOKENS: ++#### 5. 运行 EulerCopilot Tune ++```bash ++export PYTHONPATH="`pwd`:$PYTHONPATH" ++python3 src/start_tune.py ++``` + -+REMOTE_EMBEDDING_ENDPOINT: "YOUR_EMBEDDING_MODEL_URL" -+REMOTE_EMBEDDING_MODEL_NAME: "YOUR_MODEL_NAME" ++### 方法二:源码服务方式安装 ++#### 1. 获取 gitee 源码(同方法一) ++ ++#### 2. 安装系统服务 ++* 进入项目目录执行 ++```bash ++python setup.py install ++``` ++ ++#### 3. 修改配置文件 ++* 在 /etc/euler-copilot-tune 目录修改配置文件,具体内容参考[使用指南](#使用指南) ++ ++#### 4. 启动服务 ++* 开启调优主程序 ++```bash ++euler-copilot-tune ++``` ++ ++* 启动 MCP servers ++```bash ++systemctl start tune-mcpserver ++journalctl -xe -u tune-mcpserver --all -f ++``` ++ ++* 启动 OpenAPI ++```bash ++systemctl start tune-openapi ++journalctl -xe -u tune-openapi --all -f ++``` ++ ++### 方法三:RPM 包方式安装 ++#### 1. 下载 RPM 包 ++* 地址:[https://eulermaker.compass-ci.openeuler.openatom.cn/package/download?osProject=houxu:openEuler-24.03-LTS-SP2:epol&packageName=euler-copilot-tune](https://gitee.com/link?target=https%3A%2F%2Feulermaker.compass-ci.openeuler.openatom.cn%2Fpackage%2Fdownload%3FosProject%3Dhouxu%3AopenEuler-24.03-LTS-SP2%3Aepol%26packageName%3Deuler-copilot-tune) ++ ++#### 2. 设置pip镜像源 ++* 由于RPM 安装过程需要使用 pip 下载资源,为了加快安装速度,推荐设置镜像源 ++```bash ++pip config set global.index-url https://repo.huaweicloud.com/repository/pypi/simple/ ++ ++# 清华大学TUNA镜像源: https://pypi.tuna.tsinghua.edu.cn/simple ++# 阿里云镜像源: http://mirrors.aliyun.com/pypi/simple/ ++# 中国科学技术大学镜像源: https://mirrors.ustc.edu.cn/pypi/simple/ ++# 华为云镜像源: https://repo.huaweicloud.com/repository/pypi/simple/ ++# 腾讯云镜像源:https://mirrors.cloud.tencent.com/pypi/simple/ ++``` ++ ++#### 3. 安装 RPM 包 ++注意:不要在 python 虚拟环境中执行,在系统环境下安装 pip 包 ++* x86 架构 ++```bash ++dnf install euler-copilot-tune-1.0-1.oe2403sp2.x86_64.rpm ++``` ++* ARM 架构 ++```bash ++dnf install euler-copilot-tune-1.0-1.oe2403sp2.aarch64.rpm ++``` ++* 查看详细日志 ++安装过程中会在Running scriptlet: euler-copilot-tune-1.0-1.x86_64 处停留较长时间,此处是在pip安装对应依赖包,可以通过如下命令查看详细日志 ++```bash ++tail -f /pip_install.log ++``` ++* 如果安装完成后出现 pip 包安装失败情况,请单独执行如下命令进行安装: ++```bash ++pip install fastapi numpy openai paramiko pydantic pyyaml scikit-learn tqdm uvicorn requests langchain langchain-openai email-validator httpx tabulate gssapi pandas faiss-cpu pyfiglet mcp ++``` ++ ++#### 4. 修改配置文件 ++* 在 /etc/euler-copilot-tune 目录修改配置文件,具体内容参考[使用指南](#使用指南) ++ ++#### 5. 启动服务 ++* 开启调优主程序 ++```bash ++euler-copilot-tune ++``` ++ ++* 启动 MCP servers ++```bash ++systemctl start tune-mcpserver ++journalctl -xe -u tune-mcpserver --all -f ++``` + ++* 启动 OpenAPI ++```bash ++systemctl start tune-openapi ++journalctl -xe -u tune-openapi --all -f ++``` ++## 使用指南 ++### 配置文件准备 ++#### 1. 修改 .env.yaml 配置文件内容(项目 config 目录下) ++```bash ++vim config/.env.yaml ++``` ++* 具体格式如下: (**调优提升目标**的配置见其中的 feature - slo_goal 字段说明) ++```YAML ++# 根据实际使用的模型服务填写以下字段 ++LLM_KEY: "sk-XXXXXX" # 必填:模型服务的 API 密钥 ++LLM_URL: "https://api.deepseek.com" # 必填:LLM 服务的 API 接口地址,如 "https://api.deepseek.com" ++LLM_MODEL_NAME: "deepseek-chat" # 必填:要调用的模型名,如 deepseek-chat ++LLM_MAX_TOKENS: # 选填:生成文本的最大 token 数,如512或2048 ++ ++REMOTE_EMBEDDING_ENDPOINT: "https://api.embedding.com/v1/embeddings" # 嵌入模型服务地址 ++REMOTE_EMBEDDING_MODEL_NAME: "bge-large-zh" # 嵌入模型名称,如 text-embedding-3-small、bge-large-zh ++ +servers: -+ - ip: "" #应用所在ip -+ host_user: "" #登录机器的usr id -+ password: "" #登录机器的密码 -+ port: #应用所在ip的具体port -+ app: "mysql" #当前支持mysql、nginx、pgsql、spark -+ target_process_name: "mysqld" #调优应用的name ++ - ip: "" # 应用所在ip ++ host_user: "" # 登录机器的usr id ++ password: "" # 登录机器的密码 ++ port: # 应用所在ip的具体port ++ app: "mysql" # 当前支持mysql、nginx、pgsql、spark ++ listening_address: "" # 应用监听的ip(当前仅flink、nginx、spark需要填写) ++ listening_port: "" # 应用监听的端口(当前仅flink、nginx、spark需要填写) ++ target_process_name: "mysqld" # 调优应用的name + business_context: "高并发数据库服务,CPU负载主要集中在用户态处理" #调优应用的描述(用于策略生成) + max_retries: 3 + delay: 1.0 + +feature: -+ - need_restart_application: False #修改参数之后是否需要重启应用使参数生效 -+ need_recover_cluster: False #调优过程中是否需要恢复集群 -+ microDep_collector: True #是否开启微架构指标踩采集 -+ pressure_test_mode: True #是否通过压测模拟负载环境 -+ tune_system_param: False #是否调整系统参数 -+ tune_app_param: True #是否调整应用参数 -+ strategy_optimization: False #是否需要策略推荐 -+ benchmark_timeout: 3600 #benchmark执行超时限制 ++ - need_restart_application: False # 修改参数之后是否需要重启应用使参数生效 ++ need_recover_cluster: False # 调优过程中是否需要恢复集群 ++ microDep_collector: True # 是否开启微架构指标采集 ++ pressure_test_mode: True # 是否通过压测模拟负载环境 ++ tune_system_param: False # 是否调整系统参数 ++ tune_app_param: True # 是否调整应用参数 ++ strategy_optimization: False # 是否需要策略推荐 ++ benchmark_timeout: 3600 # benchmark执行超时限制 ++ max_iterations: 10 # 最大迭代轮数 ++ slo_goal: 0.1 # 调优提升目标,默认0.1也即10%,调优达成提升目标后会提前结束 ++ +``` + -+2. 完善app_config.yaml,放入项目的config/app_config.yaml中(重点是补充set_param_template、get_param_template、benchmark脚本),具体内容如下: ++#### 2. 完善 app_config.yaml(项目 config 目录下) ++(需按实际环境修改,重点关注 set_param_template、 get_param_template、 benchmark 脚本) ++* set_param_template:设置应用配置参数(copilot调优时,会使用此脚本修改参数值) ++```YAML ++# 说明: ++# - $param_name:将被copilot替换为待修改的参数名(如 worker_connections) ++# - $param_value:将被copilot替换为参数目标值(如 8192) ++# - $config_file:指向应用配置文件路径(已在 config 中定义) ++ ++# 示例(mysql): ++set_param_template: 'grep -q "^$param_name\\s*=" "$config_file" && sed -i "s/^$param_name\\s*=.*/$param_name = $param_value/" "$config_file" || sed -i "/\\[mysqld\\]/a $param_name = $param_value" "$config_file"' ++``` ++ ++* get_param_template:获取应用配置参数 ++```YAML ++# 说明: ++# - $param_name:将被copilot替换为参数名 ++ ++# 示例(mysql): ++get_param_template: 'grep -E "^$param_name\\s*=" $config_file | cut -d= -f2- | xargs' ++``` ++ ++* benchmark:压测命令模版 ++```YAML ++# 说明: ++# - $EXECUTE_MODE:local → 在 Copilot 控制机本地执行 ++# - $EXECUTE_MODE:remote → 通过 SSH 跳转到目标机器执行,默认使用remote执行模式 ++# - 其他变量(如 $host_ip, $port, $user)将被自动替换 ++ ++# 示例(mysql): ++benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/mysql/parse_benchmark.sh $host_ip $port $user $password" ++``` ++ ++* __完整配置示例如下:__ +```YAML +mysql: + user: "root" @@ -702,9 +1018,9 @@ index 0000000..5823fad + performance_metric: "QPS" + +flink: -+ set_param_template: 'sh /home/wsy/set_param.sh $param_name $param_value' -+ get_param_template: 'sh /home/wsy/get_param.sh $param_name' -+ benchmark: "sh /home/wsy/nexmark_test.sh" ++ set_param_template: '/patch/to/script/set_param.sh $param_name $param_value' ++ get_param_template: '/patch/to/script/get_param.sh $param_name' ++ benchmark: "/patch/to/script/nexmark_test.sh" + stop_workload: 'docker exec -i flink_jm_8c32g bash -c "source /etc/profile && /usr/local/flink-1.16.3/bin/stop-cluster.sh && /usr/local/nexmark/bin/shutdown_cluster.sh"' + start_workload: 'docker exec -i flink_jm_8c32g bash -c "source /etc/profile && /usr/local/flink-1.16.3/bin/start-cluster.sh"' + performance_metric: "THROUGHPUT" @@ -771,54 +1087,31 @@ index 0000000..5823fad + benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/redis/parse_benchmark.sh $host_ip $port " + performance_metric: "QPS" + -+ +``` -+其中: -+set_param_template:根据调优结果修改应用参数,用于后续测试效果 -+get_param_template:获取应用参数 -+recover_workload: 恢复集群 -+benchmark:benchmark脚本,格式如下: + -+```bash ++#### 3. benchmark.sh 脚本具体内容如下: ++```YAML +#(必须有)用于通知框架可以执行指标采集的标识 +echo 1 > /tmp/euler-copilot-fifo + -+#benchmark具体执行 ++# benchmark 具体执行 +cd /root/spark_auto_deploy_arm/spark_test +sh tpcds_test_1t_spark331_linearity_2p.sh > /home/cxm/spark_benchmark.log 2>&1 + -+#(必须有)计算并输出相应的performance_metric的语句 ++#(必须有)计算并输出相应的 performance_metric 的语句 +cd /home/cxm +time_taken=$(grep "time_taken:" "spark_benchmark.log" | sed -E 's/.*time_taken:([0-9.]+)s.*/\1/' | paste -sd+ | bc | xargs printf "%.2f") +echo $time_taken +``` -+3. 运行EulerCopilot -+```bash -+export PYTHONPATH="`pwd`:$PYTHONPATH" -+python3 src/start_tune.py -+ -+``` -+#### 服务的方式运行: + -+1、安装服务 -+ -+​ 进入项目目录,执行python setup.py install -+ -+2、在/etc/euler-copilot-tune 目录修改配置文件,具体内容参考上面源码部署方式 -+ -+3、启动服务 -+ -+~~~bash -+#命令行执行如下命令 -+#开启调优 -+euler-copilot-tune -+#开启mcpserver 日志通过执行 journalctl -xe -u tune-mcpserver --all -f 查看 -+tune-mcpserver -+~~~ ++### 应用示例 ++* [mysql 应用验证示例](doc/zh/mysql.md) ++* [spark 应用验证示例](doc/zh/spark.md) ++* [ceph 应用验证示例](doc/zh/ceph.md) ++* [nginx 应用验证示例](doc/zh/nginx.md) + -+​ + -+### 常见问题解决 ++## 常见问题解决 + +见 [FAQ.md](./FAQ.md) \ No newline at end of file @@ -827,10 +1120,10 @@ new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/config/.env.yaml b/copilot-tune/config/.env.yaml new file mode 100644 -index 0000000..d0da340 +index 0000000..0bb466d --- /dev/null +++ b/copilot-tune/config/.env.yaml -@@ -0,0 +1,72 @@ +@@ -0,0 +1,134 @@ +LLM_KEY: "YOUR_LLM_KEY" +LLM_URL: "YOUR_LLM_URL" +LLM_MODEL_NAME: "YOUR_LLM_MODEL_NAME" @@ -840,11 +1133,13 @@ index 0000000..d0da340 +REMOTE_EMBEDDING_MODEL_NAME: "YOUR_MODEL_NAME" + +servers: -+ - ip: "9.82.36.53" -+ host_user: "root" -+ password: "Huawei12#$" ++ - ip: "" ++ host_user: "" ++ password: "" + port: 22 + app: "flink" ++ listening_address: "9.82.36.53" ++ listening_port: 8081 + target_process_name: "jobmanager" + business_context: "高并发流处理服务,CPU负载主要集中在用户态算子与序列化反序列化" + max_retries: 3 @@ -857,6 +1152,8 @@ index 0000000..d0da340 +# password: "" +# port: 22 +# app: "mysql" ++# listening_address: "" ++# listening_port: "" +# target_process_name: "mysqld" +# max_retries: 3 +# delay: 1.0 @@ -867,6 +1164,8 @@ index 0000000..d0da340 +# password: "" +# port: +# app: "spark" ++# listening_address: "" ++# listening_port: "18080" +# target_process_name: "java" +# business_context: "基于内存计算的分布式批处理框架,适合大规模数据处理任务,CPU负载主要集中在用户态的序列化、反序列化及任务调度执行过程" +# max_retries: 3 @@ -877,11 +1176,13 @@ index 0000000..d0da340 +# host_user: "" +# password: "" +# port: -+# app: "gaussdb" -+# target_process_name: "gaussdb" -+# business_context: "高并发数据库服务,CPU负载主要集中在用户态处理" -+# max_retries: 3 -+# delay: 1.0 ++# app: "gaussdb" ++# listening_address: "" ++# listening_port: "" ++# target_process_name: "gaussdb" ++# business_context: "高并发数据库服务,CPU负载主要集中在用户态处理" ++# max_retries: 3 ++# delay: 1.0 + +#servers: +# - ip: "" @@ -889,11 +1190,58 @@ index 0000000..d0da340 +# password: "" +# port: +# app: "nginx" ++# listening_address: "127.0.0.1" ++# listening_port: "10000" +# target_process_name: "nginx" +# business_context: "高并发Web服务,CPU负载主要集中在用户态处理" +# max_retries: 3 +# delay: 1.0 + ++# servers: ++# # OBServer 节点 1 ++# - ip: "" ++# host_user: "" ++# password: "" ++# port: 22 ++# app: "oceanbase" ++# target_process_name: "observer" ++# business_context: "OceanBase Server 节点 1,负责数据存储与计算" ++# max_retries: 3 ++# delay: 1.0 ++ ++# # OBServer 节点 2 ++# - ip: "" ++# host_user: "" ++# password: "" ++# port: 22 ++# app: "oceanbase" ++# target_process_name: "observer" ++# business_context: "OceanBase Server 节点 2,负责数据存储与计算" ++# max_retries: 3 ++# delay: 1.0 ++ ++# # OBServer 节点 3 ++# - ip: "" ++# host_user: "" ++# password: "" ++# port: 22 ++# app: "oceanbase" ++# target_process_name: "observer" ++# business_context: "OceanBase Server 节点 3,负责数据存储与计算" ++# max_retries: 3 ++# delay: 1.0 ++ ++# # Proxy 节点 ++# - ip: "" ++# host_user: "" ++# password: "" ++# port: 22 ++# app: "oceanbase" ++# target_process_name: "obproxy" ++# business_context: "OceanBase Proxy 节点,负责 SQL 路由与连接负载均衡" ++# max_retries: 3 ++# delay: 1.0 ++ +feature: + - need_restart_application: False + need_recover_cluster: False @@ -903,12 +1251,20 @@ index 0000000..d0da340 + tune_app_param: True + strategy_optimization: False + benchmark_timeout: 3600 ++ max_iterations: 10 ++ slo_goal: 0.1 ++ ++#是否开启大模型的ssl验证 enable 开启 disable 关闭 默认开启,当大模型无法通过ssl验证的时候可以改成disable ++ssl: enable ++ ++log_level: info +\ No newline at end of file diff --git a/copilot-tune/config/app_config.yaml b/copilot-tune/config/app_config.yaml new file mode 100644 -index 0000000..8b8316c +index 0000000..07e763c --- /dev/null +++ b/copilot-tune/config/app_config.yaml -@@ -0,0 +1,81 @@ +@@ -0,0 +1,93 @@ +mysql: + user: "root" + password: "123456" @@ -970,7 +1326,7 @@ index 0000000..8b8316c + config_file: "/path/of/config_file" + port: 5432 + set_param_template: 'gs_guc set -Z datanode -N all -I all -c "${param_name}=${param_value}"' -+ get_param_template: 'gs_guc check -Z datanode -N all -I all -c "${param_name}"' ++ get_param_template: 'gs_guc check -Z datanode -N all -I all -c "${param_name}" | awk -F= "/${param_name}=/"\ {print\ \$2}' + stop_workload: "cm_ctl stop -m i" + start_workload: "cm_ctl start" + recover_workload: "$EXECUTE_MODE:local sh /path/of/gaussdb_cluster_recover.sh" @@ -984,12 +1340,266 @@ index 0000000..8b8316c +redis: + port: 6379 + config_file: "/etc/redis.conf" -+ set_param_template: "sed -i 's/^$param_name/$param_name $param_value/g' $config_file" -+ get_param_template: "grep -P '$param_name' $config_file | awk '{print $2}" ++ set_param_template: 'grep -q "^\\s*$param_name\\s\\+" "$config_file" && sed -i "s/^\\s*$param_name\\s\\+.*/$param_name $param_value/g" $config_file || echo "$param_name $param_value" >> "$config_file"' ++ get_param_template: 'grep -oP "^\s*$param_name\s*\K.*" "$config_file"' + start_workload: "systemctl start redis" + stop_workload: "systemctl stop redis" + benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/redis/parse_benchmark.sh $host_ip $port " + performance_metric: "QPS" ++ ++oceanbase: ++ user: "root@sysbench_tenant" ++ password: "" ++ config_file: "/root/.obd/cluster/obcluster/config.yaml" ++ port: 2881 ++ set_param_template: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/oceanbase/set_param.sh $param_name $param_value" ++ get_param_template: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/oceanbase/get_param.sh $param_name" ++ stop_workload: "obd cluster stop obcluster" ++ start_workload: "obd cluster start obcluster" ++ benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/oceanbase/parse_benchmark.sh" ++ performance_metric: "QPS" +\ No newline at end of file +diff --git a/copilot-tune/config/defaults.yaml b/copilot-tune/config/defaults.yaml +new file mode 100644 +index 0000000..40c3787 +--- /dev/null ++++ b/copilot-tune/config/defaults.yaml +@@ -0,0 +1,234 @@ ++ ++# ==================================================== common ===================================================== ++common: &common ++ mode: normal # fast, normal, slow ;default slow ++ extractor: ++ value: "" ++ meta: {lang: zh, version: 1} ++ analyzer: ++ value: "" ++ meta: {lang: zh, version: 1} ++ collector: ++ value: "" ++ meta: {lang: zh, version: 1} ++ fast: ++ recommender: ++ value: | ++ # CONTEXT # ++ [{self.service_name}类] 本次性能优化的目标为: ++ 性能指标为 {self.performance_metric.name} ,该指标的含义为:{self.performance_metric.value} ,目标是提升 {self.slo_goal:.2%}。 ++ 当前环境的配置信息: ++ {self.static_profile} ++ 历史调优信息(可参考改进点): ++ {history_result} ++ 当前环境的系统负载与性能分析: ++ {self.performance_analysis_report} ++ 可调整的参数集合: ++ {params_set_str} ++ # OBJECTIVE # ++ 当前性能指标没有达到预期,请你基于上述信息,直接输出一份**参数组合**。 ++ 1. 输出内容必须是一个合法的 JSON 对象。 ++ 2. 格式如下: ++ {{ ++ 参数名: 值(如果不确定这个值是否合理, 这组参数可以不推荐) ++ }} ++ 3. 只输出键值对,不要任何解释、注释、额外文字。 ++ 4. 只包含对性能有正向影响的参数调整。 ++ meta: {lang: zh, version: 1} ++ normal: ++ idea: ++ value: | ++ # CONTEXT # ++ 本次性能优化的目标为: ++ 性能指标为{self.performance_metric.name}, 该指标的含义为:{self.performance_metric.value},目标是提升{self.slo_goal:.2%} ++ 性能分析报告: ++ {self.performance_analysis_report} ++ 你可以分析的参数有: ++ {params_set_str} ++ # OBJECTIVE # ++ 你是一个专业的系统运维专家,当前性能指标未达到预期,请你基于以上性能分析报告分析有哪些调优思路。 ++ # Tone # ++ 你应该尽可能秉承严肃、认真、严谨的态度 ++ # AUDIENCE # ++ 你的答案将会是其他系统运维专家的重要参考意见,请认真思考后给出你的答案。 ++ recommender_positive: ++ value: | ++ [{self.service_name}类] 你是一个专业的系统运维专家,当前性能指标未达到预期,请你基于以下调优思路、当前环境的配置信息、可调整参数,选出可调整参数值。 ++ 请尽量精简描述,将终点需要调整的方向输出出来,不需要总结观点,对性能无影响的也不要输出。 ++ 当前环境的配置信息有: ++ {self.static_profile} ++ 以下是历史调优的信息,历史调优修改了如下参数,你可以参考如下历史调优信息来反思可以可以改进的点: ++ {history_result} ++ 调优思路是: ++ {optimization_idea} ++ 你可以调整的参数是: ++ {params_set_str} ++ 请以json格式回答问题,key为可调参数名称,请根据上述的环境配置信息给出可调整的参数,若参数不相关则不要给出 ++ value是可调参数的推荐取值,请根据上面的环境配置信息给出合理的具体取值,请仔细确认各个值是否可以被使用,避免设置后应用无法启动。 ++ 请勿给出除了json以外其他的回复,切勿增加注释。 ++ recommender_negative: ++ value: | ++ [{self.service_name}类] 你是一个专业的系统运维专家,当前性能指标未达到预期,请你基于以下调优思路、当前环境的配置信息、可调整参数,选出可调整参数值。 ++ 请尽量精简描述,将终点需要调整的方向输出出来,不需要总结观点,对性能无影响的也不要输出。 ++ 当前环境的配置信息有: ++ {self.static_profile} ++ 以下是历史调优的信息,历史调优修改了如下参数,你可以参考如下历史调优信息来反思可以可以改进的点: ++ {history_result} ++ 调优思路是: ++ {optimization_idea} ++ 你可以调整的参数是: ++ {params_set_str} ++ 请以json格式回答问题,key为可调参数名称,请根据上述的环境配置信息给出可调整的参数,若参数不相关则不要给出 ++ value是可调参数的推荐取值,请根据上面的环境配置信息给出合理的具体取值,请仔细确认各个值是否可以被使用,避免设置后应用无法启动。 ++ 请勿给出除了json以外其他的回复,切勿增加注释。 ++ slow: ++ recommender: ++ value: "" ++ meta: {lang: zh, version: 1} ++# ==================================================== nginx ===================================================== ++nginx: ++ mode: normal # fast, normal, slow ;default slow ++ extractor: ++ value: "" ++ meta: {lang: zh, version: 1} ++ analyzer: ++ value: "" ++ meta: {lang: zh, version: 1} ++ collector: ++ value: "" ++ meta: {lang: zh, version: 1} ++ fast: ++ recommender: ++ value: | ++ # CONTEXT # ++ [{self.service_name}类] 本次性能优化的目标为: ++ 性能指标为 {self.performance_metric.name} ,该指标的含义为:{self.performance_metric.value} ,目标是提升 {self.slo_goal:.2%}。 ++ 当前环境的配置信息: ++ {self.static_profile} ++ 历史调优信息(可参考改进点): ++ {history_result} ++ 当前环境的系统负载与性能分析: ++ {self.performance_analysis_report} ++ **可调整的参数集合以及其取值范围和功能信息**: ++ {params_set_str} ++ # 要求如下 # ++ 请你基于上述信息,直接输出一份 "在可调整的参数集合中选择一些你觉得有提升的参数进行推荐" ++ **严格要求:** ++ 1. 输出内容必须是一个合法的简单的 json 格式字符串。 ++ 2. 模板参考如下格式(不确定的键值对可以不推荐, 也不要重复推荐, json 对象注意左右括号闭环, 不要引入 [(中括号) 括号),且禁止出现多余的转义引号(例如 \"value\")。 ++ {{ ++ "net.core.dev_weight": 128, ++ "net.ipv4.tcp_keepalive_intvl": 60, ++ "net.ipv4.tcp_keepalive_probes": 5, ++ "net.ipv4.tcp_keepalive_time": 600, ++ "net.ipv4.tcp_tw_reuse": 1, ++ "net.ipv4.tcp_fin_timeout": 30, ++ "net.ipv4.tcp_rmem": "4096 65536 16777216", ++ "net.ipv4.tcp_wmem": "4096 65536 16777216", ++ "net.ipv4.tcp_max_syn_backlog": 8192 ++ }} ++ 3. 只输出符合格式的 json 字符串,不要任何解释、注释、额外文字。 ++ 4. 基于上述内容,请你务必提供一份干净“简单”的 json 参数值推荐,方便我直接通过 json.loads 进行解析。 ++ meta: {lang: zh, version: 1} ++ normal: ++ idea: ++ value: | ++ # CONTEXT # ++ 本次性能优化的目标为: ++ 性能指标为{self.performance_metric.name}, 该指标的含义为:{self.performance_metric.value},目标是提升{self.slo_goal:.2%} ++ 性能分析报告: ++ {self.performance_analysis_report} ++ 你可以分析的参数有: ++ {params_set_str} ++ # OBJECTIVE # ++ 你是一个专业的系统运维专家,当前性能指标未达到预期,请你基于以上性能分析报告分析有哪些调优思路。 ++ # Tone # ++ 你应该尽可能秉承严肃、认真、严谨的态度 ++ # AUDIENCE # ++ 你的答案将会是其他系统运维专家的重要参考意见,请认真思考后给出你的答案。 ++ meta: {lang: zh, version: 1} ++ recommender_positive: ++ value: | ++ 你是专业的系统运维专家。当前性能指标未达预期,但上一轮调优为正向结果(性能提升或无退化)。 ++ 请在“心中完成推理”,只输出最终 JSON;除 JSON 以外不要输出任何文字、代码块或注释。 ++ ++ 目标:基于以下信息,在保持上轮有效方向的前提下,总结参数调整经验,进一步微调参数(在安全边界内适度加大力度),仅给出需要变更的参数与推荐新值。 ++ ++ 当前环境配置信息: ++ {self.static_profile} ++ ++ 历史调优信息(包含已修改参数与结果): ++ {history_result} ++ ++ 调优思路: ++ {optimization_idea} ++ ++ 可调整参数全集(含类型/范围/枚举/默认值等)以及baseline对应的的取值为: ++ {params_set_str} ++ ++ 严格规则(务必遵守): ++ 1) 仅输出与当前配置相比“需要变化”的参数;不相关或无收益的参数不要输出。 ++ 2) 优先沿“上轮有效”的方向小步前进:连续型参数按原步长的 100%~150% 微增(通常为 +10%~+30%),离散/枚举取更激进且仍在安全范围的相邻档位;避免一次性过大变更(单参数变更幅度不超过 2 倍或 ±30%,取更严格者)。 ++ 3) 不要动已证明对性能“无影响”的参数;避免同时调整明显互斥的参数。 ++ 4) 必须满足依赖/互斥/上限下限/类型与单位要求;数值默认单位为“字节”。若数值后带单位,请以字符串表示(如 "512MB")。 ++ 5) 每个参数的推荐值必须可被系统实际接受并确保应用可启动。 ++ 6) 若无合适变更,输出空json对象。 ++ ++ 输出格式(必须严格遵守): ++ - 仅输出一个 JSON 对象,键为“可调参数名称”,值为“推荐取值”。 ++ - 不要输出任何多余文字、说明、示例、代码围栏或注释。 ++ recommender_negative: ++ value: | ++ 你是专业的系统运维专家。当前性能指标未达预期,且上一轮调优为负向结果(性能下降/不稳定/报错等)。 ++ 请在“心中完成推理”,只输出最终 JSON;除 JSON 以外不要输出任何文字、代码块或注释。 ++ ++ 目标:基于以下信息,总结历史调优经验中的baseline、最佳调优结果、最差调优结果以及上一轮调优结果以及参数取值,反向微调上轮可能导致退化的参数,并选择更保守且安全的值;仅给出需要变更的参数与推荐新值。 ++ ++ 当前环境配置信息: ++ {self.static_profile} ++ ++ 历史调优信息(包含已修改参数与结果): ++ {history_result} ++ ++ 调优思路: ++ {optimization_idea} ++ ++ 可调整参数全集(含类型/范围/枚举/默认值等)以及baseline对应的的取值为: ++ {params_set_str} ++ ++ 严格规则(务必遵守): ++ 1) 仅输出与当前配置相比“需要变化”的参数;不相关或无收益的参数不要输出。 ++ 2) 对上轮参与变更且疑似致退化的参数:沿“相反方向”小步调整(幅度为上轮步长的 30%~50%,通常为 -10%~-20%);必要时关闭可选的高开销特性。 ++ 3) 避免一次调整过多参数;不要同时调整互斥参数;优先选择风险更低的修正方案。 ++ 4) 必须满足依赖/互斥/上限下限/类型与单位要求;数值默认单位为“字节”。若数值后带单位,请以字符串表示(如 "1GB")。 ++ 5) 每个参数的推荐值必须可被系统实际接受并确保应用可启动。 ++ 6) 若无合适变更,输出空json对象。 ++ ++ 输出格式(必须严格遵守): ++ - 仅输出一个 JSON 对象,键为“可调参数名称”,值为“推荐取值”。 ++ - 不要输出任何多余文字、说明、示例、代码围栏或注释。 ++ slow: ++ recommender: ++ value: "" ++ meta: {lang: zh, version: 1} ++# ==================================================== mysql ===================================================== ++mysql: ++ <<: *common ++# ==================================================== pgsql ===================================================== ++pgsql: ++ <<: *common ++# ==================================================== redis ===================================================== ++redis: ++ <<: *common ++# ==================================================== spark ===================================================== ++spark: ++ <<: *common ++# ==================================================== flink ===================================================== ++flink: ++ <<: *common ++# ==================================================== ceph ===================================================== ++ceph: ++ <<: *common ++# ==================================================== gaussdb ===================================================== ++gaussdb: ++ <<: *common +\ No newline at end of file diff --git a/copilot-tune/config/knob_rag_config.json b/copilot-tune/config/knob_rag_config.json new file mode 100644 index 0000000..2eb63f1 @@ -1003,10 +1613,10 @@ index 0000000..2eb63f1 \ No newline at end of file diff --git a/copilot-tune/config/openapi.yaml b/copilot-tune/config/openapi.yaml new file mode 100644 -index 0000000..ddeefb0 +index 0000000..8cbf882 --- /dev/null +++ b/copilot-tune/config/openapi.yaml -@@ -0,0 +1,69 @@ +@@ -0,0 +1,48 @@ +openapi: 3.0.0 +info: + title: 性能调优分析接口 @@ -1019,56 +1629,35 @@ index 0000000..ddeefb0 + get: + summary: 获取数据采集结果 + description: 通过ip获取数据采集结果 -+ parameters: -+ - name: ip -+ in: query -+ required: true -+ schema: -+ type: string -+ example: "9.82.201.111" -+ description: 待采集指标的ip地址 + responses: + '200': + description: 成功采集数据 + content: + application/json: -+ schema: '#/components/schemas/ApiResponse' ++ schema: ++ $ref: '#/components/schemas/ApiResponse' + /analyzer: + get: + summary: 瓶颈分析接口 + description: 基于采集到的数据分析性能瓶颈 -+ parameters: -+ - name: ip -+ in: query -+ required: true -+ schema: -+ type: string -+ example: "9.82.201.111" -+ description: 待分析瓶颈机器的ip地址 + responses: + '200': + description: 成功采集数据 + content: + application/json: -+ schema: '#/components/schemas/ApiResponse' ++ schema: ++ $ref: '#/components/schemas/ApiResponse' + /optimizer: + get: + summary: 参数推荐接口 + description: 基于当前瓶颈推荐对应参数 -+ parameters: -+ - name: ip -+ in: query -+ required: true -+ schema: -+ type: string -+ example: "9.82.201.111" -+ description: 待分析瓶颈机器的ip地址 + responses: + '200': + description: 成功采集数据 + content: + application/json: -+ schema: '#/components/schemas/ApiResponse' ++ schema: ++ $ref: '#/components/schemas/ApiResponse' +components: + schemas: + ApiResponse: @@ -1076,6 +1665,7 @@ index 0000000..ddeefb0 + properties: + data: + type: object +\ No newline at end of file diff --git a/copilot-tune/config/optimize_config.yaml b/copilot-tune/config/optimize_config.yaml new file mode 100644 index 0000000..71cb539 @@ -1292,36 +1882,1280 @@ index 0000000..5823fad + +见 [FAQ.md](./FAQ.md) \ No newline at end of file -diff --git a/copilot-tune/requirements.txt b/copilot-tune/requirements.txt -new file mode 100644 -index 0000000..2b86bdc ---- /dev/null -+++ b/copilot-tune/requirements.txt -@@ -0,0 +1,20 @@ -+faiss_cpu==1.8.0.post1 -+fastapi==0.112.2 -+numpy==1.26.4 -+openai==1.52.0 -+paramiko==3.4.1 -+pydantic>=2.8.2 -+PyYAML==6.0.2 -+scikit_learn==1.5.1 -+tqdm==4.66.4 -+uvicorn==0.30.6 -+zhipuai==2.1.4.20230814 -+requests==2.32.3 -+langchain==0.3.4 -+langchain-openai==0.2.3 -+email-validator==2.2.0 -+httpx==0.27.2 -+tabulate==0.9.0 -+pyfiglet==1.0.3 -+gssapi==1.9.0 -+pandas==2.3.1 -\ No newline at end of file -diff --git a/copilot-tune/scripts/ceph/benchmark.sh b/copilot-tune/scripts/ceph/benchmark.sh +diff --git a/copilot-tune/doc/zh/ceph.md b/copilot-tune/doc/zh/ceph.md new file mode 100644 -index 0000000..b372677 +index 0000000..9952b56 +--- /dev/null ++++ b/copilot-tune/doc/zh/ceph.md +@@ -0,0 +1,365 @@ ++# 1.ceph环境准备 ++ceph环境部署后包含benchmark测试工具rados ++## 1.1部署ceph集群 ++ ++准备四台网络互通的虚拟机环境:​ ++client:​客户端节点,管理和监控 Ceph 集群。​ ++ceph1、ceph2、ceph3:​Ceph 集群的存储节点。 ++ ++### 1.1.1 设置主机名 ++分别在四个节点上执行重命名命令,设置相应的主机名,用于区分不同功能的节点: ++```bash ++hostnamectl set-hostname client #client节点执行 ++hostnamectl set-hostname ceph1 #ceph1节点执行 ++hostnamectl set-hostname ceph2 #ceph2节点执行 ++hostnamectl set-hostname ceph3 #ceph3节点执行 ++``` ++设置新的主机名后,使用 hostnamectl 来查看是否配置生效,通常看到 Static hostname: client 这行内容表示设置成功。 ++### 1.1.2 配置主机映射 ++在四个节点上配置hosts文件(路径 /etc/hosts),使服务器之间可以通过主机名互相访问,(根据实际 IP 地址进行修改): ++```bash ++vim /etc/hosts #打开hosts文件,添加节点的 IP 地址和主机名映射 ++ ++9.82.154.180 client ++9.82.222.129 ceph1 ++9.82.247.43 ceph2 ++9.82.179.1 ceph3 ++``` ++hosts文件配置后,确认各个节点之间可以互相ping通。如: ++```bash ++ping ceph1 ++``` ++### 1.1.3 配置SSH免密登录 ++ ++ceph安装过程中需要多次输入机器的密码,可以通过配置免密登录提升效率。 ++ ++本地生成密钥,执行此命令后按Enter键可以全部采用默认的配置。 ++```bash ++ssh-keygen ++``` ++将公钥分发到各个节点,分发公钥过程中需要输入相应虚拟机的密码: ++```bash ++ssh-copy-id root@ceph1 ++ssh-copy-id root@ceph2 ++ssh-copy-id root@ceph3 ++``` ++验证免密登录,远程虚拟机免密登录后,可以使用 exit 命令返回本地虚拟机: ++```bash ++ssh root@ceph1 ++ssh root@ceph2 ++ssh root@ceph3 ++``` ++### 1.1.4 关闭防火墙(可选) ++ ++为避免防火墙阻止 Ceph 节点之间的通信,在所有节点上暂时关闭防火墙: ++ ++```bash ++systemctl stop firewalld ++systemctl disable firewalld ++``` ++ ++### 1.1.5 安装ceph ++ ++所有节点(client、ceph1、ceph2、ceph3)上分别安装 Ceph 软件包。执行以下命令: ++```bash ++yum install ceph -y ++``` ++### 1.1.5 下载ceph-deploy自动化部署工具 ++在 client 节点上,使用 pip3 安装 ceph-deploy 工具,该工具用于简化 Ceph 集群的部署和管理,通过 SSH 在远程主机上执行命令,自动化 Ceph 集群的部署过程: ++```bash ++pip3 install git+https://github.com/ceph/ceph-deploy.git ++``` ++安装过程中会有warning告警,可以忽略,使用--help命令来查看是否安装成功。ceph-deploy --help会列出所有的可用命令和选项: ++```bash ++ceph-deploy --help ++``` ++### 1.1.6 初始化ceph集群 ++ ++ ++**创建配置文件目录** ++ ++在client节点上执行,创建一个用于存放集群配置和密钥的目录: ++```bash ++mkdir ceph-cluster ++cd ceph-cluster/ ++``` ++**创建ceph节点** ++ ++在 client 节点上,使用 ceph-deploy 创建一个新的 Ceph 集群配置,指定初始的监视器(MON)节点。执行后将在当前目录生成一个 ceph.conf 配置文件,包含指定的 MON 节点信息: ++```bash ++ceph-deploy new ceph1 ceph2 ceph3 ++``` ++各节点之间如果没有配置免密,命令执行后需要多次输入密码。 ++该命令执行成功后会生成文件ceph.conf(Ceph 集群的配置文件),ceph.mon.keyring(Monitor 节点的密钥文件),ceph-deploy-ceph.log(Monitor 节点的日志文件)。 ++ ++**部署MON服务** ++ ++在 client 节点上,部署并初始化 MON 服务: ++```bash ++ceph-deploy mon create-initial ++``` ++执行成功后会在管理节点上生成密钥文件,如ceph.client.admin.keyring等,包含ceph集群的认证密钥。 ++ ++**将 ceph.client.admin.keyring 拷贝到各个节点上** ++ ++在client节点上执行,为了确保各个节点能与集群进行交互,将认证密钥文件(ceph.client.admin.keyring)分发到所有的 Ceph 节点和客户端节点: ++```bash ++ceph-deploy --overwrite-conf admin ceph1 ceph2 ceph3 client ++``` ++该命令成功执行后,会将当前目录下的ceph.conf,ceph.client.admin.keyring文件推送到指定的节点(ceph1、ceph2、ceph3 和 client)的/etc/ceph/ 目录下,并覆盖目标节点的同名文件。 ++ ++ ++将client节点上的密钥分发到各节点 ++ ++``` ++for host in ceph1 ceph2 ceph3; do ++ scp ceph.conf root@$host:/etc/ceph/ceph.conf ++ scp ceph.client.admin.keyring root@$host:/etc/ceph/ceph.client.admin.keyring ++ ssh root@$host "mkdir -p /var/lib/ceph/bootstrap-osd/" ++ scp ceph.bootstrap-osd.keyring root@$host:/var/lib/ceph/bootstrap-osd/ceph.keyring ++ ssh root@$host "chown -R ceph:ceph /var/lib/ceph/bootstrap-osd && chmod 600 /var/lib/ceph/bootstrap-osd/ceph.keyring" ++done ++``` ++ ++**部署MGR节点** ++ ++在client节点上执行,为ceph1、ceph2、ceph3三个节点部署 MGR 服务,保证集群的监控和管理功能正常运行: ++```bash ++ceph-deploy mgr create ceph1 ceph2 ceph3 ++``` ++**配置客户端访问** ++ ++在client节点执行,客户端可以通过 Ceph 的配置文件来访问集群: ++```bash ++ceph-deploy admin client ceph1 ceph2 ceph3 ++``` ++执行成功后,可以通过 ceph -s 来查看集群中的守护管理进程(ceph-mgr)是否正常运行。 ++ ++**禁用不安全的MON认证恢复** ++ ++在client节点执行,auth_allow_insecure_global_id_reclaim 是 Ceph 配置中的一个参数,默认情况下,Ceph 可能会在认证恢复时使用全局 ID,禁用它可以提高集群的安全性: ++```bash ++ceph config set mon auth_allow_insecure_global_id_reclaim false ++``` ++执行过程中没有error报错,表示执行成功。 ++ ++**创建OSD存储** ++ ++在三个ceph节点,ceph1、ceph2、ceph3上执行。 ++查看磁盘使用情况,列出系统中的所有磁盘和分区,选择一个空闲的磁盘来作为 OSD 存储,下面命令以/dev/sda3为例: ++```bash ++lsblk ++``` ++##### 新创建磁盘分区 ++ ++``` ++umount /dev/mapper/openeuler-home # 卸载 /home 逻辑卷(确保数据不被占用才能操作) ++e2fsck -f /dev/mapper/openeuler-home # 强制检查并修复 /home 文件系统 ++resize2fs /dev/mapper/openeuler-home XXG # 调整文件系统大小,推荐值为 20G ++lvreduce -L 20G /dev/mapper/openeuler-home # 缩小逻辑卷大小到 20G(要先缩文件系统,再缩逻辑卷) ++mount /dev/mapper/openeuler-home /home # 重新挂载 /home 逻辑卷 ++df -h /home # 查看 /home 挂载点的磁盘使用情况 ++pvresize --setphysicalvolumesize XXG /dev/vda3 # 调整物理卷 /dev/vda3 的可用大小 ++vgdisplay openeuler # 查看卷组 openeuler 的信息 ++lvcreate -n ceph-osd-01 -L XG openeuler # 在卷组 openeuler 中创建一个 XG 的逻辑卷 ceph-osd-01,推荐值为 9G ++ceph-volume lvm prepare --data /dev/openeuler/ceph-osd-01 # 使用 ceph-volume 工具准备 OSD,指定数据盘为新建的逻辑卷 ++ ++umount /dev/mapper/openeuler-home ++e2fsck -f /dev/mapper/openeuler-home ++resize2fs /dev/mapper/openeuler-home XXG ++lvreduce -L 20G /dev/mapper/openeuler-home ++mount /dev/mapper/openeuler-home /home ++df -h /home ++pvresize --setphysicalvolumesize XXG /dev/vda3 ++vgdisplay openeuler ++lvcreate -n ceph-osd-02 -L XG openeuler ++ceph-volume lvm prepare --data /dev/openeuler/ceph-osd-02 ++ ++umount /dev/mapper/openeuler-home ++e2fsck -f /dev/mapper/openeuler-home ++resize2fs /dev/mapper/openeuler-home XXG ++lvreduce -L 20G /dev/mapper/openeuler-home ++mount /dev/mapper/openeuler-home /home ++df -h /home ++pvresize --setphysicalvolumesize XXG /dev/vda3 ++vgdisplay openeuler ++lvcreate -n ceph-osd-03 -L XG openeuler ++ceph-volume lvm prepare --data /dev/openeuler/ceph-osd-03 ++``` ++ ++ ++ ++在client节点上执行,对三个ceph节点分别执行如下命令,注意disk zap 操作会擦除磁盘上的所有数据,建议提前数据备份。osd create 操作会将该磁盘创建为 Ceph 集群中的 OSD 存储设备: ++```bash ++ceph-deploy disk zap ceph1 /dev/openeuler/ceph-osd-01 ++ceph-deploy osd create ceph1 --data /dev/openeuler/ceph-osd-01 ++ceph-deploy disk zap ceph2 /dev/openeuler/ceph-osd-02 ++ceph-deploy osd create ceph2 --data /dev/openeuler/ceph-osd-02 ++ceph-deploy disk zap ceph3 /dev/openeuler/ceph-osd-03 ++ceph-deploy osd create ceph3 --data /dev/openeuler/ceph-osd-03 ++``` ++可以查看ceph的日志文件来查看详细命令执行过程,确保执行成功: ++```bash ++tail -f /var/log/ceph/ceph.log ++``` ++### 1.1.7 确认集群搭建完成 ++ ++执行以下命令来查看ceph-mon服务的状态,确保所有ceph节点都正常运行。如果所有节点都显示为 active (running),则说明ceph节点已成功启动: ++```bash ++systemctl status ceph-mon@ceph1 ++systemctl status ceph-mon@ceph2 ++systemctl status ceph-mon@ceph3 ++``` ++ ++查看集群的健康状态、OSD 节点、MON 节点的运行状态等信息: ++```bash ++ceph -s ++``` ++正常输出结果, ++有3个监视器守护进程(ceph1、ceph2、ceph3),它们已组成法定人数(quorum)并且运行正常,有3个管理守护进程(mgr.ceph1 是活动的,ceph2 和 ceph3 是备用的),有存储池和存储的对象。 ++```bash ++[root@client ceph]# ceph -s ++ cluster: ++ id: 27a84062-b91f-447d-a9a7-e184df2d9ed5 ++ health: HEALTH_WARN ++ clock skew detected on mon.ceph2 ++ 1 pool(s) do not have an application enabled ++ ++ services: ++ mon: 3 daemons, quorum ceph1,ceph2,ceph3 (age 105m) ++ mgr: ceph1(active, since 28h), standbys: ceph2, ceph3 ++ osd: 3 osds: 3 up (since 28h), 3 in (since 28h) ++ ++ data: ++ pools: 2 pools, 33 pgs ++ objects: 2 objects, 577 KiB ++ usage: 414 MiB used, 2.6 GiB / 3.0 GiB avail ++ pgs: 33 active+clean ++ ++``` ++# 部署 copilot ++* 详细安装方法参见[EulerCopilot Tune 安装使用指南](../../README.md) ++ ++## 1. 下载 copilot 源码 ++```bash ++git clone https://gitee.com/openeuler/A-Tune.git ++cd A-Tune/ ++# 切换到 euler-copilot-tune 分支 ++git checkout euler-copilot-tune ++``` ++## 2. 安装系统依赖 ++```bash ++pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ++``` ++## 3. 修改配置文件 ++* __环境信息 config/.env.yaml 配置__ ++```YAML ++# LLM 服务配置示例 ++# 根据实际使用的模型服务(如 OpenAI、通义千问、deepseek 等)填写以下字段 ++LLM_KEY: "sk-XXXXXX" # 必填:模型服务的 API 密钥 ++LLM_URL: "https://api.deepseek.com" # 必填:LLM 服务的 API 接口地址,如 "https://api.deepseek.com" ++LLM_MODEL_NAME: "deepseek-chat" # 必填:要调用的模型名,如 deepseek-chat ++LLM_MAX_TOKENS: # 选填:生成文本的最大 token 数,如512或2048 ++ ++# 部署应用的机器 ip 信息(重点补充 ip、host_user、password) ++servers: ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "ceph" ++ target_process_name: "rados" ++ business_context: "Ceph 客户端节点,负责运行管理命令和压测" ++ max_retries: 3 ++ delay: 1.0 ++ ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "ceph-osd,ceph-mon,ceph-mgr" ++ target_process_name: "ceph-osd" ++ business_context: "Ceph 存储节点,负责存储对象数据,并运行 mon/mgr 保障集群一致性和管理" ++ max_retries: 3 ++ delay: 1.0 ++ ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "ceph-osd,ceph-mon,ceph-mgr" ++ target_process_name: "ceph-osd" ++ business_context: "Ceph 存储节点,负责存储对象数据,并运行 mon/mgr 保障集群一致性和管理" ++ max_retries: 3 ++ delay: 1.0 ++ ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "ceph-osd,ceph-mon,ceph-mgr" ++ target_process_name: "ceph-osd" ++ business_context: "Ceph 存储节点,负责存储对象数据,并运行 mon/mgr 保障集群一致性和管理" ++ max_retries: 3 ++ delay: 1.0 ++``` ++* __应用部署信息 config/app_config.yaml 配置__ ++需按实际环境填写;一般无需修改,若部署方式不同需要修改对应命令。 ++```YAML ++ceph: ++ set_param_template: 'ceph config set osd "$param_name" "$param_value"' ++ get_param_template: 'ceph config dump | grep "$param_name" | sed "s/.*$param_name[[:space:]]*\([0-9]*\)/\1/"' ++ stop_workload: "systemctl restart ceph.target" ++ start_workload: "systemctl restart ceph.target" ++ benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/ceph/parse_benchmark.sh" ++ performance_metric: "BANDWIDTH" ++``` ++* __scripts/ceph/benchmark.sh 脚本内容:__ ++```YAML ++PG_NUMBER=200 ++PGP_NUMBER=200 ++ ++echo "ceph benchmark start prep data." ++if ceph osd pool ls |grep -q "^testbench$"; then ++ echo "[WARN] pool testbench exist !!!" ++else ++ echo "[INFO] start create testbench ..." ++ ceph osd pool create testbench ${PG_NUMBER} ${PGP_NUMBER} ++ if [ $? -eq 0 ]; then ++ echo "[INFO] Create testbench success ." ++ else ++ echo "[WARN] osd pool create testbench failed." ++ fi ++fi ++ ++rados -p testbench bench 60 write > ceph_iops_bandwidth_write.log # write benchmark ++# rados bench -p testbench 10 rand > ceph_iops_bandwidth_write.log # random read benchmark, need write first ++count=0 ++while true ++do ++ bandwidth_val=$(cat ceph_iops_bandwidth_write.log |grep "Bandwidth (MB/sec):" | awk -F ':' '{print $2}') ++ iops_val=$(cat ceph_iops_bandwidth_write.log |grep "Average IOPS:" | awk -F ':' '{print $2}') ++ if [ -z "${bandwidth_val}" ] || [ -z "${iops_val}" ]; then ++ if [ ${count} -eq 10 ]; then ++ echo "[ERROR] get val has reach 10 times, end !!!" ++ break ++ else ++ count=$((${count}+1)) ++ sleep 3 ++ fi ++ break ++ else ++ echo "${bandwidth_val} and ${iops_val}" ++ break ++ fi ++done ++echo 'bandwidth_val = '${bandwidth_val} ++echo 'iops_val = '${iops_val} ++``` ++ ++## 4. 执行调优程序 ++```bash ++# 在源码根目录执行 ++export PYTHONPATH="`pwd`:$PYTHONPATH" ++python3 src/start_tune.py ++``` +\ No newline at end of file +diff --git a/copilot-tune/doc/zh/mysql.md b/copilot-tune/doc/zh/mysql.md +new file mode 100644 +index 0000000..f1f3f44 +--- /dev/null ++++ b/copilot-tune/doc/zh/mysql.md +@@ -0,0 +1,228 @@ ++# MySQL 安装与配置 ++## 1. 安装 MySQL 数据库 ++ ++``` ++yum install -y mysql mysql-devel mysql-server ++``` ++## 2. 创建运行目录 ++ ++``` ++mkdir -p /usr/local/mysql/{data,tmp,run,log} ++``` ++ ++## 3. 修改目录属组 ++ ++``` ++chown -R mysql:mysql /usr/local/mysql ++``` ++ ++## 4. 清理数据 ++ ++``` ++rm -rf /usr/local/mysql/data/* ++``` ++ ++## 5. 将可执行文件目录加入到 PATH ++ ++``` ++export PATH=`echo $PATH`:/usr/local/mysql/bin ++``` ++ ++## 6. 初始化 MySQL 数据库 ++ ++``` ++mysqld --user=root --initialize-insecure ++``` ++ ++## 7. 启动 MySQL ++ ++``` ++chown -R mysql:mysql /var/lib/mysql ++chown -R mysql:mysql /run/mysqld/ ++systemctl start mysqld ++``` ++ ++## 8. 数据库配置修改 ++ ++``` ++mysql -uroot << EOF ++alter user 'root'@'localhost' identified by '123456'; ++flush privileges; ++use mysql; ++update user set host='%' where user='root'; ++flush privileges; ++create database sbtest; ++quit ++EOF ++``` ++ ++## 9. 重载配置参数 ++ ++``` ++systemctl daemon-reload ++``` ++ ++# Sysbench 安装与基线测试 ++## 1. 源码下载编译安装 ++ ++``` ++yum install -y git ++git clone --depth=1 https://github.com/akopytov/sysbench.git ++cd sysbench ++yum install -y automake libtool ++./autogen.sh ++./configure ++make -j ++make install ++``` ++ ++## 2. 查看版本 ++ ++``` ++sysbench --version ++``` ++ ++## 3. 基线测试命令 ++ ++``` ++sh -x benchmark.sh 127.0.0.1 3306 root 123456 ++``` ++ ++benchmark.sh 内容 ++ ++``` ++# 1.prepare 阶段 ++ ++echo "sysbench prepare" ++sysbench \ ++ --db-driver=mysql \ ++ --mysql-host=$1 \ ++ --mysql-port=$2 \ ++ --mysql-user=$3 \ ++ --mysql-password=$4 \ ++ --mysql-db=sbtest \ ++ --table_size=10000 \ ++ --tables=10 \ ++ --time=180 \ ++ --threads=96 \ ++ --report-interval=10 \ ++ oltp_read_write \ ++ prepare ++ ++ ++# 2.copilot调优信号发送 ++echo "发送调优信号1" ++echo 1 > /tmp/euler-copilot-fifo ++ ++ ++# 3.sysbench压测开始 ++echo "压测开始" ++rm -rf benchmark.log ++ ++sysbench \ ++ --mysql-host=$1 \ ++ --mysql-port=$2 \ ++ --mysql-user=$3 \ ++ --mysql-password=$4 \ ++ --mysql-db=sbtest \ ++ --mysql-storage-engine=innodb \ ++ --mysql-ignore-errors=1062,1213,1205,1020 \ ++ --table-size=10000 \ ++ --tables=10 \ ++ --time=180 \ ++ --events=0 \ ++ --report-interval=1 \ ++ --rand-type=uniform \ ++ --rand-seed=100 \ ++ --db-driver=mysql \ ++ --percentile=95 \ ++ --forced-shutdown=off \ ++ --db-ps-mode=disable \ ++ --threads=128 \ ++ oltp_write_only \ ++ run 2>&1 | tee benchmark.log ++ ++# 4.sysbench清理工作 ++echo "开始清理工作" ++sysbench \ ++ --db-driver=mysql \ ++ --mysql-host=$1 \ ++ --mysql-port=$2 \ ++ --mysql-user=$3 \ ++ --mysql-password=$4 \ ++ --mysql-db=sbtest \ ++ --table_size=10000 \ ++ --tables=10 \ ++ --time=180 \ ++ --threads=96 \ ++ --report-interval=10 \ ++ oltp_read_write \ ++ cleanup ++ ++``` ++# 部署 copilot ++* 详细安装方法参见[EulerCopilot Tune 安装使用指南](../../README.md) ++ ++## 1. 下载 copilot 源码 ++```bash ++git clone https://gitee.com/openeuler/A-Tune.git ++cd A-Tune/ ++# 切换到 euler-copilot-tune 分支 ++git checkout euler-copilot-tune ++``` ++## 2. 安装系统依赖 ++```bash ++pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ++``` ++## 3. 修改配置文件 ++* __环境信息 config/.env.yaml 配置__ ++```YAML ++# LLM 服务配置示例 ++# 根据实际使用的模型服务(如 OpenAI、通义千问、deepseek 等)填写以下字段 ++LLM_KEY: "sk-XXXXXX" # 必填:模型服务的 API 密钥 ++LLM_URL: "https://api.deepseek.com" # 必填:LLM 服务的 API 接口地址,如 "https://api.deepseek.com" ++LLM_MODEL_NAME: "deepseek-chat" # 必填:要调用的模型名,如 deepseek-chat ++LLM_MAX_TOKENS: # 选填:生成文本的最大 token 数,如512或2048 ++ ++# 部署应用的机器 ip 信息(重点补充 ip、host_user、password) ++servers: ++ - ip: "" #应用所在ip ++ host_user: "" #登录机器的usr id ++ password: "" #登录机器的密码 ++ port: 22 #应用所在ip的具体port ++ app: "mysql" #当前支持mysql、nginx、pgsql、spark ++ target_process_name: "mysqld" #调优应用的name ++ business_context: "高并发数据库服务,CPU负载主要集中在用户态处理" #调优应用的描述(用于策略生成) ++ max_retries: 3 ++ delay: 1.0 ++``` ++* __应用部署信息 config/app_config.yaml 配置__ ++需按实际环境填写;一般无需修改,若部署方式不同需要修改对应命令。 ++```YAML ++mysql: ++ user: "root" ++ password: "123456" ++ config_file: "/etc/my.cnf" ++ port: 3306 ++ set_param_template: 'grep -q "^$param_name\\s*=" "$config_file" && sed -i "s/^$param_name\\s*=.*/$param_name = $param_value/" "$config_file" || sed -i "/\\[mysqld\\]/a $param_name = $param_value" "$config_file"' ++ get_param_template: 'grep -E "^$param_name\s*=" $config_file | cut -d= -f2- | xargs' ++ stop_workload: "systemctl stop mysqld" ++ start_workload: "systemctl start mysqld" ++ benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/mysql/parse_benchmark.sh 127.0.0.1 3306 root 123456" ++ performance_metric: "QPS" ++``` ++ ++* __scripts/mysql/benchmark.sh 脚本内容:__ ++```YAML ++sysbench --db-driver=mysql --mysql-host=$1 --mysql-port=$2 --mysql-user=$3 --mysql-password=$4 --mysql-db=sbtest --table_size=5000 --tables=10 --time=180 --threads=96 --report-interval=10 oltp_read_write prepare ++echo 1 > /tmp/euler-copilot-fifo" ++sysbench --mysql-host=$1 --mysql-port=$2 --mysql-user=$3 --mysql-password=$4 --mysql-db=sbtest --mysql-storage-engine=innodb --mysql-ignore-errors=1062,1213,1205,1020 --tables=10 --table-size=5000 --time=180 --events=0 --report-interval=1 --rand-type=uniform --db-driver=mysql --percentile=95 oltp_read_write --forced-shutdown=off --db-ps-mode=disable --threads=128 run ++sysbench --db-driver=mysql --mysql-host=$1 --mysql-port=$2 --mysql-user=$3 --mysql-password=$4 --mysql-db=sbtest --table_size=5000 --tables=10 --time=180 --threads=96 --report-interval=10 oltp_read_write cleanup ++``` ++ ++## 4. 执行调优程序 ++```bash ++# 在源码根目录执行 ++export PYTHONPATH="`pwd`:$PYTHONPATH" ++python3 src/start_tune.py ++``` +\ No newline at end of file +diff --git a/copilot-tune/doc/zh/nginx.md b/copilot-tune/doc/zh/nginx.md +new file mode 100644 +index 0000000..7478e87 +--- /dev/null ++++ b/copilot-tune/doc/zh/nginx.md +@@ -0,0 +1,276 @@ ++# Nginx 应用验证 ++ ++## 安装应用 & 环境准备 ++### 1. 安装 Nginx ++* __安装依赖 & 下载 Nginx 源码__ ++```bash ++# 安装依赖 ++yum install -y openssl openssl-devel pcre pcre-devel zlib zlib-devel gcc make ++ ++# 若服务器可以访问网络,通过 wget 命令直接下载 Nginx 源码,再将 Nginx 源码上传到虚拟机的“/home”目录。 ++wget https://nginx.org/download/nginx-1.21.5.tar.gz --no-check-certificate ++``` ++ ++### 2. 部署 Nginx ++```bash ++tar -zxvf nginx-1.21.5.tar.gz ++cd nginx-1.21.5/ ++chmod 755 configure ++./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module ++make -j 60 && make install ++ln -s /usr/local/nginx/sbin/nginx /usr/bin/nginx ++# 创建nginx用户 ++useradd -s /sbin/nologin -M nginx ++``` ++ ++### 3. 生成 OpenSLL 证书 ++* __进入 "/usr/local/nginx" 目录,生成 RSA 密钥__(对于 CentOS 7.6 或 CentOS 8.1 下通过镜像站 RPM 包安装的 Nginx,目录需替换为 "/etc/nginx" ) ++```bash ++cd /usr/local/nginx ++openssl genrsa -des3 -out server_2048.key 2048 ++# 此处系统会提示需要两次输入密码,请设置为相同的密码,完成后会生成 server_2048.key 文件。 ++ ++# 设置完成后可通过以下命令实现免密使用 server_2048.key 文件。 ++openssl rsa -in server_2048.key -out server_2048.key ++``` ++* __创建服务器证书的申请文件__ ++```bash ++openssl req -new -key server_2048.key -out server_2048.csr ++# Country Name 填写 CN,其他选项直接回车 ++``` ++* __重写 RSA 密钥并生成证书__ ++```bash ++openssl rsa -in server_2048.key -out server_2048.key ++openssl x509 -req -days 365 -in server_2048.csr -signkey server_2048.key -out server_2048.crt ++``` ++*注意:若生成 OpenSSL 证书时,提示 "unable to find 'distinguished_name' in config" ,说明与验证 KAE 性能时执行的 export OPENSSL_CONF=/home/openssl.cnf 命令冲突,请参见[部署 vKAE 特性时,在虚拟机部署 Nginx 过程中生成 OpenSSL 证书时报错](https://www.hikunpeng.com/document/detail/zh/kunpengcpfs/systuningguide/systemtg/kunpengvkae_20_022.html)解决该问题。 ++ ++### 4. 运行nginx ++启动nginx:(监听端口见配置文件中的listen关键字) ++```bash ++nginx -c /usr/local/nginx/conf/nginx.conf ++``` ++停止/重载nginx配置: ++```bash ++nginx -s stop ++nginx -s reload ++``` ++  ++### 5. 配置使能 KAE + Nginx 的同步模式 ++* 对于不使能 KAE,以及使能 KAE + Nginx 的异步模式的配置详情,请参见[部署 Ngnix 指南](https://www.hikunpeng.com/document/detail/zh/kunpengcpfs/basicAccelFeatures/comAccel/kunpengvkae_20_016.html)。 ++* __在“usr/local/nginx/conf”目录下创建一个名为 nginx_kae.conf 的配置文件,配置内容如下:__ ++```YAML ++user root; ++worker_processes auto; ++#4-7 ++#worker_cpu_affinity ++#10000 ++#100000 ++#1000000 ++#10000000 ++#; ++#daemon off; ++error_log /dev/null; ++ ++worker_rlimit_nofile 102400; ++events { ++ use epoll; ++ worker_connections 102400; ++ accept_mutex off; ++ multi_accept on; ++} ++ ++http { ++ include mime.types; ++ default_type application/octet-stream; ++ #log_format main '$remote_addr - $remote_user [$time_local] $request_time "$request" ' ++ # '$status $body_bytes_sent $request_length $bytes_sent "$http_referer" ' ++ # '"$http_user_agent" "$http_x_forwarded_for"'; ++ #access_log logs/access.log main; ++ access_log off; ++ ++ sendfile on; ++ tcp_nopush on; ++ tcp_nodelay on; ++ server_tokens off; ++ sendfile_max_chunk 512k; ++ keepalive_timeout 65; ++ keepalive_requests 20000; ++ client_header_buffer_size 4k; ++ large_client_header_buffers 4 32k; ++ server_names_hash_bucket_size 128; ++ client_max_body_size 100m; ++ open_file_cache max=102400 inactive=40s; ++ open_file_cache_valid 50s; ++ open_file_cache_min_uses 1; ++ open_file_cache_errors on; ++ #gzip on; ++ ++ server { ++ listen 10000 reuseport; ++ server_name localhost; ++ ++ #charset koi8-r; ++ ++ #access_log logs/host.access.log main; ++ ++ location / { ++ root html; ++ index index.html index.htm; ++ } ++ ++ #error_page 404 /404.html; ++ ++ # redirect server error pages to the static page /50x.html ++ # ++ error_page 500 502 503 504 /50x.html; ++ location = /50x.html { ++ root html; ++ } ++ ++ } ++ # HTTPS server ++ # ++ server { ++ listen 20000 ssl reuseport; ++ server_name localhost; ++ ++ ssl_certificate /usr/local/nginx/server_2048.crt; ++ ssl_certificate_key /usr/local/nginx/server_2048.key; ++ ++ ssl_session_cache shared:SSL:1m; ++ ssl_session_timeout 5m; ++ ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ++ ssl_ciphers AES256-GCM-SHA384; ++ ssl_prefer_server_ciphers on; ++ ssl_session_tickets off; ++ location / { ++ root html; ++ index index.html index.htm; ++ } ++ access_log off; ++ } ++ ++} ++``` ++* __运行使能 KAE+ 参数调优过的 Nginx 同步模式的配置文件__ ++```bash ++/usr/local/nginx/sbin/nginx -s stop || true; sleep 1; ++OPENSSL_CONF=/home/openssl.cnf /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx_kae.conf ++``` ++ ++## 安装测压软件 ++### 1. 安装编译依赖 ++```bash ++yum install -y gnutls-devel libev-devel ++``` ++### 2. 下载源码包 ++```bash ++git clone https://github.com/yarosla/httpress.git ++``` ++### 3. 编译并安装 ++```bash ++# 进入目录并编译 ++cd httpress && make ++# 安装到系统路径 ++cp bin/Release/httpress /usr/bin ++``` ++ ++### 4. 测试httpress ++```bash ++TARGET_HOST=localhost ++TARGET_PORT=80 ++# 自动取配置文件中的监听端口: ++# TARGET_PORT=`grep -E -e '^[ ]+listen' /usr/local/nginx/conf/nginx.conf | head -1 | awk '{print $2}' | tr -d ';'` ++httpress -n 200000 -c 512 -t 7 -k http://${TARGET_HOST}:${TARGET_PORT} ++``` ++ ++## 部署 copilot ++* 详细安装方法参见[EulerCopilot Tune 安装使用指南](../../README.md) ++ ++### 1. 下载 copilot 源码 ++```bash ++git clone https://gitee.com/openeuler/A-Tune.git ++cd A-Tune/ ++# 切换到 euler-copilot-tune 分支 ++git checkout euler-copilot-tune ++``` ++### 2. 安装系统依赖 ++```bash ++pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ++``` ++### 3. 修改配置文件 ++* __环境信息 config/.env.yaml 配置__ ++```YAML ++# LLM 服务配置示例 ++# 根据实际使用的模型服务(如 OpenAI、通义千问、deepseek 等)填写以下字段 ++LLM_KEY: "sk-XXXXXX" # 必填:模型服务的 API 密钥 ++LLM_URL: "https://api.deepseek.com" # 必填:LLM 服务的 API 接口地址,如 "https://api.deepseek.com" ++LLM_MODEL_NAME: "deepseek-chat" # 必填:要调用的模型名,如 deepseek-chat ++LLM_MAX_TOKENS: # 选填:生成文本的最大 token 数,如512或2048 ++ ++# 部署应用的机器 ip 信息(重点补充 ip、host_user、password) ++servers: ++ - ip: "" # 必填:服务器 IP ++ host_user: "" # 必填:主机用户名 ++ password: "" # 必填:登录密码 ++ port: 22 # SSH端口,默认22 ++ app: "nginx" ++ listening_address: "127.0.0.1" # 必填:服务监听地址 ++ listening_port: "10000" # 必填:服务监听端口 ++ target_process_name: "nginx" ++ business_context: "高并发Web服务,CPU负载主要集中在用户态处理" ++ max_retries: 3 ++ delay: 1.0 ++``` ++* __应用部署信息 config/app_config.yaml 配置__ ++需按实际环境填写;一般无需修改,若部署方式不同需要修改对应命令。 ++```YAML ++nginx: ++ port: 10000 ++ config_file: "/usr/local/nginx/conf/nginx.conf" ++ # 设置某个参数的模板 ++ set_param_template: 'grep -q "^\\s*$param_name\\s\\+" "$config_file" && sed -i "s|^\\s*$param_name\\s\\+.*| $param_name $param_value;|" "$config_file" || sed -i "/http\\s*{/a\ $param_name $param_value;" "$config_file"' ++ # 获取某个参数的模板 ++ get_param_template: 'grep -E "^\\s*$param_name\\s+" $config_file | head -1 | sed -E "s/^\\s*$param_name\\s+(.*);/\\1/"' ++ stop_workload: "/usr/local/nginx/sbin/nginx -s reload" ++ start_workload: "/usr/local/nginx/sbin/nginx -s reload" ++ # 压测脚本 ++ benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/nginx/parse_benchmark.sh $host_ip $port" ++ performance_metric: "QPS" ++ ++``` ++ ++* __scripts/nginx/benchmark.sh 脚本内容:__ ++```YAML ++#!/bin/sh ++echo 1 > /tmp/euler-copilot-fifo ++ ++retry_num=0 ++csv_file="rps_result_matuner.csv" ++ ++# 初始化 CSV 文件,添加表头(如果不存在) ++if [ ! -f "$csv_file" ]; then ++ echo "rps,kbps" > "$csv_file" ++fi ++while [ $retry_num -le 5 ] ++do ++ httpress -n 20000000 -c 512 -t 7 -k http://localhost:10000 > benchmark_result 2>&1 ++ if [ $? == 0 ];then ++ cat benchmark_result ++ rps=$(cat benchmark_result | grep 'TIMING:' | awk '{print $4}') ++ kbps=$(cat benchmark_result | grep 'TIMING:' | awk '{print $6}') ++ ++ echo "${rps},${kbps}" >> "$csv_file" ++ break ++ fi ++ retry_num=$((retry_num + 1)) ++done ++``` ++ ++### 4. 执行调优程序 ++```bash ++# 在源码根目录执行 ++export PYTHONPATH="`pwd`:$PYTHONPATH" ++python3 src/start_tune.py ++``` +\ No newline at end of file +diff --git a/copilot-tune/doc/zh/oceanbase.md b/copilot-tune/doc/zh/oceanbase.md +new file mode 100644 +index 0000000..f94b0ba +--- /dev/null ++++ b/copilot-tune/doc/zh/oceanbase.md +@@ -0,0 +1,128 @@ ++# oceanbase部署文档(包含sysbench压测命令) ++https://www.hikunpeng.com/document/detail/zh/kunpengdbs/ecosystemEnable/OceanBase/kunpengocenbase_04_0001.html ++# 部署 copilot ++* 详细安装方法参见[EulerCopilot Tune 安装使用指南](../../README.md) ++ ++## 1. 下载 copilot 源码 ++```bash ++git clone https://gitee.com/openeuler/A-Tune.git ++cd A-Tune/ ++# 切换到 euler-copilot-tune 分支 ++git checkout euler-copilot-tune ++``` ++## 2. 安装系统依赖 ++```bash ++pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ++``` ++## 3. 修改配置文件 ++* __环境信息 config/.env.yaml 配置__ ++```YAML ++# LLM 服务配置示例 ++# 根据实际使用的模型服务(如 OpenAI、通义千问、deepseek 等)填写以下字段 ++LLM_KEY: "sk-XXXXXX" # 必填:模型服务的 API 密钥 ++LLM_URL: "https://api.deepseek.com" # 必填:LLM 服务的 API 接口地址,如 "https://api.deepseek.com" ++LLM_MODEL_NAME: "deepseek-chat" # 必填:要调用的模型名,如 deepseek-chat ++LLM_MAX_TOKENS: # 选填:生成文本的最大 token 数,如512或2048 ++ ++# 部署应用的机器 ip 信息(重点补充 ip、host_user、password) ++servers: ++ # OBServer 节点 1 ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "oceanbase" ++ target_process_name: "observer" ++ business_context: "OceanBase Server 节点 1,负责数据存储与计算" ++ max_retries: 3 ++ delay: 1.0 ++ ++ # OBServer 节点 2 ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "oceanbase" ++ target_process_name: "observer" ++ business_context: "OceanBase Server 节点 2,负责数据存储与计算" ++ max_retries: 3 ++ delay: 1.0 ++ ++ # OBServer 节点 3 ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "oceanbase" ++ target_process_name: "observer" ++ business_context: "OceanBase Server 节点 3,负责数据存储与计算" ++ max_retries: 3 ++ delay: 1.0 ++ ++ # Proxy 节点 ++ - ip: "" ++ host_user: "" ++ password: "" ++ port: 22 ++ app: "oceanbase" ++ target_process_name: "obproxy" ++ business_context: "OceanBase Proxy 节点,负责 SQL 路由与连接负载均衡" ++ max_retries: 3 ++ delay: 1.0 ++``` ++* __应用部署信息 config/app_config.yaml 配置__ ++需按实际环境填写;一般无需修改,若部署方式不同需要修改对应命令。 ++```YAML ++oceanbase: ++ user: "root@sysbench_tenant" ++ password: "" ++ config_file: "/root/.obd/cluster/obcluster/config.yaml" ++ port: 2881 ++ set_param_template: "sh xxx/set_param.sh $param_name $param_value" ++ get_param_template: "grep -P '$param_name' $config_file | awk '{print $2;exit}'" ++ stop_workload: "obd cluster stop obcluster" ++ start_workload: "obd cluster start obcluster" ++ benchmark: "$EXECUTE_MODE:local sh $SCRIPTS_DIR/oceanbase/parse_benchmark.sh" ++ performance_metric: "QPS" ++``` ++* set_param.sh 脚本内容: ++```YAML ++#!/bin/bash ++# 需要把该脚本移动至oceanbase应用部署机器上 ++ ++param_name=$1 ++param_value=$2 ++config_file="/root/.obd/cluster/obcluster/config.yaml" ++ ++# 如果参数已存在,只修改第一次出现的地方 ++if grep -q "^[[:space:]]*$param_name:" "$config_file"; then ++ sed -i "0,/^[[:space:]]*$param_name:.*/s// $param_name: $param_value/" "$config_file" ++else ++ # 如果不存在,在第一个 global: 后插入 ++ awk -v key="$param_name" -v val="$param_value" ' ++ BEGIN {inserted=0} ++ /^ global:/ { ++ print ++ if (!inserted) { ++ print " " key ": " val ++ inserted=1 ++ } ++ next ++ } ++ {print} ++ ' "$config_file" > /tmp/tmp_conf && mv /tmp/tmp_conf "$config_file" ++fi ++``` ++ ++* __scripts/oceanbase/benchmark.sh 脚本内容:__ ++```YAML ++echo 1 > /tmp/euler-copilot-fifo ++obd test sysbench obcluster --tenant=sysbench_tenant --script-name=oltp_read_only.lua --tables=30 --table-size=10000 --threads=32 ++``` ++ ++## 4. 执行调优程序 ++```bash ++# 在源码根目录执行 ++export PYTHONPATH="`pwd`:$PYTHONPATH" ++python3 src/start_tune.py ++``` +\ No newline at end of file +diff --git a/copilot-tune/doc/zh/spark.md b/copilot-tune/doc/zh/spark.md +new file mode 100644 +index 0000000..837d9b9 +--- /dev/null ++++ b/copilot-tune/doc/zh/spark.md +@@ -0,0 +1,209 @@ ++# Spark部署文档(包含benchmark测试脚本) ++https://www.hikunpeng.com/document/detail/zh/kunpengbds/ecosystemEnable/Spark/kunpengspark_04_0001.html ++# 部署 copilot ++* 详细安装方法参见[EulerCopilot Tune 安装使用指南](../../README.md) ++ ++## 1. 下载 copilot 源码 ++```bash ++git clone https://gitee.com/openeuler/A-Tune.git ++cd A-Tune/ ++# 切换到 euler-copilot-tune 分支 ++git checkout euler-copilot-tune ++``` ++## 2. 安装系统依赖 ++```bash ++pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple ++``` ++## 3. 修改配置文件 ++* __环境信息 config/.env.yaml 配置__ ++```YAML ++# LLM 服务配置示例 ++# 根据实际使用的模型服务(如 OpenAI、通义千问、deepseek 等)填写以下字段 ++LLM_KEY: "sk-XXXXXX" # 必填:模型服务的 API 密钥 ++LLM_URL: "https://api.deepseek.com" # 必填:LLM 服务的 API 接口地址,如 "https://api.deepseek.com" ++LLM_MODEL_NAME: "deepseek-chat" # 必填:要调用的模型名,如 deepseek-chat ++LLM_MAX_TOKENS: # 选填:生成文本的最大 token 数,如512或2048 ++ ++# 部署应用的机器 ip 信息(重点补充 ip、host_user、password) ++servers: ++ - ip: "" # 服务器 IP ++ host_user: "" # 登录用户 ++ password: "" # 登录密码(如果用密钥可留空) ++ port: 22 # SSH 端口(默认 22) ++ app: "spark" # 应用类型(这里是 Spark) ++ target_process_name: "java" # Spark 进程名,一般是 java ++ business_context: "基于内存计算的分布式批处理框架,适合大规模数据处理任务,CPU负载主要集中在用户态的序列化、>反序列化及任务调度执行过程" # 业务上下文描述(例如:Spark SQL Benchmark) ++ max_retries: 3 # 最大重试次数 ++ delay: 1.0 # 重试间隔(秒) ++``` ++* __应用部署信息 config/app_config.yaml 配置__ ++需按实际环境填写;一般无需修改,若部署方式不同需要修改对应命令。 ++```YAML ++spark: ++ set_param_template: 'sh /path/of/set_param.sh $param_name $param_value' ++ get_param_template: 'sh /path/of/get_param.sh $param_name' ++ benchmark: "sh /path/of/spark_benchmark.sh" ++ performance_metric: "DURATION" ++``` ++* set_param.sh 脚本内容: ++```YAML ++#!/bin/bash ++ ++param="$1" ++value="$2" ++config_file="/xxx/spark_test/spark_params.sh" ++ ++# 参数检查 ++if [ -z "$param" ] || [ -z "$value" ]; then ++ echo "Usage: $0 " ++ echo "Examples:" ++ echo " $0 driver-cores 16" ++ echo " $0 spark.sql.shuffle.partitions 500" ++ exit 1 ++fi ++ ++# 参数名合法性检查(允许 spark.xxx 或 xxx) ++if ! [[ "$param" =~ ^[a-zA-Z0-9._-]+$ ]]; then ++ echo "Error: Invalid parameter name: $param" ++ exit 1 ++fi ++ ++# 文件存在性检查 ++if [ ! -f "$config_file" ]; then ++ echo "Error: $config_file not found!" ++ exit 1 ++fi ++ ++# 创建临时文件并设置清理 ++temp_file=$(mktemp) ++trap 'rm -f "$temp_file"' EXIT ++ ++# 转义参数名(用于正则匹配) ++escaped_param=$(printf '%s' "$param" | sed 's/[.[\*^$()+?{|]/\\&/g') ++ ++# 标记是否已替换 ++found=0 ++ ++# 缩进:我们使用 2 个空格(根据你的文件结构) ++indent=" " ++ ++# 遍历每一行,安全处理无换行结尾 ++while IFS= read -r line || [ -n "$line" ]; do ++ ++ # 情况1: --conf spark.xxx=... ++ if [[ "$line" =~ ^[[:space:]]*--conf[[:space:]]+\"?$escaped_param= ]]; then ++ if [ $found -eq 0 ]; then ++ echo "${indent}--conf $param=$value" >> "$temp_file" ++ found=1 ++ else ++ echo "# Removed duplicate: $line" >&2 ++ fi ++ continue ++ fi ++ ++ # 情况2: --param value (如 --driver-cores 8) ++ # 构造匹配模式:--driver-cores 后跟空格或tab,再跟任意值 ++ if [[ "$line" =~ ^[[:space:]]*--$escaped_param[[:space:]]+[^[:space:]]+ ]]; then ++ if [ $found -eq 0 ]; then ++ echo "${indent}--$param $value" >> "$temp_file" ++ found=1 ++ else ++ echo "# Removed duplicate: $line" >&2 ++ fi ++ continue ++ fi ++ ++ # 检查是否是 SPARK_OPERATOR_PARAMS 的结束括号 ) ++ if [[ "$line" =~ ^[[:space:]]*\)[[:space:]]*$ ]] && [ $found -eq 0 ]; then ++ # 插入新参数 ++ if [[ "$param" == spark.* ]]; then ++ echo "${indent}--conf $param=$value" >> "$temp_file" ++ else ++ echo "${indent}--$param $value" >> "$temp_file" ++ fi ++ fi ++ ++ # 输出原行 ++ echo "$line" >> "$temp_file" ++ ++done < "$config_file" ++ ++# 确保文件以换行结尾 ++sed -i -e '$a\' "$temp_file" ++ ++# 替换原文件 ++mv "$temp_file" "$config_file" ++ ++echo "Updated '$param' to '$value' in $config_file" ++ ++``` ++* get_param.sh 脚本内容: ++```YAML ++#!/bin/bash ++ ++param="$1" ++execute_scripts="/xxx/spark_test/spark_params.sh" ++ ++if [ -z "$param" ]; then ++ echo "Usage: $0 " ++ echo "Examples:" ++ echo " $0 spark.sql.shuffle.partitions" ++ echo " $0 driver-cores" ++ echo " $0 executor-memory" ++ exit 1 ++fi ++ ++if [ ! -f "$execute_scripts" ]; then ++ echo "Error: Config file not found: $execute_scripts" >&2 ++ exit 1 ++fi ++ ++# 安全提取 SPARK_OPERATOR_PARAMS 内容 ++params=$(awk ' ++ /SPARK_OPERATOR_PARAMS=\(/, /\)/ { ++ if (!/SPARK_OPERATOR_PARAMS=\(/ && !/\)/) print ++ } ++' "$execute_scripts") ++ ++# 查找参数并提取值 ++while IFS= read -r line; do ++ [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue ++ ++ # 情况1: --conf spark.xxx=yyy ++ if [[ "$line" =~ --conf[[:space:]]*[\"\']?([a-zA-Z0-9._-]+)=([^\"]*) ]]; then ++ key="${BASH_REMATCH[1]}" ++ val="${BASH_REMATCH[2]}" ++ # ✅ 修复:一步删除末尾引号+空格 ++ val=$(echo "$val" | sed -e "s/[\"'][[:space:]]*\$//") ++ if [[ "$key" == "$param" ]]; then ++ echo "$val" ++ exit 0 ++ fi ++ fi ++ ++ # 情况2: --param value ++ if [[ "$line" =~ --([a-zA-Z0-9._-]+)[[:space:]]+([^[:space:]]+) ]]; then ++ key="${BASH_REMATCH[1]}" ++ val="${BASH_REMATCH[2]}" ++ if [[ "$key" == "$param" ]]; then ++ echo "$val" ++ exit 0 ++ fi ++ fi ++done <<< "$params" ++ ++echo "Error: Parameter '$param' not found." >&2 ++exit 1 ++ ++``` ++* __scripts/spark/benchmark.sh 脚本内容:__ ++```YAML ++cd /root/spark_auto_deploy_arm/spark_test && sh tpcds_test_1t_spark331_linearity_2p.sh ++``` ++ ++## 4. 执行调优程序 ++```bash ++# 在源码根目录执行 ++export PYTHONPATH="`pwd`:$PYTHONPATH" ++python3 src/start_tune.py ++``` +\ No newline at end of file +diff --git a/copilot-tune/requirements.txt b/copilot-tune/requirements.txt +new file mode 100644 +index 0000000..a487e1c +--- /dev/null ++++ b/copilot-tune/requirements.txt +@@ -0,0 +1,23 @@ ++faiss_cpu==1.8.0.post1 ++fastapi==0.112.2 ++numpy==1.26.4 ++openai==1.52.0 ++paramiko==3.4.1 ++pydantic>=2.8.2 ++PyYAML==6.0.2 ++scikit_learn==1.5.1 ++tqdm==4.66.4 ++uvicorn==0.30.6 ++zhipuai==2.1.4.20230814 ++requests==2.32.3 ++langchain==0.3.4 ++langchain-openai==0.2.3 ++email-validator==2.2.0 ++httpx==0.27.2 ++tabulate==0.9.0 ++pyfiglet==1.0.3 ++gssapi==1.9.0 ++pandas==2.3.1 ++mcp==1.6.0 ++json5>=0.12.1 ++wcwidth>=0.2.13 +\ No newline at end of file +diff --git a/copilot-tune/scripts/ceph/benchmark.sh b/copilot-tune/scripts/ceph/benchmark.sh +new file mode 100644 +index 0000000..b372677 --- /dev/null +++ b/copilot-tune/scripts/ceph/benchmark.sh @@ -0,0 +1,2 @@ @@ -1434,34 +3268,179 @@ index 0000000..cdc832f \ No newline at end of file diff --git a/copilot-tune/scripts/nginx/benchmark.sh b/copilot-tune/scripts/nginx/benchmark.sh new file mode 100644 -index 0000000..18940c3 +index 0000000..e381655 --- /dev/null +++ b/copilot-tune/scripts/nginx/benchmark.sh @@ -0,0 +1,5 @@ -+# 获取目标地址和端口 -+TARGET_HOST="$1" -+TARGET_PORT="$2" -+echo 1 > /tmp/euler-copilot-fifo ++# 获取目标地址和端口 ++TARGET_HOST="$1" ++TARGET_PORT="$2" ++echo 1 > /tmp/euler-copilot-fifo +httpress -n 20000000 -c 512 -t 7 -k http://${TARGET_HOST}:${TARGET_PORT} \ No newline at end of file diff --git a/copilot-tune/scripts/nginx/parse_benchmark.sh b/copilot-tune/scripts/nginx/parse_benchmark.sh new file mode 100644 -index 0000000..a5c2e28 +index 0000000..fcafb25 --- /dev/null +++ b/copilot-tune/scripts/nginx/parse_benchmark.sh @@ -0,0 +1,12 @@ ++#!/bin/bash ++ ++SCRIPT_PATH="$(realpath "$0")" ++SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" ++cd "$SCRIPT_DIR" ++ ++# 运行benchmark.sh,传入所有参数,输出重定向到benchmark.log ++sh benchmark.sh $1 $2 > benchmark.log 2>&1 ++ ++# 从benchmark.log中提取 rps(Requests per second) ++# 以 httpress 输出的 TIMING 行为例,第四个字段是 rps ++grep 'TIMING:' benchmark.log | awk '{print $4}' +\ No newline at end of file +diff --git a/copilot-tune/scripts/oceanbase/benchmark.sh b/copilot-tune/scripts/oceanbase/benchmark.sh +new file mode 100644 +index 0000000..1223f8b +--- /dev/null ++++ b/copilot-tune/scripts/oceanbase/benchmark.sh +@@ -0,0 +1,2 @@ ++echo 1 > /tmp/euler-copilot-fifo ++obd test sysbench obcluster --tenant=sysbench_tenant --script-name=oltp_read_only.lua --tables=30 --table-size=10000 --threads=32 +\ No newline at end of file +diff --git a/copilot-tune/scripts/oceanbase/get_param.sh b/copilot-tune/scripts/oceanbase/get_param.sh +new file mode 100644 +index 0000000..120852e +--- /dev/null ++++ b/copilot-tune/scripts/oceanbase/get_param.sh +@@ -0,0 +1,54 @@ +#!/bin/bash ++# 功能:根据参数 scope 自动选择 observer / obproxy 查询参数 ++# 用法: ++# 初始设置时 ++# ./get_param.sh ++# 参数获取时: ++# ./get_param.sh + ++PARAM=$1 ++OBSERVERIP=$2 ++OBSERVERPD=$3 ++PROXYIP=$4 ++PROXYPD=$5 ++COPILOTPATH=$6 ++ ++# ===== 用户配置区 ===== ++OBCLIENT_OBSERVER="obclient -h$OBSERVERIP -P2881 -uroot@sys -p$OBSERVERPD -Doceanbase -A" ++OBCLIENT_PROXY="obclient -h$PROXYIP -P2883 -uroot@proxysys -p$PROXYPD -Doceanbase -A" ++PARAM_FILE="$COPILOTPATH/A-Tune-euler-copilot-tune/src/knowledge_base/knob_params/oceanbase.json" ++# ======================= ++ ++if [ $# -lt 1 ]; then ++ echo "用法:$0 " ++ exit 1 ++fi ++ ++# 校验参数文件 ++if [ ! -f "$PARAM_FILE" ]; then ++ echo "错误:找不到参数文件 $PARAM_FILE" ++ exit 1 ++fi ++ ++# 从 JSON 提取 scope ++SCOPE=$(jq -r --arg name "$PARAM" '.[$name].scope' "$PARAM_FILE") ++ ++if [ "$SCOPE" == "null" ] || [ -z "$SCOPE" ]; then ++ echo "❌ 未在参数文件中找到 $PARAM" ++ exit 1 ++fi ++ ++# ---------- 查询 ---------- ++case "$SCOPE" in ++ observer) ++ REMOTE_CMD="$OBCLIENT_OBSERVER -e \"SHOW PARAMETERS LIKE '$PARAM';\" | awk 'NR>3 && \$1==\"zone2\"{print \$7}'" ++ ssh -q root@$OBSERVERIP "$REMOTE_CMD" ++ ;; ++ obproxy) ++ REMOTE_CMD="$OBCLIENT_PROXY -e \"SHOW PROXYCONFIG LIKE '$PARAM';\" | awk -v name=\"$PARAM\" '\$1==name{print \$2}'" ++ ssh -q root@$OBSERVERIP "$REMOTE_CMD" ++ ;; ++ *) ++ echo "❌ 未知scope类型:$SCOPE" ++ ;; ++esac +\ No newline at end of file +diff --git a/copilot-tune/scripts/oceanbase/parse_benchmark.sh b/copilot-tune/scripts/oceanbase/parse_benchmark.sh +new file mode 100644 +index 0000000..24ef2d7 +--- /dev/null ++++ b/copilot-tune/scripts/oceanbase/parse_benchmark.sh +@@ -0,0 +1,6 @@ ++#!/bin/bash +SCRIPT_PATH="$(realpath "$0")" +SCRIPT_DIR="$(dirname "$SCRIPT_PATH")" +cd "$SCRIPT_DIR" ++sh benchmark.sh > benchmark.log 2>&1 ++grep "queries:" benchmark.log | awk -F'[()]' '{print $2}' | awk '{print $1}' +\ No newline at end of file +diff --git a/copilot-tune/scripts/oceanbase/set_param.sh b/copilot-tune/scripts/oceanbase/set_param.sh +new file mode 100644 +index 0000000..fc7e85c +--- /dev/null ++++ b/copilot-tune/scripts/oceanbase/set_param.sh +@@ -0,0 +1,55 @@ ++#!/bin/bash ++# 功能:根据参数 scope 自动选择 observer / obproxy 设置参数 ++# 用法: ++# 初始设置时 ++# ./set_param.sh ++# 参数设置时: ++# ./set_param.sh + -+# 运行benchmark.sh,传入所有参数,输出重定向到benchmark.log -+sh benchmark.sh $1 $2 > benchmark.log 2>&1 ++PARAM=$1 ++VALUE=$2 ++OBSERVERIP=$3 ++OBSERVERPD=$4 ++PROXYIP=$5 ++PROXYPD=$6 ++COPILOTPATH=$7 + -+# 从benchmark.log中提取 rps(Requests per second) -+# 以 httpress 输出的 TIMING 行为例,第四个字段是 rps -+grep 'TIMING:' benchmark.log | awk '{print $4}' ++# ===== 用户配置区 ===== ++OBCLIENT_OBSERVER="obclient -h$OBSERVERIP -P2881 -uroot@sys -p$OBSERVERPD -Doceanbase -A" ++OBCLIENT_PROXY="obclient -h$PROXYIP -P2883 -uroot@proxysys -p$PROXYPD -Doceanbase -A" ++PARAM_FILE="$COPILOTPATH/A-Tune-euler-copilot-tune/src/knowledge_base/knob_params/oceanbase.json" ++# ======================= ++ ++if [ $# -lt 2 ]; then ++ echo "用法:$0 " ++ exit 1 ++fi ++ ++# 校验参数文件 ++if [ ! -f "$PARAM_FILE" ]; then ++ echo "错误:找不到参数文件 $PARAM_FILE" ++ exit 1 ++fi ++ ++# 从 JSON 提取 scope ++SCOPE=$(jq -r --arg name "$PARAM" '.[$name].scope' "$PARAM_FILE") ++ ++if [ "$SCOPE" == "null" ] || [ -z "$SCOPE" ]; then ++ echo "❌ 未在参数文件中找到 $PARAM" ++ exit 1 ++fi ++ ++# ---------- 设置 ---------- ++case "$SCOPE" in ++ observer) ++ REMOTE_CMD="$OBCLIENT_OBSERVER -e \"ALTER SYSTEM SET $PARAM = $VALUE;\"" ++ ssh -q root@$OBSERVERIP "$REMOTE_CMD" ++ ;; ++ obproxy) ++ REMOTE_CMD="$OBCLIENT_PROXY -e \"ALTER PROXYCONFIG SET $PARAM = $VALUE;\"" ++ ssh -q root@$OBSERVERIP "$REMOTE_CMD" ++ ;; ++ *) ++ echo "❌ 未知scope类型:$SCOPE" ++ ;; ++esac \ No newline at end of file diff --git a/copilot-tune/scripts/postgresql/benchmark.sh b/copilot-tune/scripts/postgresql/benchmark.sh new file mode 100644 @@ -1598,7 +3577,7 @@ index 0000000..f890a2d \ No newline at end of file diff --git a/copilot-tune/setup.py b/copilot-tune/setup.py new file mode 100644 -index 0000000..c78fb45 +index 0000000..2ad2b14 --- /dev/null +++ b/copilot-tune/setup.py @@ -0,0 +1,68 @@ @@ -1607,7 +3586,7 @@ index 0000000..c78fb45 +from setuptools import setup, find_packages +from glob import glob + -+cfg_path = "/etc/copilot-tune" ++cfg_path = "/etc/euler-copilot-tune" +for root, dirs, files in os.walk(cfg_path): + for file in files: + os.remove(os.path.join(root, file)) @@ -1633,27 +3612,27 @@ index 0000000..c78fb45 +knowledge_files = get_recursive_files_with_relpath(knowledge_src_root) +knowledge_data = [] +for src_file, rel_path in knowledge_files: -+ target_dir = os.path.join('/etc/copilot-tune/knowledge_base/', os.path.dirname(rel_path)) ++ target_dir = os.path.join('/etc/euler-copilot-tune/knowledge_base/', os.path.dirname(rel_path)) + knowledge_data.append((target_dir, [src_file])) +config_src_root = 'config' +config_files = get_recursive_files_with_relpath(config_src_root) +config_data = [] +for src_file, rel_path in config_files: -+ target_dir = os.path.join('/etc/copilot-tune/config/', os.path.dirname(rel_path)) ++ target_dir = os.path.join('/etc/euler-copilot-tune/config/', os.path.dirname(rel_path)) + config_data.append((target_dir, [src_file])) + +scripts_src_root = 'scripts' +scripts_files = get_recursive_files_with_relpath(scripts_src_root) +scripts_data = [] +for src_file, rel_path in scripts_files: -+ target_dir = os.path.join('/etc/copilot-tune/scripts/', os.path.dirname(rel_path)) ++ target_dir = os.path.join('/etc/euler-copilot-tune/scripts/', os.path.dirname(rel_path)) + scripts_data.append((target_dir, [src_file])) -+data_files = [('/etc/copilot-tune/config/', glob('config/*')), -+ ('/etc/copilot-tune/config/', glob('config/.env.yaml')), -+ ('/etc/copilot-tune/scripts/', glob('scripts/*/*')), ++data_files = [('/etc/euler-copilot-tune/config/', glob('config/*')), ++ ('/etc/euler-copilot-tune/config/', glob('config/.env.yaml')), ++ ('/etc/euler-copilot-tune/scripts/', glob('scripts/*/*')), + ('/usr/lib/systemd/system/', glob('service/*'))] + knowledge_data + config_data + scripts_data +setup( -+ name="copilot-tune", ++ name="euler-copilot-tune", + version="1.0", + author="xu hou", + author_email="houxu5@h-partners.com", @@ -1665,28 +3644,250 @@ index 0000000..c78fb45 + "console_scripts": [ + "tune-openapi = src.start_workflow:main", + "tune-mcpserver = src.start_mcpserver:main", -+ "copilot-tune = src.start_tune:main", ++ "euler-copilot-tune = src.start_tune:main", + ], + }, -+ url="https://gitee.com/openeuler/A-Tune/tree/copilot-tune/" ++ url="https://gitee.com/openeuler/A-Tune/tree/euler-copilot-tune/" +) diff --git a/copilot-tune/src/__init__.py b/copilot-tune/src/__init__.py new file mode 100644 -index 0000000..9e464c7 +index 0000000..81e8947 --- /dev/null +++ b/copilot-tune/src/__init__.py -@@ -0,0 +1,11 @@ +@@ -0,0 +1,13 @@ +import logging ++from src.config import config +from src.utils.common import display_banner + +logging.getLogger("paramiko.transport").propagate = False +display_banner() + ++level_name = str(config.__getitem__("log_level") or "INFO").upper() +logging.basicConfig( -+ level=logging.INFO, # 设置日志级别 -+ format="%(asctime)s - %(levelname)s [%(filename)s:%(funcName)s:%(lineno)d] - %(message)s", # 设置日志格式 -+ datefmt="%Y-%m-%d %H:%M:%S", # 设置时间格式 ++ level=getattr(logging, level_name, logging.INFO), ++ format="%(asctime)s - %(levelname)s - %(message)s", ++ datefmt="%Y-%m-%d %H:%M:%S", ++) +diff --git a/copilot-tune/src/check_tests/01_check_collect_static_metrics.py b/copilot-tune/src/check_tests/01_check_collect_static_metrics.py +new file mode 100644 +index 0000000..377bc40 +--- /dev/null ++++ b/copilot-tune/src/check_tests/01_check_collect_static_metrics.py +@@ -0,0 +1,10 @@ ++import json ++from src.check_tests.check_tests_utils import check_collect_static_metrics ++ ++def main(): ++ result = check_collect_static_metrics() ++ ++ print(f"{json.dumps(result, indent=4, ensure_ascii=False)}") ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/copilot-tune/src/check_tests/02_check_collect_runtime_metrics.py b/copilot-tune/src/check_tests/02_check_collect_runtime_metrics.py +new file mode 100644 +index 0000000..77be1be +--- /dev/null ++++ b/copilot-tune/src/check_tests/02_check_collect_runtime_metrics.py +@@ -0,0 +1,10 @@ ++import json ++from src.check_tests.check_tests_utils import check_collect_runtime_metrics ++ ++def main(): ++ result = check_collect_runtime_metrics() ++ ++ print(f"{json.dumps(result, indent=4, ensure_ascii=False)}") ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/copilot-tune/src/check_tests/03_check_analyze_performance.py b/copilot-tune/src/check_tests/03_check_analyze_performance.py +new file mode 100644 +index 0000000..f1117c3 +--- /dev/null ++++ b/copilot-tune/src/check_tests/03_check_analyze_performance.py +@@ -0,0 +1,9 @@ ++from src.check_tests.check_tests_utils import check_analyze_performance ++ ++def main(): ++ report, bottlenecksult = check_analyze_performance() ++ ++ print(f">>> PerformanceAnalyzer运行结果:\n{report}\n分析结论:\n{bottlenecksult}") ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/copilot-tune/src/check_tests/04_check_describe_param_background_knob.py b/copilot-tune/src/check_tests/04_check_describe_param_background_knob.py +new file mode 100644 +index 0000000..29656d6 +--- /dev/null ++++ b/copilot-tune/src/check_tests/04_check_describe_param_background_knob.py +@@ -0,0 +1,11 @@ ++from src.check_tests.check_tests_utils import check_describe_param_background_knob ++ ++def main(): ++ params_describe_list = check_describe_param_background_knob() ++ ++ params_describe_list_res = ("\n\n").join(params_describe_list) ++ ++ print(f"{params_describe_list_res}") ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/copilot-tune/src/check_tests/05_check_param_recommender.py b/copilot-tune/src/check_tests/05_check_param_recommender.py +new file mode 100644 +index 0000000..b614535 +--- /dev/null ++++ b/copilot-tune/src/check_tests/05_check_param_recommender.py +@@ -0,0 +1,9 @@ ++from src.check_tests.check_tests_utils import check_param_recommender ++ ++def main(): ++ result = check_param_recommender() ++ ++ print(f"{result}") ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/copilot-tune/src/check_tests/06_check_apply_params.py b/copilot-tune/src/check_tests/06_check_apply_params.py +new file mode 100644 +index 0000000..4be75ae +--- /dev/null ++++ b/copilot-tune/src/check_tests/06_check_apply_params.py +@@ -0,0 +1,7 @@ ++from src.check_tests.check_tests_utils import check_apply_params ++ ++def main(): ++ check_apply_params() ++ ++if __name__ == "__main__": ++ main() +\ No newline at end of file +diff --git a/copilot-tune/src/check_tests/check_tests_utils.py b/copilot-tune/src/check_tests/check_tests_utils.py +new file mode 100644 +index 0000000..cda4d3e +--- /dev/null ++++ b/copilot-tune/src/check_tests/check_tests_utils.py +@@ -0,0 +1,116 @@ ++from src.config import config ++from src.start_tune import ( ++ create_ssh_client, ++ collect_static_metrics, ++ collect_runtime_metrics, ++ analyze_performance +) ++from src.performance_optimizer.param_knowledge import ParamKnowledge ++from src.utils.config.app_config import AppInterface ++from src.performance_optimizer.param_recommender import ParamRecommender ++from src.performance_optimizer.param_optimizer import ParamOptimizer ++ ++def get_feature_cfg(): ++ return config["feature"][0] ++ ++def get_server_cfg(): ++ return config["servers"][0] ++ ++def check_collect_static_metrics(): ++ ssh_client = create_ssh_client(get_server_cfg()) ++ return collect_static_metrics(ssh_client) ++ ++def check_collect_runtime_metrics(): ++ server_cfg = get_server_cfg() ++ feature_cfg = get_feature_cfg() ++ ssh_client = create_ssh_client(server_cfg) ++ return collect_runtime_metrics(ssh_client, server_cfg, feature_cfg["pressure_test_mode"]) ++ ++def check_analyze_performance(): ++ metrics_data = check_collect_runtime_metrics() ++ server_cfg = get_server_cfg() ++ report, bottleneck = analyze_performance(metrics_data, server_cfg["app"]) ++ return report, bottleneck ++ ++def check_describe_param_background_knob(): ++ service_name = get_server_cfg()["app"] ++ feature_cfg = get_feature_cfg() ++ param_knowledge = ParamKnowledge( ++ ssh_client=create_ssh_client(get_server_cfg()), ++ tune_system_param=feature_cfg["tune_system_param"], ++ tune_app_param=feature_cfg["tune_app_param"] ++ ) ++ ++ all_params = param_knowledge.get_params(service_name) ++ params_describe_list = param_knowledge.describe_param_background_knob( ++ service_name, all_params ++ )[0] ++ ++ return params_describe_list ++ ++def check_param_recommender(): ++ server_cfg = get_server_cfg() ++ feature_cfg = get_feature_cfg() ++ service_name = server_cfg["app"] ++ ssh_client = create_ssh_client(server_cfg) ++ app_interface = AppInterface(ssh_client).get(service_name) ++ static_profile = check_collect_static_metrics() ++ analysis_report = check_analyze_performance()[0] ++ param_knowledge = ParamKnowledge( ++ ssh_client=create_ssh_client(get_server_cfg()), ++ tune_system_param=feature_cfg["tune_system_param"], ++ tune_app_param=feature_cfg["tune_app_param"] ++ ) ++ all_params = param_knowledge.get_params(service_name) ++ params_set = param_knowledge.describe_param_background_knob( ++ service_name, all_params ++ )[0] ++ ++ param_recommender = ParamRecommender( ++ service_name=service_name, ++ slo_goal=0.1, ++ performance_metric=app_interface.performance_metric, ++ static_profile=static_profile, ++ performance_analysis_report=analysis_report, ++ ssh_client=ssh_client, ++ all_params=all_params, ++ params_set=params_set ++ ) ++ ++ recommend_params = param_recommender.run(history_result={}, is_positive=True) ++ ++ return recommend_params ++ ++def check_apply_params(): ++ server_cfg = get_server_cfg() ++ feature_cfg = get_feature_cfg() ++ service_name = server_cfg["app"] ++ ssh_client = create_ssh_client(server_cfg) ++ analysis_report = check_analyze_performance()[0] ++ static_profile = check_collect_static_metrics() ++ ++ def slo_calc_callback(baseline, benchmark_result, symbol): ++ if baseline is None or abs(baseline) < 1e-9: ++ return 0.0 ++ return symbol * (benchmark_result - baseline) / baseline ++ ++ optimizer = ParamOptimizer( ++ service_name=service_name, ++ slo_goal=0.1, ++ analysis_report=analysis_report, ++ static_profile=static_profile, ++ ssh_client=ssh_client, ++ slo_calc_callback=slo_calc_callback, ++ need_restart_application=feature_cfg["need_restart_application"], ++ pressure_test_mode=feature_cfg["pressure_test_mode"], ++ tune_system_param= feature_cfg["tune_system_param"], ++ tune_app_param= feature_cfg["tune_app_param"], ++ need_recover_cluster=feature_cfg["need_recover_cluster"], ++ benchmark_timeout=feature_cfg["benchmark_timeout"] ++ ) ++ ++ recommend_params = optimizer.param_recommender.run(history_result={}, is_positive=True) ++ print(f"推荐设置的参数为:{recommend_params}") ++ optimizer.apply_params(recommend_params) ++ if optimizer.need_restart_application: ++ optimizer.restart_application() diff --git a/copilot-tune/src/config.py b/copilot-tune/src/config.py new file mode 100644 index 0000000..0e823c7 @@ -1735,10 +3936,10 @@ index 0000000..22dd30b \ No newline at end of file diff --git a/copilot-tune/src/knowledge_base/knob_params/ceph.json b/copilot-tune/src/knowledge_base/knob_params/ceph.json new file mode 100644 -index 0000000..80d0921 +index 0000000..978e9bd --- /dev/null +++ b/copilot-tune/src/knowledge_base/knob_params/ceph.json -@@ -0,0 +1,641 @@ +@@ -0,0 +1,615 @@ +{ + "bluestore_throttle_bytes": { + "desc": "该参数控制Bluestore的写入速率限制,以字节为单位。可以缓解disk IO瓶颈。增大该值可以提高写入性能,减小则可以降低写入速率以保护其他操作。", @@ -1827,7 +4028,7 @@ index 0000000..80d0921 + 1, + null + ], -+ "default_value": "1Ki", ++ "default_value": "1024", + "related_param": [] + }, + "osd_max_pg_log_entries": { @@ -1880,32 +4081,6 @@ index 0000000..80d0921 + "default_value": "4194304", + "related_param": [] + }, -+ "osd_mclock_iops_capacity_low_threshold_hdd": { -+ "desc": "该参数用于设置HDD的IOPS容量低阈值(以4KiB块大小为单位),当IOPS容量低于该阈值时,将忽略OSD基准测试结果,并回退到由osd_mclock_max_capacity_iops_hdd定义的最后有效或默认IOPS容量。减小该值可以避免HDD过载,从而缓解disk IO瓶颈。", -+ "type": "continuous", -+ "dtype": "float", -+ "range": [ -+ 0, -+ null -+ ], -+ "default_value": "50.0", -+ "related_param": [ -+ "osd_mclock_max_capacity_iops_hdd" -+ ] -+ }, -+ "osd_mclock_iops_capacity_low_threshold_ssd": { -+ "desc": "该参数定义了SSD的IOPS容量低阈值(以4KiB块大小为单位),当OSD的IOPS性能低于此阈值时,将忽略OSD基准测试结果,并回退到由osd_mclock_max_capacity_iops_ssd定义的最后有效或默认IOPS容量。减小该值可以避免SSD过载,从而缓解磁盘IO瓶颈。", -+ "type": "continuous", -+ "dtype": "float", -+ "range": [ -+ 0, -+ null -+ ], -+ "default_value": "1000.0", -+ "related_param": [ -+ "osd_mclock_max_capacity_iops_ssd" -+ ] -+ }, + "osd_mclock_iops_capacity_threshold_hdd": { + "desc": "该参数定义了HDD的IOPS容量阈值(以4KiB块大小为单位),超过该阈值后将忽略OSD基准测试结果,并回退到由osd_mclock_max_capacity_iops_hdd定义的最后有效或默认IOPS容量。增大该值可以提高HDD的使用效率,从而缓解磁盘IO瓶颈。", + "type": "continuous", @@ -2294,7 +4469,7 @@ index 0000000..80d0921 + 0, + null + ], -+ "default_value": "7 days", ++ "default_value": "604800", + "related_param": [ + "osd_scrub_min_interval" + ] @@ -2382,10 +4557,10 @@ index 0000000..80d0921 + diff --git a/copilot-tune/src/knowledge_base/knob_params/flink.json b/copilot-tune/src/knowledge_base/knob_params/flink.json new file mode 100644 -index 0000000..670f2a7 +index 0000000..4b9f4ae --- /dev/null +++ b/copilot-tune/src/knowledge_base/knob_params/flink.json -@@ -0,0 +1,300 @@ +@@ -0,0 +1,190 @@ +{ + "execution.checkpointing.interval": { + "desc": "设置执行检查点的时间间隔,单位为毫秒。该参数用于启用和控制状态快照的频率,能够缓解系统在状态恢复时的磁盘IO瓶颈。增大该值可以减少检查点的频率,从而降低系统负载,但可能导致恢复时间增加;减小该值可以提高数据一致性,但会增加系统负载。要启用检查点,必须将此值设置为大于0的正整数。", @@ -2427,18 +4602,8 @@ index 0000000..670f2a7 + "dtype": "int", + "default_value": 60000 + }, -+ "jobmanager.memory.flink.size": { -+ "desc": "JobManager的总内存大小,包括JVM堆内存和非堆内存,但不包括JVM Metaspace和JVM Overhead。增加此值可以缓解内存瓶颈,提升JobManager的性能,主要用于Flink的内部数据结构和状态管理。", -+ "type": "discrete", -+ "range": [ -+ null, -+ null -+ ], -+ "dtype": "string", -+ "default_value": "(none)" -+ }, + "jobmanager.memory.jvm-overhead.max": { -+ "desc": "设置 JobManager JVM 的最大开销内存,确保 JVM 在高负载下有足够的内存。增加此值可以缓解内存瓶颈,提升系统的稳定性。此参数用于指定为 JVM 开销保留的非堆内存,例如线程栈空间、编译缓存等。", ++ "desc": "该参数的单位应采用小写形式,数值与单位间有一个空格,数值应为整数。该值不应大于32GB。设置 JobManager JVM 的最大开销内存,确保 JVM 在高负载下有足够的内存。增加此值可以缓解内存瓶颈,提升系统的稳定性。此参数用于指定为 JVM 开销保留的非堆内存,例如线程栈空间、编译缓存等。", + "type": "discrete", + "range": [ + null, @@ -2448,7 +4613,7 @@ index 0000000..670f2a7 + "default_value": "1 gb" + }, + "jobmanager.memory.jvm-overhead.min": { -+ "desc": "设置 JobManager JVM 的最小开销内存,确保 JVM 有足够的内存用于运行。此参数用于预留 JVM 的非堆内存开销,包括线程栈空间、编译缓存等。增加此值可以缓解内存瓶颈,避免 JVM 频繁进行垃圾回收。", ++ "desc": "该参数的单位应采用小写形式,数值与单位间有一个空格,数值应为整数。设置 JobManager JVM 的最小开销内存,确保 JVM 有足够的内存用于运行。此参数用于预留 JVM 的非堆内存开销,包括线程栈空间、编译缓存等。增加此值可以缓解内存瓶颈,避免 JVM 频繁进行垃圾回收。", + "type": "discrete", + "range": [ + null, @@ -2457,16 +4622,6 @@ index 0000000..670f2a7 + "dtype": "string", + "default_value": "192 mb" + }, -+ "jobmanager.memory.process.size": { -+ "desc": "设置 JobManager 进程的总内存大小,包括 JVM heap、off-heap、网络缓冲等。此参数影响 JobManager 的内存使用情况,适当增加此值可以缓解内存瓶颈,确保 JobManager 能够处理更多的任务和状态信息。该值包括所有 JobManager JVM 进程消耗的内存,包括总 Flink 内存、JVM Metaspace 和 JVM Overhead。", -+ "type": "continuous", -+ "range": [ -+ 1024, -+ 65536 -+ ], -+ "dtype": "int", -+ "default_value": "(none)" -+ }, + "parallelism.default": { + "desc": "设置 Flink 作业的默认并行度,若未指定并行度则使用该值。增加此值可以缓解 CPU 瓶颈,提升作业的执行效率。默认值为1,最大值为128。", + "type": "discrete", @@ -2519,7 +4674,7 @@ index 0000000..670f2a7 + "default_value": "memory" + }, + "state.backend.rocksdb.block.cache-size": { -+ "desc": "设置 RocksDB 的块缓存大小,单位为字节。该参数用于控制 RocksDB 在内存中缓存的数据块数量,从而提高读取性能。", ++ "desc": "该参数的单位应采用小写形式,数值与单位间有一个空格,数值应为整数。设置 RocksDB 的块缓存大小,单位为字节。该参数用于控制 RocksDB 在内存中缓存的数据块数量,从而提高读取性能。", + "type": "continuous", + "range": [ + 0, @@ -2539,7 +4694,7 @@ index 0000000..670f2a7 + "default_value": 4 + }, + "state.backend.rocksdb.compaction.level.max-size-level-base": { -+ "desc": "设置 RocksDB 的最大压缩级别基础大小,单位为字节。该参数用于优化存储性能,能够缓解系统在状态管理时的磁盘 I/O 瓶颈。", ++ "desc": "该参数的单位应采用小写形式,数值与单位间有一个空格,数值应为整数。设置 RocksDB 的最大压缩级别基础大小,单位为字节。该参数用于优化存储性能,能够缓解系统在状态管理时的磁盘 I/O 瓶颈。", + "type": "continuous", + "range": [ + 1, @@ -2566,7 +4721,7 @@ index 0000000..670f2a7 + "default_value": "false" + }, + "state.backend.rocksdb.write-batch-size": { -+ "desc": "设置 RocksDB 的写批处理大小,单位为字节。该参数用于优化写入性能。", ++ "desc": "该参数的单位应采用小写形式,数值与单位间有一个空格,数值应为整数。设置 RocksDB 的写批处理大小,单位为字节。该参数用于优化写入性能。", + "type": "continuous", + "range": [ + 1, @@ -2575,105 +4730,15 @@ index 0000000..670f2a7 + "dtype": "string", + "default_value": "2 mb" + }, -+ "state.backend.rocksdb.writebuffer.count": { -+ "desc": "设置 RocksDB 的写缓冲区数量,用于优化写入性能。", -+ "type": "continuous", -+ "range": [ -+ 1, -+ null -+ ], -+ "dtype": "int", -+ "default_value": 2 -+ }, -+ "taskmanager.memory.flink.size": { -+ "desc": "设置 TaskManager 用于 Flink 的内存大小,主要用于 Flink 的内部数据结构和状态管理。", -+ "type": "continuous", -+ "range": [ -+ 0, -+ 65536 -+ ], -+ "dtype": "int", -+ "default_value": 1024 -+ }, -+ "taskmanager.memory.managed.fraction": { -+ "desc": "该参数设置 TaskManager 管理的内存占总内存的比例,控制 Flink 的内存使用。", -+ "type": "continuous", -+ "range": [ -+ 0.0, -+ 1.0 -+ ], -+ "dtype": "float", -+ "default_value": 0.4 -+ }, -+ "taskmanager.memory.managed.size": { -+ "desc": "设置 TaskManager 管理的内存大小,主要用于 Flink 的状态管理和数据处理。", -+ "type": "continuous", -+ "range": [ -+ 0, -+ 65536 -+ ], -+ "dtype": "int", -+ "default_value": 0 -+ }, -+ "taskmanager.memory.network.fraction": { -+ "desc": "设置 TaskManager 网络缓冲区占总内存的比例,控制网络缓冲区的大小。", -+ "type": "continuous", -+ "range": [ -+ 0.0, -+ 1.0 -+ ], -+ "dtype": "float", -+ "default_value": 0.1 -+ }, -+ "taskmanager.memory.network.max": { -+ "desc": "最大网络内存大小,用于TaskExecutors。", -+ "type": "continuous", -+ "range": [ -+ 0, -+ null -+ ], -+ "dtype": "string", -+ "default_value": "1 gb" -+ }, -+ "taskmanager.memory.network.min": { -+ "desc": "设置 TaskManager 网络缓冲区的最小大小,确保在低负载情况下网络缓冲区不会过小。", -+ "type": "continuous", -+ "range": [ -+ 0, -+ 65536 -+ ], -+ "dtype": "string", -+ "default_value": "64 mb" -+ }, -+ "taskmanager.memory.process.size": { -+ "desc": "设置 TaskManager 进程的总内存大小,包括 JVM heap、off-heap、网络缓冲等。", -+ "type": "continuous", -+ "range": [ -+ 1024, -+ 65536 -+ ], -+ "dtype": "int", -+ "default_value": 1024 -+ }, -+ "taskmanager.memory.task.heap.size": { -+ "desc": "设置 TaskManager 任务的堆内存大小,主要用于任务的计算和状态管理。", -+ "type": "continuous", -+ "range": [ -+ 0, -+ 65536 -+ ], -+ "dtype": "int", -+ "default_value": 1024 -+ }, -+ "taskmanager.memory.task.off-heap.size": { -+ "desc": "设置 TaskManager 任务的 off-heap 内存大小,主要用于存储大对象和避免 JVM 的垃圾回收。", -+ "type": "discrete", ++ "state.backend.rocksdb.writebuffer.count": { ++ "desc": "设置 RocksDB 的写缓冲区数量,用于优化写入性能。", ++ "type": "continuous", + "range": [ -+ null, ++ 1, + null + ], -+ "dtype": "string", -+ "default_value": "0 bytes" ++ "dtype": "int", ++ "default_value": 2 + }, + "taskmanager.numberOfTaskSlots": { + "desc": "该参数设置 TaskManager 的任务槽数量,决定了单个 TaskManager 可以并行处理的任务数量。", @@ -2689,7 +4754,7 @@ index 0000000..670f2a7 \ No newline at end of file diff --git a/copilot-tune/src/knowledge_base/knob_params/gaussdb.json b/copilot-tune/src/knowledge_base/knob_params/gaussdb.json new file mode 100644 -index 0000000..95f6466 +index 0000000..6be8c02 --- /dev/null +++ b/copilot-tune/src/knowledge_base/knob_params/gaussdb.json @@ -0,0 +1,382 @@ @@ -3065,14 +5130,14 @@ index 0000000..95f6466 + "default_value": "on" + }, + "max_process_memory": { -+ "desc": "设置一个数据库节点可用的最大物理内存。该参数用于防止数据库内存使用超出系统承载能力,避免节点OOM。建议值为物理内存的66.5%以内(考虑预留操作系统内存)。修改后需重启实例生效。", -+ "type": "continuous", -+ "range": [ -+ 2097152, -+ 2147483647 -+ ], -+ "dtype": "Integer", -+ "default_value": 696320000 ++ "desc": "设置一个数据库节点可用的最大物理内存。该参数用于防止数据库内存使用超出系统承载能力,避免节点OOM。建议值为物理内存的66.5%以内(考虑预留操作系统内存)。修改后需重启实例生效。", ++ "type": "continuous", ++ "range": [ ++ 2097152, ++ 2147483647 ++ ], ++ "dtype": "Integer", ++ "default_value": 696320000 + } +} \ No newline at end of file @@ -4131,12 +6196,384 @@ index 0000000..6c186d2 + } +} \ No newline at end of file +diff --git a/copilot-tune/src/knowledge_base/knob_params/oceanbase.json b/copilot-tune/src/knowledge_base/knob_params/oceanbase.json +new file mode 100644 +index 0000000..ded5c92 +--- /dev/null ++++ b/copilot-tune/src/knowledge_base/knob_params/oceanbase.json +@@ -0,0 +1,366 @@ ++{ ++ "workers_per_cpu_quota": { ++ "desc": "此参数 * 租户 max_cpu = 租户能分配的最大线程数。这里的最大线程数是分配出来的,可以使用的,并不是同时运行的最大线程数。此参数一般不做调整。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [2, 20], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "net_thread_count": { ++ "desc": "接收客户端请求的网络线程数。重启生效。通过 top -H 查看 MySQLIO 线程的使用效率:若使用率大于 90%,说明接收请求的线程可能成为瓶颈,建议提高该参数;若所有线程都小于 50%,建议减小该参数以减少线程切换开销。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 64], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "enable_early_lock_release": { ++ "desc": "开启提前解行锁。热点行场景建议开启。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "enable_monotonic_weak_read": { ++ "desc": "开启单调读。性能场景建议关闭。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "enable_perf_event": { ++ "desc": "开启信息采集。生产环境必须开启,性能测试场景可以酌情关闭。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "enable_record_trace_log": { ++ "desc": "开启 Trace Log 功能。生产环境在开启 Perf Event 和 SQL Audit 功能的情况下,此参数可以关闭。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "syslog_level": { ++ "desc": "系统日志的日志级别。性能场景下建议设置为 PERF,以减少非关键日志输出。", ++ "type": "discrete", ++ "dtype": "string", ++ "range": ["DEBUG", "INFO", "WARN", "ERROR", "PERF"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "clog_sync_time_warn_threshold": { ++ "desc": "日志同步耗时超过一定阈值时打印报警日志,阈值可通过该配置项调整。建议调大,但需注意可能影响 Clog 同步慢问题的排查。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, 1000], ++ "unit": "ms", ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "builtin_db_data_verify_cycle": { ++ "desc": "宏块巡检周期参数。当设置为 0 时关闭巡检。性能相关场景建议设置为 0 以减少系统开销。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 360], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "disk_io_thread_count": { ++ "desc": "磁盘 I/O 线程数,须为偶数。可根据 I/O 压力适当调整该参数。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [2, 32], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "enable_async_syslog": { ++ "desc": "开启异步日志输出。性能场景建议设置为 True,以降低日志打印对性能的影响。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "fuse_row_cache_priority": { ++ "desc": "融合行缓存在 KV cache 中的优先级。建议设置为大于 1,以避免缓存过早被替换,提升查询性能。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, null], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "micro_block_merge_verify_level": { ++ "desc": "宏块验证级别,0 表示不做任何校验。性能场景可设置为 0,但生产系统不建议修改。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 1, 2], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "compaction_low_thread_score": { ++ "desc": "低优先级 Compaction 的并发线程数(主要针对 Major SSTable)。默认为 0 表示 6 个并发线程,可根据负载适当调大以提高合并速度。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 100], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "compaction_mid_thread_score": { ++ "desc": "中优先级 Compaction 的并发线程数(主要针对 Minor SSTable)。默认为 0 表示 6 个并发线程,可根据负载适当调大以提升合并效率。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 100], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "compaction_high_thread_score": { ++ "desc": "高优先级 Compaction 的并发线程数(主要针对 Mini SSTable)。默认为 0 表示 6 个并发线程,可根据系统压力适当调大。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 100], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "ob_proxy_readonly_transaction_routing_policy": { ++ "desc": "控制 Proxy 对于事务的路由是否受只读语句影响。建议设置为 False,表示以第一条实际开启事务的语句为准。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "observer", ++ "edit_level": "dynamic" ++ }, ++ "work_thread_num": { ++ "desc": "OBProxy 的工作线程数,对 CPU 占用影响较大,默认值为 128。可根据 CPU 数量动态调整,更改后需重启。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, 128], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "task_thread_num": { ++ "desc": "OBProxy 后台任务线程数(如拉取 rslist),默认值为 2。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, 4], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "block_thread_num": { ++ "desc": "OBProxy 的 block 线程数,默认值为 1。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, 4], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "grpc_thread_num": { ++ "desc": "OBProxy 的 gRPC 线程数,默认值为 8。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [4, 32], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "net_accept_threads": { ++ "desc": "执行 accept 的线程数,默认值为 2。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [8, 16], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "automatic_match_work_thread": { ++ "desc": "是否根据 CPU 数自动调整工作线程数。true 时上限为 work_thread_num,适用于混合部署;false 时使用固定值,适用于独占部署。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_compression_protocol": { ++ "desc": "是否启用压缩协议以降低 OBProxy 的 CPU 占用。建议配置为 False。说明:自 V4.3.4 版本起该参数已废弃。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "server_tcp_user_timeout": { ++ "desc": "OBProxy 与 OBServer 连接的 TCP user timeout(单位:秒)。默认值为 0,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 20], ++ "unit": "s", ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "client_tcp_user_timeout": { ++ "desc": "OBProxy 与 Client 连接的 TCP user timeout(单位:秒)。默认值为 0,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 20], ++ "unit": "s", ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "client_max_connections": { ++ "desc": "OBProxy 可接受的最大客户端连接数,默认值为 8192。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, null], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "connect_observer_max_retries": { ++ "desc": "OBProxy 重连 OBServer 的最大重试次数,默认值为 3。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [2, 5], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "observer_query_timeout_delta": { ++ "desc": "网络传输延时补偿,给 OBServer 的 ob_query_timeout 增加的时间(单位:秒)。默认值为 20,取值范围 [1,39]。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, 30], ++ "unit": "s", ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "sock_option_flag_out": { ++ "desc": "OBProxy 与 OBServer 之间的 TCP 参数(bit 位控制 no_delay、keepalive、linger_on)。不同版本默认值不同:V1.8.x 建议为 3,V3.x 建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 7], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "server_tcp_keepidle": { ++ "desc": "OBProxy 与 OBServer 的 TCP 启动 keepalive 前的 idle 时间(秒)。默认值为 5,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 7200], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "server_tcp_keepintvl": { ++ "desc": "OBProxy 与 OBServer 的 TCP 两次 keepalive 探活间隔(秒)。默认值为 5,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 75], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "server_tcp_keepcnt": { ++ "desc": "OBProxy 与 OBServer 的 TCP 最多发送的 keepalive 包数量,默认值为 5。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 9], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "client_sock_option_flag_out": { ++ "desc": "客户端与 OBProxy 之间的 TCP 参数(bit 位控制 no_delay、keepalive、linger_on)。V1.8.x 默认值为 0,V3.x 为 2,建议设置为 3 启用 no_delay 和 linger_on。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 3], ++ "scope": "obproxy", ++ "effection_mode": "dynamic" ++ }, ++ "client_tcp_keepidle": { ++ "desc": "客户端与 OBProxy 的 TCP 启动 keepalive 前 idle 时间(秒)。默认值为 5,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [1, 7200], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "client_tcp_keepintvl": { ++ "desc": "客户端与 OBProxy 的 TCP 两次 keepalive 探活间隔(秒)。默认值为 5,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, 75], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "client_tcp_keepcnt": { ++ "desc": "客户端与 OBProxy 的 TCP 最多发送的 keepalive 包数量,默认值为 5。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "int", ++ "range": [0, null], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_index_route": { ++ "desc": "是否开启基于全局索引键的全局索引表路由。默认 True,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_pl_route": { ++ "desc": "是否开启 PL 路由。默认 True,性能场景建议关闭(False)。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_reroute": { ++ "desc": "是否开启二次路由。需开启 OceanBase 2.0 协议(enable_ob_protocol_v2)后生效。默认 False,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_partition_table_route": { ++ "desc": "是否开启分区表路由。默认 True,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "server_routing_mode": { ++ "desc": "OBProxy 路由模式。包括 oceanbase(默认)、random、mock、mysql 等。建议保持默认值。", ++ "type": "discrete", ++ "dtype": "string", ++ "range": ["oceanbase", "random", "mock", "mysql"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_ob_protocol_v2": { ++ "desc": "是否开启 OBProxy 与 OBServer 之间的 OceanBase 2.0 协议(基于 MySQL 压缩协议)。默认 False,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "routing_cache_mem_limited": { ++ "desc": "设置 OBProxy 路由缓存(表缓存、地址缓存等)的内存上限。默认值为 128 MB,取值范围 [1KB, 100G],建议保持默认值。", ++ "type": "discrete", ++ "dtype": "string", ++ "range": ["1KB", "100G"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ }, ++ "enable_bad_route_reject": { ++ "desc": "是否拒绝无法路由的请求。默认 False,建议保持默认值。", ++ "type": "discrete", ++ "dtype": "boolean", ++ "range": ["true", "false"], ++ "scope": "obproxy", ++ "edit_level": "dynamic" ++ } ++} diff --git a/copilot-tune/src/knowledge_base/knob_params/pgsql.json b/copilot-tune/src/knowledge_base/knob_params/pgsql.json new file mode 100644 -index 0000000..62824b9 +index 0000000..7bf4d64 --- /dev/null +++ b/copilot-tune/src/knowledge_base/knob_params/pgsql.json -@@ -0,0 +1,448 @@ +@@ -0,0 +1,467 @@ +{ + "archive_mode": { + "desc": "启用或禁用归档模式。当启用 archive_mode 时,已完成的 WAL 段将发送到归档存储。设置为 on 时,WAL 日志将被归档,适合需要数据恢复的场景。开启归档模式会增加磁盘 I/O,但能提高数据安全性。", @@ -4146,7 +6583,8 @@ index 0000000..62824b9 + "off", + "on", + "always" -+ ] ++ ], ++ "default_value": "off" + }, + "autovacuum_analyze_scale_factor": { + "desc": "该参数控制自动分析(autovacuum)在执行分析操作时,表中需要更新的行数与表的总行数的比例。增大该值可以减少自动分析的频率,降低CPU和IO的负担,但可能导致统计信息不准确。减小该值会增加自动分析的频率,有助于保持查询优化器的统计信息更新,缓解memory瓶颈。", @@ -4154,17 +6592,19 @@ index 0000000..62824b9 + "dtype": "float", + "range": [ + 0, -+ 1 -+ ] ++ 100 ++ ], ++ "default_value": 0.1 + }, + "autovacuum_max_workers": { + "desc": "设置自动清理进程的最大工作线程数。增加此值可以提高数据库的自动清理能力,缓解由于死元组导致的磁盘IO瓶颈。取值范围为0到1024,0表示禁用自动清理。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ 1024 -+ ] ++ 1, ++ 262143 ++ ], ++ "default_value": 3 + }, + "autovacuum_vacuum_scale_factor": { + "desc": "该参数控制自动清理(autovacuum)在执行清理操作时,表中需要删除的行数与表的总行数的比例。增大该值可以减少自动清理的频率,从而降低CPU和IO的负担,但可能导致表膨胀。减小该值会增加自动清理的频率,有助于及时释放空间,缓解disk IO瓶颈。", @@ -4172,35 +6612,39 @@ index 0000000..62824b9 + "dtype": "float", + "range": [ + 0, -+ 1 -+ ] ++ 100 ++ ], ++ "default_value": 0.2 + }, + "autovacuum_work_mem": { -+ "desc": "指定每个自动清理工作进程可以使用的最大内存量。增大此值可以提高自动清理的效率,缓解内存瓶颈,尤其是在大表的自动清理时。建议根据表的大小和系统资源进行调整。", ++ "desc": "指定每个自动清理工作进程可以使用的最大内存量。单位为千字节(MB),取值范围为64-1073741823。增大此值可以提高自动清理的效率,缓解内存瓶颈,尤其是在大表的自动清理时。建议根据表的大小和系统资源进行调整。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 64, -+ 1073741823 -+ ] ++ -1, ++ 2147483647 ++ ], ++ "default_value": -1 + }, + "bgwriter_delay": { + "desc": "设置后台写入器活动轮次之间的延迟时间。增大此值可以减少磁盘IO的频率,适合IO密集型的工作负载。默认值为200毫秒,单位为毫秒,取值范围为0到1000。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ 1000 -+ ] ++ 10, ++ 10000 ++ ], ++ "default_value": 200 + }, + "bgwriter_flush_after": { -+ "desc": "bgwriter_flush_after参数设置后台写入器在写入数据后,尝试强制操作系统将这些写入操作发送到基础存储的阈值。增大此值可以减少磁盘IO的频率,适合IO密集型的工作负载。单位为字节,默认值为512kB,取值范围为0到1GB。", ++ "desc": "bgwriter_flush_after参数设置后台写入器在写入数据后,尝试强制操作系统将这些写入操作发送到基础存储的阈值。增大此值可以减少磁盘IO的频率,适合IO密集型的工作负载。单位为8千字节(8kb),默认值为64(512kb),取值范围为0到256(1GB),请给出0-256范围内的数值,不要包含单位。", + "type": "continuous", + "dtype": "int", + "range": [ + 0, -+ 1073741824 -+ ] ++ 256 ++ ], ++ "default_value": 64 + }, + "bgwriter_lru_maxpages": { + "desc": "设置后台写入器每次写入的最大页面数。在每一轮中,后台写入器写入的缓冲区不超过这个数量。增大此值可以提高写入性能,适合内存充足的系统。取值范围为0到10000,0表示禁用此功能。", @@ -4208,8 +6652,9 @@ index 0000000..62824b9 + "dtype": "int", + "range": [ + 0, -+ 10000 -+ ] ++ 1073741823 ++ ], ++ "default_value": 100 + }, + "checkpoint_completion_target": { + "desc": "设置检查点完成的目标时间比例,作为检查点之间总时间的分数。增大此值可以使检查点过程更加平滑,缓解磁盘IO瓶颈,尤其是在高负载情况下。建议设置为0.5到0.9之间的值。", @@ -4218,7 +6663,8 @@ index 0000000..62824b9 + "range": [ + 0.0, + 1.0 -+ ] ++ ], ++ "default_value": 0.5 + }, + "checkpoint_timeout": { + "desc": "自动 WAL 检查点之间的最大时间间隔。增大此值可以减少检查点的频率,缓解磁盘 I/O 瓶颈,但可能会增加恢复时间。建议根据系统负载和性能需求进行调整。", @@ -4227,7 +6673,8 @@ index 0000000..62824b9 + "range": [ + 30, + 86400 -+ ] ++ ], ++ "default_value": 300 + }, + "commit_siblings": { + "desc": "在执行 commit_delay 延迟之前,必须有多少个并发打开事务。该参数影响VACUUM操作的触发频率,增大该值可以减少VACUUM的频率,从而降低CPU和磁盘IO的负担。", @@ -4235,8 +6682,9 @@ index 0000000..62824b9 + "dtype": "int", + "range": [ + 0, -+ 100 -+ ] ++ 1000 ++ ], ++ "default_value": 5 + }, + "cpu_index_tuple_cost": { + "desc": "设置规划器对索引扫描期间处理每个索引条目的成本的估计。增大此值可以影响查询计划的选择,适合CPU密集型的工作负载。", @@ -4244,8 +6692,9 @@ index 0000000..62824b9 + "dtype": "float", + "range": [ + 0, -+ 1000 -+ ] ++ 100000000 ++ ], ++ "default_value": 0.005 + }, + "cpu_tuple_cost": { + "desc": "设置规划器对查询期间处理每一行的成本的估计。增大此值可以影响查询计划的选择,适合CPU密集型的工作负载。", @@ -4253,8 +6702,9 @@ index 0000000..62824b9 + "dtype": "float", + "range": [ + 0, -+ 1000 -+ ] ++ 100000000 ++ ], ++ "default_value": 0.01 + }, + "deadlock_timeout": { + "desc": "这是在检查是否存在死锁条件之前等待锁的时间。增大该值可能会导致死锁检测延迟,但可以减少CPU的消耗。减小该值可以更快地检测到死锁。", @@ -4262,17 +6712,19 @@ index 0000000..62824b9 + "dtype": "int", + "range": [ + 1, -+ 10000 -+ ] ++ 2147483647 ++ ], ++ "default_value": 1000 + }, + "effective_cache_size": { -+ "desc": "设置PostgreSQL查询优化器假设的可用缓存内存量。增大此值可以提高查询优化器的决策质量,缓解内存瓶颈,尤其是在复杂查询中。通常建议设置为系统总内存的50%到75%。", ++ "desc": "设置PostgreSQL查询优化器假设的可用缓存内存量。单位为千字节(KB),请直接回答16-2147483647范围内的数值,不要包括单位,仔细检查不要超出参数合法范围。增大此值可以提高查询优化器的决策质量,缓解内存瓶颈,尤其是在复杂查询中。", + "type": "continuous", + "dtype": "integer", + "range": [ + 16, -+ 1073741823 -+ ] ++ 2147483647 ++ ], ++ "default_value": 524288 + }, + "effective_io_concurrency": { + "desc": "设置预期的并发磁盘I/O操作数量。增大此值可以提高并发I/O操作的性能,缓解磁盘I/O瓶颈,尤其是在高并发的读写操作中。建议根据硬件能力进行调整,值越高,性能越好,但也要考虑系统负载。", @@ -4281,7 +6733,8 @@ index 0000000..62824b9 + "range": [ + 1, + 1000 -+ ] ++ ], ++ "default_value": 1 + }, + "enable_bitmapscan": { + "desc": "启用或禁用查询规划器对位图扫描计划类型的使用。启用此选项可以提高大数据集的查询性能。取值为ON或OFF。", @@ -4290,7 +6743,8 @@ index 0000000..62824b9 + "range": [ + "off", + "on" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_hashjoin": { + "desc": "启用或禁用查询规划器对哈希连接计划类型的使用。启用此选项可以提高连接查询的性能,特别适合大数据集的连接操作。", @@ -4299,7 +6753,8 @@ index 0000000..62824b9 + "range": [ + "off", + "on" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_indexscan": { + "desc": "启用或禁用查询规划器对索引扫描和仅索引扫描计划类型的使用。启用此选项可以提高查询性能,适合有索引的表。", @@ -4308,7 +6763,8 @@ index 0000000..62824b9 + "range": [ + "off", + "on" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_material": { + "desc": "启用或禁用查询规划器对物化(materialization)的使用。启用此选项可以提高复杂查询的性能,适合需要多次访问相同数据的查询。", @@ -4317,7 +6773,8 @@ index 0000000..62824b9 + "range": [ + "off", + "on" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_mergejoin": { + "desc": "启用或禁用查询规划器对归并连接计划类型的使用。启用此选项可以提高大数据集连接的性能。取值为ON或OFF。", @@ -4326,7 +6783,8 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_nestloop": { + "desc": "启用或禁用查询规划器对嵌套循环连接计划的使用。启用此选项可以提高小数据集连接的性能。取值为ON或OFF。", @@ -4335,7 +6793,8 @@ index 0000000..62824b9 + "range": [ + "ON", + "OFF" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_partition_pruning": { + "desc": "启用或禁用查询规划器从查询计划中消除分区表的分区的功能。启用此选项可以提高分区表的查询性能。取值为ON或OFF。", @@ -4344,7 +6803,8 @@ index 0000000..62824b9 + "range": [ + "ON", + "OFF" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_seqscan": { + "desc": "启用或禁用查询规划器使用顺序扫描计划类型。启用此选项可以提高查询性能,适合小数据集的查询。", @@ -4353,7 +6813,8 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] ++ ], ++ "default_value": "on" + }, + "enable_sort": { + "desc": "启用或禁用查询规划器使用显式排序步骤。启用此选项可以提高查询性能,适合需要排序的查询。", @@ -4362,7 +6823,8 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] ++ ], ++ "default_value": "on" + }, + "fsync": { + "desc": "控制数据库在每次写入后是否强制将数据写入磁盘。设置为on时,确保数据的持久性,但会增加磁盘IO。关闭fsync可能会提高性能,但会增加数据丢失的风险。该参数影响检查点和批量数据写入期间的强制写入操作。", @@ -4371,7 +6833,8 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] ++ ], ++ "default_value": "on" + }, + "full_page_writes": { + "desc": "当该参数开启时,PostgreSQL在修改每个磁盘页面时会将其完整内容写入WAL(Write-Ahead Logging)。这确保在发生崩溃时可以恢复完整页面,适合需要高数据安全性的场景。开启该选项会增加磁盘I/O。", @@ -4380,16 +6843,8 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] -+ }, -+ "idle_in_transaction_session_timeout": { -+ "desc": "设置空闲事务会话的超时时间。增大该值可以减少因空闲会话导致的资源浪费,但可能会导致事务延迟。减小该值可以更快地释放资源。", -+ "type": "continuous", -+ "dtype": "int", -+ "range": [ -+ 0, -+ null -+ ] ++ ], ++ "default_value": "on" + }, + "jit": { + "desc": "该参数控制是否启用JIT(Just-In-Time)编译。设置为ON可以提高复杂查询的性能,缓解CPU瓶颈。设置为OFF则会禁用JIT,适用于简单查询或对性能要求不高的场景。", @@ -4398,34 +6853,38 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] ++ ], ++ "default_value": "on" + }, + "lock_timeout": { -+ "desc": "设置获取锁的超时时间。增大该值可以减少因锁竞争导致的错误,但可能会导致事务延迟。减小该值可以更快地释放锁。", ++ "desc": "设置获取锁的超时时间。增大该值可以减少因锁竞争导致的错误,但可能会导致事务延迟。减小该值可以更快地释放锁,但是可能导致业务异常,请谨慎调整。", + "type": "continuous", + "dtype": "int", + "range": [ + 0, -+ 10000 -+ ] ++ 2147483647 ++ ], ++ "default_value": 0 + }, + "log_min_duration_statement": { + "desc": "设置记录执行时间超过该值的SQL语句的日志。增大该值可以减少日志记录的数量,适合对性能要求较高的场景。减小该值可以更快地发现性能问题。", + "type": "continuous", -+ "dtype": "int", -+ "range": [ -+ 0, -+ 10000 -+ ] ++ "dtype": "int", ++ "range": [ ++ -1, ++ 2147483647 ++ ], ++ "default_value": -1 + }, + "maintenance_work_mem": { -+ "desc": "指定维护操作(如VACUUM、CREATE INDEX等)可以使用的最大内存量。增大此值可以加速这些操作,缓解内存瓶颈,尤其是在大表的维护时。建议根据维护任务的复杂度进行调整。", ++ "desc": "指定维护操作(如VACUUM、CREATE INDEX等)可以使用的最大内存量。单位为千字节(KB),请直接回答1024-2147483647范围内的数值,不要包括单位,仔细检查不要超出参数合法范围。增大此值可以加速这些操作,缓解内存瓶颈,尤其是在大表的维护时。建议根据维护任务的复杂度进行调整。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 64, -+ 1073741823 -+ ] ++ 1024, ++ 2147483647 ++ ], ++ "default_value": 65536 + }, + "max_connections": { + "desc": "确定到数据库服务器的最大并发连接数。增大此值可以支持更多的并发用户,缓解内存瓶颈,但也会增加系统资源的消耗。建议根据系统资源和应用需求进行调整。默认值通常为 100 个连接。", @@ -4433,17 +6892,19 @@ index 0000000..62824b9 + "dtype": "int", + "range": [ + 1, -+ 10000 -+ ] ++ 200000 ++ ], ++ "default_value": 100 + }, + "max_locks_per_transaction": { + "desc": "该参数定义每个事务可以获得的最大锁数量。增大该值可以支持更多的并发事务,适用于高并发的应用场景,缓解内存瓶颈。减小该值可能导致事务因锁不足而失败。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 1, -+ 1024 -+ ] ++ 10, ++ 2147483647 ++ ], ++ "default_value": 64 + }, + "max_parallel_workers": { + "desc": "设置系统可以使用的最大并行工作进程数。增大此值可以提高并行处理能力,缓解CPU瓶颈,尤其是在高并发查询中。建议根据系统资源进行调整。", @@ -4452,7 +6913,8 @@ index 0000000..62824b9 + "range": [ + 0, + 1024 -+ ] ++ ], ++ "default_value": 8 + }, + "max_parallel_workers_per_gather": { + "desc": "设置每个 Gather 或 Gather Merge 节点可以启动的最大工作进程数。增大此值可以提高查询性能,缓解 CPU 瓶颈,尤其是在复杂查询中。建议根据查询复杂度和系统资源进行调整。", @@ -4461,7 +6923,8 @@ index 0000000..62824b9 + "range": [ + 0, + 1024 -+ ] ++ ], ++ "default_value": 2 + }, + "max_prepared_transactions": { + "desc": "该参数设置可以同时处于 '已准备' 状态的最大事务数。增大该值可以支持更多的分布式事务,适用于需要高并发的应用场景,缓解内存瓶颈。减小该值可能导致预备事务因数量不足而失败。", @@ -4469,26 +6932,29 @@ index 0000000..62824b9 + "dtype": "int", + "range": [ + 0, -+ 1024 -+ ] ++ 262143 ++ ], ++ "default_value": 0 + }, + "max_wal_size": { -+ "desc": "在自动检查点期间允许 WAL 增长的最大大小。增大此值可以减少检查点的频率,缓解磁盘 I/O 瓶颈,尤其是在高写入负载的情况下。这是一个软限制;在特殊情况下,WAL 大小可能会超过此值。建议根据系统的写入负载进行调整。", ++ "desc": "在自动检查点期间允许 WAL 增长的最大大小。增大此值可以减少检查点的频率,缓解磁盘 I/O 瓶颈,尤其是在高写入负载的情况下。单位为兆字节(MB),默认值为1024MB(1GB),取值范围为2MB到2147483647MB(2000GB),请给出2-2147483647范围内的数数值,不要包括单位。这是一个软限制;在特殊情况下,WAL 大小可能会超过此值。建议根据系统的写入负载进行调整。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 1, -+ 1073741823 -+ ] ++ 2, ++ 2147483647 ++ ], ++ "default_value": 1024 + }, + "random_page_cost": { + "desc": "用于估算随机读取的真实成本,相对于顺序存取的成本。增大此值可以影响查询优化器的选择,缓解disk IO瓶颈,尤其是在SSD和HDD之间的选择时。通常SSD的值可以设置为1.1,HDD的值可以设置为4.0。", + "type": "continuous", + "dtype": "float", + "range": [ -+ 1.1, -+ 100 -+ ] ++ 0, ++ 10000000 ++ ], ++ "default_value": 4.0 + }, + "seq_page_cost": { + "desc": "设置规划器对作为一系列顺序获取的一部分的磁盘页面获取成本的估计。较低的值会使查询优化器更倾向于使用顺序扫描,从而可能缓解磁盘IO瓶颈。增大该值会使优化器更倾向于使用索引扫描。", @@ -4496,26 +6962,19 @@ index 0000000..62824b9 + "dtype": "float", + "range": [ + 0, -+ 1000 -+ ] ++ 10000000 ++ ], ++ "default_value": 1.0 + }, + "shared_buffers": { -+ "desc": "设置数据库服务器用于共享内存缓冲区的内存量。增加此值可以提高内存使用效率,缓解内存瓶颈,尤其是在高并发的读写操作中。通常建议设置为系统总内存的25%。", -+ "type": "continuous", -+ "dtype": "int", -+ "range": [ -+ 128, -+ 1073741823 -+ ] -+ }, -+ "statement_timeout": { -+ "desc": "设置SQL语句的超时时间,超过该时间将中止执行。增大该值可以减少因长时间运行的查询被中断的风险,但可能会导致资源占用过久。减小该值可以更快地释放资源。", ++ "desc": "设置数据库服务器用于共享内存缓冲区的内存量。单位为千字节(KB),请直接回答16-515246范围内的数值,不要包括单位,仔细检查不要超出参数合法范围。增加此值可以提高内存使用效率,缓解内存瓶颈,尤其是在高并发的读写操作中。通常建议设置为系统总内存的25%。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ null -+ ] ++ 16, ++ 515246 ++ ], ++ "default_value": 1024 + }, + "synchronous_commit": { + "desc": "指定在数据库服务器返回成功指示之前,必须完成多少WAL处理。设置为on可以确保数据安全,但会增加延迟;设置为off可以提高性能,但可能会丢失数据。建议根据数据安全性和性能需求进行调整。", @@ -4527,34 +6986,39 @@ index 0000000..62824b9 + "remote_apply", + "on", + "off" -+ ] ++ ], ++ "default_value": "on" + }, + "temp_buffers": { + "desc": "设置每个数据库会话中用于临时缓冲区的最大内存量。增大该值可以提高临时表的性能,适合需要大量临时表的场景。减小该值可能会导致临时表性能下降。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ 2147483647 -+ ] ++ 100, ++ 1073741823 ++ ], ++ "default_value": 1024 + }, + "temp_file_limit": { -+ "desc": "指定一个进程可以用于临时文件的最大磁盘空间量。增大该值可以允许更大的临时文件,适合需要处理大数据集的场景。减小该值可以防止临时文件占用过多磁盘空间。", ++ "desc": "指定一个进程可以用于临时文件的最大磁盘空间量。单位为千字节(KB),请直接回答数值,不要包括单位,仔细检查不要超出参数合法范围。增大该值可以允许更大的临时文件,适合需要处理大数据集的场景。减小该值可以防止临时文件占用过多磁盘空间。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ 1073741823 ++ -1, ++ 2147483647 + ] ++ , ++ "default_value": -1 + }, + "vacuum_cost_limit": { + "desc": "这是VACUUM操作的成本限制。增大该值可以允许VACUUM操作使用更多的资源,适合需要频繁清理的场景。减小该值可以减少VACUUM对系统性能的影响。默认值为200。", + "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ 100000 -+ ] ++ 1, ++ 10000 ++ ], ++ "default_value": 200 + }, + "wal_compression": { + "desc": "该参数控制是否对WAL(Write Ahead Log)进行压缩。启用WAL压缩可以减少磁盘IO和存储空间的使用,适用于IO瓶颈的场景。禁用WAL压缩则会提高写入性能,但会增加磁盘空间的使用。", @@ -4563,17 +7027,8 @@ index 0000000..62824b9 + "range": [ + "on", + "off" -+ ] -+ }, -+ "wal_level": { -+ "desc": "确定写入WAL(Write Ahead Log)中的信息量,影响数据恢复和复制的能力。可选值为minimal、replica和logical。增大该值会增加磁盘IO的负担,但提供更强的数据保护和复制能力。", -+ "type": "discrete", -+ "dtype": "string", -+ "range": [ -+ "minimal", -+ "replica", -+ "logical" -+ ] ++ ], ++ "default_value": "off" + }, + "work_mem": { + "desc": "设置在写入临时磁盘文件之前,查询操作可以使用的内存量。增大此值可以提高复杂查询的性能,缓解内存瓶颈,尤其是在排序和哈希操作中。建议根据查询复杂度和并发量进行调整。", @@ -4582,7 +7037,8 @@ index 0000000..62824b9 + "range": [ + 64, + 2147483647 -+ ] ++ ], ++ "default_value": 4096 + } +} \ No newline at end of file @@ -4926,10 +7382,10 @@ index 0000000..075d303 +} diff --git a/copilot-tune/src/knowledge_base/knob_params/spark.json b/copilot-tune/src/knowledge_base/knob_params/spark.json new file mode 100644 -index 0000000..6183b4a +index 0000000..552e43a --- /dev/null +++ b/copilot-tune/src/knowledge_base/knob_params/spark.json -@@ -0,0 +1,199 @@ +@@ -0,0 +1,192 @@ +{ + "spark.default.parallelism": { + "desc": "RDD 中的默认分区数,由 `join`、`reduceByKey` 和 `parallelize` 等转换返回,当用户未设置时。对于分布式 shuffle 操作(如 reduceByKey 和 join),它取决于父 RDD 中的最大分区数。对于没有父 RDD 的 parallelize 操作,默认值依赖于集群管理器。例如,在本地模式下,默认值为本地机器上的核心数。通常建议将此值设置为集群总核心数的 2 到 4 倍,以提高 CPU 利用率,缓解 CPU 瓶颈。", @@ -4938,67 +7394,38 @@ index 0000000..6183b4a + "range": [ + 1, + null -+ ] ++ ], ++ "default_value": 128 + }, + "spark.driver.memory": { -+ "desc": "用于驱动程序进程的内存量,影响驱动程序的性能和稳定性。增大此值可以缓解内存瓶颈,尤其是在驱动程序需要处理大量数据时。取值通常为如1g、2g等,具体取值应根据集群资源和任务需求进行调整。", ++ "desc": "用于驱动程序进程的内存量,影响驱动程序的性能和稳定性。增大此值可以缓解内存瓶颈,尤其是在驱动程序需要处理大量数据时。格式与 JVM 内存字符串相同,带有大小单位后缀('k'、'm'、'g'或't')(例如 512m、2g)取值通常为如1g、2g等,具体取值应根据集群资源和任务需求进行调整。", + "type": "discrete", + "dtype": "string", -+ "range": null ++ "range": [ ++ "512m", ++ "32g" ++ ], ++ "default_value": "4g" + }, + "spark.driver.memoryOverhead": { + "desc": "在集群模式下,每个驱动程序进程要分配的非堆内存量,用于存储非JVM内存的开销,如网络和文件系统的缓冲区。适当增加此值可以缓解由于内存不足导致的性能问题,尤其是在处理大数据集时。单位为MiB,默认值为driverMemory * spark.driver.memoryOverheadFactor。", + "type": "continuous", -+ "dtype": "string", -+ "range": [ -+ 384, -+ null -+ ] -+ }, -+ "spark.dynamicAllocation.initialExecutors": { -+ "desc": "如果启用了动态分配,则要运行的初始执行器数量。此参数影响任务启动时的资源分配,增大此值可以提高任务启动速度,缓解CPU瓶颈。取值应根据集群资源和任务需求进行调整,通常为1到数个executor。", -+ "type": "continuous", + "dtype": "int", + "range": [ -+ 0, -+ null -+ ] -+ }, -+ "spark.dynamicAllocation.maxExecutors": { -+ "desc": "如果启用了动态分配,则执行器数量的上限。该参数限制了在负载高峰时可用的资源,增大此值可以提高并行度,缓解CPU瓶颈。取值应根据集群资源和任务需求进行调整,通常为数十到数百个executor。", -+ "type": "continuous", -+ "dtype": "string", -+ "range": [ -+ 0, -+ null -+ ] -+ }, -+ "spark.dynamicAllocation.minExecutors": { -+ "desc": "如果启用了动态分配,则执行器数量的下限。该参数确保在负载较低时仍有一定的资源可用。增大此值可以缓解CPU和内存瓶颈。取值应根据集群资源和任务需求进行调整,通常为1到数个executor。", -+ "type": "continuous", -+ "dtype": "int", -+ "range": [ -+ 0, -+ null -+ ] -+ }, -+ "spark.executor.cores": { -+ "desc": "每个执行器上要使用的核心数量,影响并行度和任务执行速度。增大此值可以缓解CPU瓶颈,但需确保集群资源足够。通常取值为1到数个核心,具体取值应根据任务并行需求和集群资源进行调整。", -+ "type": "continuous", -+ "dtype": "int", -+ "range": [ -+ 1, -+ null -+ ] ++ 384, ++ 32768 ++ ], ++ "default_value": 3276 + }, + "spark.executor.memory": { -+ "desc": "每个执行器进程使用的内存量,通常以字节为单位(如1g、2g等)进行指定。增大此值可以缓解内存瓶颈,尤其是在处理大数据集时。具体取值应根据集群资源和任务需求进行调整。", ++ "desc": "每个执行器进程使用的内存量,格式与 JVM 内存字符串相同,带有大小单位后缀('k'、'm'、'g'或't')(例如 512m、2g)。通常以字节为单位(如1g、2g等)进行指定。增大此值可以缓解内存瓶颈,尤其是在处理大数据集时。具体取值应根据集群资源和任务需求进行调整。", + "type": "continuous", + "dtype": "string", + "range": [ -+ null, -+ null -+ ] ++ "512m", ++ "32g" ++ ], ++ "default_value": "4g" + }, + "spark.executor.memoryOverhead": { + "desc": "每个执行器进程要分配的额外内存量,以 MiB 为单位,主要用于存储非JVM内存的开销,包括 VM 开销、内部字符串和其他本机开销等。通常建议的取值为执行器内存的 6-10%。增大此值可以缓解内存瓶颈,尤其是在使用外部库时。具体取值应根据任务需求进行调整。", @@ -5006,19 +7433,9 @@ index 0000000..6183b4a + "dtype": "int", + "range": [ + 384, -+ null -+ ] -+ }, -+ "spark.io.compression.codec": { -+ "desc": "用于压缩内部数据的编解码器,例如 RDD 分区、事件日志、广播变量和混洗输出。支持的编码格式包括 LZ4、LZF、Snappy 和 ZSTD。使用压缩可以减少磁盘 IO 和网络带宽的消耗,提升数据传输效率。不同的编码格式在压缩比和压缩速度上有所不同,例如 Snappy 压缩速度快但压缩比相对较低,而 Gzip 压缩比高但速度较慢。默认情况下,Spark 提供四种编解码器:lz4、lzf、snappy 和 zstd。", -+ "type": "discrete", -+ "dtype": "string", -+ "range": [ -+ "lz4", -+ "lzf", -+ "snappy", -+ "zstd" -+ ] ++ 32768 ++ ], ++ "default_value": 384 + }, + "spark.kryo.referenceTracking": { + "desc": "启用或禁用Kryo序列化的引用跟踪。启用引用跟踪可以减少内存使用和序列化时间,特别是在处理大量重复对象时。这在对象图具有循环时是必需的,并且如果它们包含同一对象的多个副本,则对效率很有用。如果知道情况并非如此,可以禁用它以提高性能。", @@ -5027,7 +7444,8 @@ index 0000000..6183b4a + "range": [ + "true", + "false" -+ ] ++ ], ++ "default_value": "true" + }, + "spark.locality.wait": { + "desc": "在调度任务时,Spark会等待数据本地性(locality)的时间,以提高任务的执行效率。该参数定义了在放弃并将任务调度到不太本地化的节点之前,Spark等待的时间。等待时间会逐步遍历多个本地化级别(进程本地、节点本地、机架本地等)。如果任务执行时间较长且本地化效果不佳,可以考虑增加此设置,但默认值通常能够满足大多数场景的需求。适当调整此参数可以在CPU和网络瓶颈之间取得平衡。", @@ -5036,7 +7454,8 @@ index 0000000..6183b4a + "range": [ + null, + null -+ ] ++ ], ++ "default_value": "3s" + }, + "spark.memory.fraction": { + "desc": "spark.memory.fraction参数用于指定执行和存储所使用的内存比例,计算方式为(堆空间 - 300MB)的部分。此值越低,溢出和缓存数据驱逐的频率越高。增大此值可以提高内存利用率,缓解内存瓶颈。建议根据具体任务特性进行调整,默认值为0.6。", @@ -5045,7 +7464,8 @@ index 0000000..6183b4a + "range": [ + 0.0, + 1.0 -+ ] ++ ], ++ "default_value": "0.6" + }, + "spark.memory.offHeap.enabled": { + "desc": "启用off-heap内存管理,允许Spark使用JVM外的内存。启用后可以缓解内存瓶颈,尤其是在处理大数据集时。若启用堆外内存使用,则必须设置spark.memory.offHeap.size为正值。", @@ -5054,25 +7474,28 @@ index 0000000..6183b4a + "range": [ + "true", + "false" -+ ] ++ ], ++ "default_value": "true" + }, + "spark.memory.offHeap.size": { -+ "desc": "用于堆外分配的内存绝对量,以字节为单位。此设置不会影响堆内存使用情况,因此,如果您的执行器总内存消耗必须符合某个硬性限制,请务必相应地缩小 JVM 堆大小。当 spark.memory.offHeap.enabled=true 时,此值必须设置为正值。增大此值可以提高内存利用率,缓解内存瓶颈。取值通常为如512m、1g等,具体取值应根据集群资源和任务需求进行调整。", ++ "desc": "用于堆外分配的内存绝对量,以字节为单位。格式与 JVM 内存字符串相同,带有大小单位后缀('k'、'm'、'g'或't')(例如 512m、2g)。此设置不会影响堆内存使用情况,因此,如果您的执行器总内存消耗必须符合某个硬性限制,请务必相应地缩小 JVM 堆大小。当 spark.memory.offHeap.enabled=true 时,此值必须设置为正值。增大此值可以提高内存利用率,缓解内存瓶颈。取值通常为如512m、1g等,具体取值应根据集群资源和任务需求进行调整。", + "type": "continuous", -+ "dtype": "int", ++ "dtype": "string", + "range": [ + 0, -+ null -+ ] ++ "32g" ++ ], ++ "default_value": "35g" + }, + "spark.reducer.maxSizeInFlight": { + "desc": "每个 reduce 任务同时获取的映射输出的最大大小(以 MiB 为单位)。此参数影响 shuffle 性能,增大此值可以提高网络利用率,缓解网络瓶颈。建议根据网络带宽和任务特性进行调整,通常取值为 48m、64m 等。为了避免每个 reduce 任务的固定内存开销,建议将此值设置得较小。", + "type": "continuous", -+ "dtype": "string", ++ "dtype": "int", + "range": [ + 1, -+ null -+ ] ++ 32768 ++ ], ++ "default_value": 48 + }, + "spark.shuffle.compress": { + "desc": "启用shuffle数据压缩以减少网络传输和磁盘IO的开销。通常建议启用此功能,以缓解disk IO和network瓶颈。启用时需注意CPU的额外开销。取值为true或false。", @@ -5081,16 +7504,18 @@ index 0000000..6183b4a + "range": [ + "true", + "false" -+ ] ++ ], ++ "default_value": "true" + }, + "spark.shuffle.file.buffer": { -+ "desc": "每个混洗文件输出流的内存缓冲区的大小(以 KiB 为单位)。这些缓冲区可以减少在创建中间混洗文件时的磁盘寻址和系统调用次数,从而提高性能。增大此值可以提高磁盘IO性能,缓解disk IO瓶颈。取值通常为如32k、64k等,具体取值应根据任务特性进行调整。", ++ "desc": "每个混洗文件输出流的内存缓冲区的大小(以 KiB 为单位)。这些缓冲区可以减少在创建中间混洗文件时的磁盘寻址和系统调用次数,从而提高性能。增大此值可以提高磁盘IO性能,缓解disk IO瓶颈。取值通常为如32、64等,具体取值应根据任务特性进行调整。", + "type": "continuous", -+ "dtype": "string", ++ "dtype": "int", + "range": [ + 1, + 1048576 -+ ] ++ ], ++ "default_value": 32 + }, + "spark.speculation": { + "desc": "启用或禁用任务推测执行。推测执行可以在某些任务运行缓慢时启动额外的副本,以减少整体作业的执行时间。此参数可以缓解由于某些任务的CPU或内存瓶颈导致的性能问题。取值为true时启用推测执行,false时禁用。", @@ -5099,7 +7524,8 @@ index 0000000..6183b4a + "range": [ + "true", + "false" -+ ] ++ ], ++ "default_value": "false" + }, + "spark.sql.adaptive.maxNumPostShufflePartitions": { + "desc": "设置Spark SQL自适应查询执行中,Shuffle后最大分区数。此参数可以帮助优化Shuffle后的数据分布,适当调整可以缓解内存和CPU瓶颈。增大此值可以提高并行度,减小此值可以减少分区数量。建议根据具体的作业需求进行调整,以达到最佳性能。", @@ -5108,16 +7534,18 @@ index 0000000..6183b4a + "range": [ + 1, + null -+ ] ++ ], ++ "default_value": 500 + }, + "spark.sql.files.maxPartitionBytes": { -+ "desc": "设置Spark SQL读取文件时每个分区的最大字节数。此配置仅在使用基于文件的源(如Parquet、JSON和ORC)时有效。增大此值可以减少分区数量,但可能导致内存瓶颈;减小此值可以增加分区数量,提高并行度,从而影响任务的并行性和内存使用。该参数的默认值为128MB。", ++ "desc": "设置Spark SQL读取文件时每个分区的最大字节数。格式与 JVM 内存字符串相同,带有大小单位后缀('k'、'm'、'g'或't')(例如 512m、2g)。此配置仅在使用基于文件的源(如Parquet、JSON和ORC)时有效。增大此值可以减少分区数量,但可能导致内存瓶颈;减小此值可以增加分区数量,提高并行度,从而影响任务的并行性和内存使用。该参数的默认值为128MB。", + "type": "continuous", + "dtype": "string", + "range": [ + 1, + null -+ ] ++ ], ++ "default_value": "128m" + }, + "spark.task.maxFailures": { + "desc": "在放弃作业之前,任何特定任务连续失败的次数。此参数可以帮助在任务失败时进行重试,从而提高作业的成功率。适当增加此值可以缓解由于临时故障导致的任务失败,但过高的值可能会导致资源浪费。", @@ -5126,19 +7554,40 @@ index 0000000..6183b4a + "range": [ + 1, + null -+ ] ++ ], ++ "default_value": 1000 ++ }, ++ "spark.sql.shuffle.partitions": { ++ "desc": "在进行连接或聚合操作时,用于数据混洗的默认分区数。分区数影响并行度、任务调度开销和每个任务处理的数据量。注意:对于结构化流,此配置在从同一检查点位置重新启动查询之间不能更改。", ++ "type": "continuous", ++ "dtype": "int", ++ "range": [ ++ 1, ++ null ++ ], ++ "default_value": 400 ++ }, ++ "spark.sql.sources.parallelPartitionDiscovery.parallelism": { ++ "desc": "该参数控制在发现数据源目录分区时,用于并行扫描分区路径的并行度(即并发任务数)。较高的并行度可以加快大规模分区目录的元数据发现速度,尤其是在分区数量非常多(如成千上万个分区)的情况下。一般与spark.sql.shuffle.partitions参数保持一致", ++ "type": "continuous", ++ "dtype": "int", ++ "range": [ ++ 1, ++ null ++ ], ++ "default_value": 10000 + } -+} ++ } \ No newline at end of file diff --git a/copilot-tune/src/knowledge_base/knob_params/system.json b/copilot-tune/src/knowledge_base/knob_params/system.json new file mode 100644 -index 0000000..93438ea +index 0000000..25a4c64 --- /dev/null +++ b/copilot-tune/src/knowledge_base/knob_params/system.json @@ -0,0 +1,1395 @@ +{ + "kernel.sched_cfs_bandwidth_slice_us": { -+ "desc": "1. 当需要更精细控制CFS带宽分配时(如高负载容器环境),可适当减小该值(默认5000微秒),但需注意过小会增加调度开销\n2. 在CPU资源充足且需要减少调度开销的场景下,可增大该值以减少全局时间池的分配频率", ++ "desc": "1. 当需要更精细控制CFS带宽分配时(如高负载容器环境),可适当减小该值(默认5000微秒),但需注意过小会增加调度开销;2. 在CPU资源充足且需要减少调度开销的场景下,可增大该值以减少全局时间池的分配频率", + "type": "continuous", + "range": [ + 1000, @@ -5149,7 +7598,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.sched_cfs_bandwidth_slice_us" + }, + "kernel.sched_latency_ns": { -+ "desc": "1. 当系统运行高优先级实时任务时,若出现调度延迟过高的情况,可适当减小该值以提高调度响应速度\n\n2. 对于CPU密集型负载且任务数量较多(超过8个逻辑CPU)的系统,应增大该值以减少上下文切换开销", ++ "desc": "1. 当系统运行高优先级实时任务时,若出现调度延迟过高的情况,可适当减小该值以提高调度响应速度;2. 对于CPU密集型负载且任务数量较多(超过8个逻辑CPU)的系统,应增大该值以减少上下文切换开销", + "type": "continuous", + "range": [ + 1000000, @@ -5160,7 +7609,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.sched_latency_ns" + }, + "kernel.sched_min_granularity_ns": { -+ "desc": "1. 当系统负载较高且存在大量短时间运行的进程时,可以适当增大该值以减少上下文切换开销,建议从默认值(通常为1,000,000 ns)逐步增加测试,观察性能变化\n\n2. 对于CPU密集型工作负载且进程运行时间普遍较长的情况,可以适当减小该值以提高系统响应能力,建议从默认值逐步减少测试", ++ "desc": "1. 当系统负载较高且存在大量短时间运行的进程时,可以适当增大该值以减少上下文切换开销,建议从默认值(通常为1,000,000 ns)逐步增加测试,观察性能变化;2. 对于CPU密集型工作负载且进程运行时间普遍较长的情况,可以适当减小该值以提高系统响应能力,建议从默认值逐步减少测试", + "type": "continuous", + "range": [ + 1000000, @@ -5171,7 +7620,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.sched_min_granularity_ns" + }, + "transparent_hugepage.defrag": { -+ "desc": "1. 对于需要低延迟的应用(如数据库、实时系统),建议禁用该参数(设置为never),以避免因内存碎片整理导致的性能波动\n2. 对于内存密集型且对延迟不敏感的应用(如批处理作业),建议启用该参数(设置为always或defer+madvise),以提高大内存页使用率减少TLB缺失", ++ "desc": "1. 对于需要低延迟的应用(如数据库、实时系统),建议禁用该参数(设置为never),以避免因内存碎片整理导致的性能波动;2. 对于内存密集型且对延迟不敏感的应用(如批处理作业),建议启用该参数(设置为always或defer+madvise),以提高大内存页使用率减少TLB缺失", + "range": [ + "always", + "defer", @@ -5185,7 +7634,7 @@ index 0000000..93438ea + "get": "cat /sys/kernel/mm/transparent_hugepage/defrag | sed -n 's/.*\\[\\(.*\\)\\].*/\\1/p'" + }, + "transparent_hugepage.enabled": { -+ "desc": "1. 对于延迟敏感型应用(如数据库、实时系统),建议禁用(设置为never或madvise),以避免因透明大页碎片整理导致的不可预测延迟\n\n2. 对于内存密集型批处理作业(如科学计算、大数据处理),建议启用(设置为always),以通过减少页表项提升内存访问效率", ++ "desc": "1. 对于延迟敏感型应用(如数据库、实时系统),建议禁用(设置为never或madvise),以避免因透明大页碎片整理导致的不可预测延迟;2. 对于内存密集型批处理作业(如科学计算、大数据处理),建议启用(设置为always),以通过减少页表项提升内存访问效率", + "range": [ + "always", + "madvise", @@ -5197,7 +7646,7 @@ index 0000000..93438ea + "get": "cat /sys/kernel/mm/transparent_hugepage/enabled | sed -n 's/.*\\[\\(.*\\)\\].*/\\1/p'" + }, + "net.netfilter.nf_conntrack_max": { -+ "desc": "1. 当服务器处理大量并发连接(如超过默认值65536)时,若出现\"table full\"相关内核日志或连接跟踪表频繁满导致丢包,应增加该值(通常设置为总内存MB数/16384,如8GB内存可设为524288)\n\n2. 在高并发短连接场景下,若nf_conntrack_count经常接近nf_conntrack_max值,应结合连接跟踪超时时间(nf_conntrack_tcp_timeout_*系列参数)一同调整,避免过早占满跟踪表", ++ "desc": "1. 当服务器处理大量并发连接(如超过默认值65536)时,若出现\"table full\"相关内核日志或连接跟踪表频繁满导致丢包,应增加该值(通常设置为总内存MB数/16384,如8GB内存可设为524288);2. 在高并发短连接场景下,若nf_conntrack_count经常接近nf_conntrack_max值,应结合连接跟踪超时时间(nf_conntrack_tcp_timeout_*系列参数)一同调整,避免过早占满跟踪表", + "type": "continuous", + "range": [ + 0, @@ -5208,7 +7657,7 @@ index 0000000..93438ea + "get": "sysctl -n net.netfilter.nf_conntrack_max" + }, + "kernel.pid_max": { -+ "desc": "- 当系统频繁达到当前pid_max限制导致无法创建新进程时,应适当增大该值,通常可设置为默认值(32768)的2-4倍\n- 在容器化环境中若需支持大量短生命周期进程,建议将pid_max提升至262144(2^18)以匹配现代Linux内核支持的上限", ++ "desc": "- 当系统频繁达到当前pid_max限制导致无法创建新进程时,应适当增大该值,通常可设置为默认值(32768)的2-4倍 , - 在容器化环境中若需支持大量短生命周期进程,建议将pid_max提升至262144(2^18)以匹配现代Linux内核支持的上限", + "type": "continuous", + "range": [ + 1048576, @@ -5219,7 +7668,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.pid_max" + }, + "kernel.shmmni": { -+ "desc": "- 当运行需要大量共享内存段的数据库(如Oracle)或科学计算应用时,若出现\"SHMMNI\"相关错误日志,应增加该值至超过应用实际需求的20%冗余量\n\n- 在容器化或虚拟化环境中,若单个物理节点需承载多个共享内存密集型实例,应按实例数乘以单个实例需求量的1.5倍进行设置", ++ "desc": "- 当运行需要大量共享内存段的数据库(如Oracle)或科学计算应用时,若出现\"SHMMNI\"相关错误日志,应增加该值至超过应用实际需求的20%冗余量。- 在容器化或虚拟化环境中,若单个物理节点需承载多个共享内存密集型实例,应按实例数乘以单个实例需求量的1.5倍进行设置", + "type": "continuous", + "range": [ + 1024, @@ -5230,7 +7679,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.shmmni" + }, + "kernel.shmmax": { -+ "desc": "1. 当运行需要大量共享内存的应用(如Oracle数据库、SAP HANA等)时,如果应用报错提示共享内存不足,需要将kernel.shmmax设置为至少等于所有共享内存段总和的80%-90%,但不超过物理内存的90%\n\n2. 在容器化或虚拟化环境中,当多个实例需要共享内存通信且出现性能瓶颈时,应根据每个实例的实际共享内存需求总和来调整kernel.shmmax,确保其值大于所有实例需求之和", ++ "desc": "1. 当运行需要大量共享内存的应用(如Oracle数据库、SAP HANA等)时,如果应用报错提示共享内存不足,需要将kernel.shmmax设置为至少等于所有共享内存段总和的80%-90%,但不超过物理内存的90%;2. 在容器化或虚拟化环境中,当多个实例需要共享内存通信且出现性能瓶颈时,应根据每个实例的实际共享内存需求总和来调整kernel.shmmax,确保其值大于所有实例需求之和", + "type": "continuous", + "range": [ + 17179869184, @@ -5241,7 +7690,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.shmmax" + }, + "kernel.shmall": { -+ "desc": "1. 当系统运行需要大量共享内存的应用(如Oracle数据库)且出现\"SHMMAX too small\"错误时,需要增加该值至物理内存的80%左右\n\n2. 当系统频繁使用共享内存但未充分利用物理内存时,可适当降低该值以避免资源浪费,通常设置为(总物理内存 - 系统保留内存) / PAGE_SIZE", ++ "desc": "1. 当系统运行需要大量共享内存的应用(如Oracle数据库)且出现\"SHMMAX too small\"错误时,需要增加该值至物理内存的80%左右;2. 当系统频繁使用共享内存但未充分利用物理内存时,可适当降低该值以避免资源浪费,通常设置为(总物理内存 - 系统保留内存) / PAGE_SIZE", + "type": "continuous", + "range": [ + 1073741824, @@ -5252,7 +7701,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.shmall" + }, + "kernel.core_uses_pid": { -+ "desc": "1. 当需要快速定位崩溃进程时,建议启用该参数(设为1),通过core文件名中的PID可以快速关联到具体进程信息\n\n2. 当系统频繁产生core文件且磁盘空间紧张时,建议禁用该参数(设为0),避免文件名过长导致管理困难", ++ "desc": "1. 当需要快速定位崩溃进程时,建议启用该参数(设为1),通过core文件名中的PID可以快速关联到具体进程信息;2. 当系统频繁产生core文件且磁盘空间紧张时,建议禁用该参数(设为0),避免文件名过长导致管理困难", + "range": [ + "0", + "1" @@ -5263,7 +7712,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.core_uses_pid" + }, + "kernel.msgmni": { -+ "desc": "1. 当系统日志频繁出现\"msgmni limit reached\"错误时,表明当前队列数量不足以支撑应用需求,需要增加该值\n\n2. 对于频繁使用System V消息队列的中间件应用(如Oracle数据库),建议将该值设置为进程数量的4倍以上", ++ "desc": "1. 当系统日志频繁出现\"msgmni limit reached\"错误时,表明当前队列数量不足以支撑应用需求,需要增加该值;2. 对于频繁使用System V消息队列的中间件应用(如Oracle数据库),建议将该值设置为进程数量的4倍以上", + "type": "continuous", + "range": [ + 8000, @@ -5274,7 +7723,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.msgmni" + }, + "kernel.msgmax": { -+ "desc": "1. 当应用频繁发送超过当前 kernel.msgmax 限制的大消息导致消息队列操作失败时,应适当增大该值,但需确保不超过系统可用内存的合理比例\n\n2. 若系统存在大量小消息传输且 msgmax 设置过大导致内存碎片化,应降低该值以匹配实际消息大小,通常不低于 8KB", ++ "desc": "1. 当应用频繁发送超过当前 kernel.msgmax 限制的大消息导致消息队列操作失败时,应适当增大该值,但需确保不超过系统可用内存的合理比例;2. 若系统存在大量小消息传输且 msgmax 设置过大导致内存碎片化,应降低该值以匹配实际消息大小,通常不低于 8KB", + "type": "continuous", + "range": [ + 4096, @@ -5285,7 +7734,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.msgmax" + }, + "kernel.msgmnb": { -+ "desc": "增加该值当消息队列频繁达到默认上限(通常为16384字节)导致应用报错时 \n降低该值当系统存在大量闲置消息队列且需要减少内核内存占用时", ++ "desc": "增加该值当消息队列频繁达到默认上限(通常为16384字节)导致应用报错时 , 降低该值当系统存在大量闲置消息队列且需要减少内核内存占用时", + "type": "continuous", + "range": [ + 4096, @@ -5296,7 +7745,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.msgmnb" + }, + "kernel.hung_task_timeout_secs": { -+ "desc": "1. 当系统频繁出现hung_task警告但实际任务仍在正常执行时,可适当增大该值(如从默认120秒调整为300秒),避免误报\n\n2. 对于存储密集型应用(如数据库服务器),若观察到存储设备响应较慢导致任务频繁超时,应结合存储延迟指标调高该值至存储设备平均响应时间的2-3倍", ++ "desc": "1. 当系统频繁出现hung_task警告但实际任务仍在正常执行时,可适当增大该值(如从默认120秒调整为300秒),避免误报;2. 对于存储密集型应用(如数据库服务器),若观察到存储设备响应较慢导致任务频繁超时,应结合存储延迟指标调高该值至存储设备平均响应时间的2-3倍", + "type": "continuous", + "range": [ + 30, @@ -5307,7 +7756,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.hung_task_timeout_secs" + }, + "kernel.nmi_watchdog": { -+ "desc": "1. 在生产服务器上建议禁用该参数(设置为0),因为NMI watchdog会周期性触发NMI中断,可能对系统性能产生轻微影响,尤其在高负载场景下\n\n2. 在调试内核死锁或硬件问题时可以临时启用(设置为1),帮助捕获长时间关中断导致的挂起问题", ++ "desc": "1. 在生产服务器上建议禁用该参数(设置为0),因为NMI watchdog会周期性触发NMI中断,可能对系统性能产生轻微影响,尤其在高负载场景下;2. 在调试内核死锁或硬件问题时可以临时启用(设置为1),帮助捕获长时间关中断导致的挂起问题", + "range": [ + "0", + "1" @@ -5318,7 +7767,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.nmi_watchdog" + }, + "kernel.sched_rt_runtime_us": { -+ "desc": "1. 当系统需要运行更多实时任务时,可以适当增加该值(但不超过sched_rt_period_us的95%),默认值950000微秒可提高到990000微秒\n\n2. 当非实时任务出现严重饥饿现象时,应减小该值(建议不低于800000微秒),为普通任务保留更多CPU时间", ++ "desc": "1. 当系统需要运行更多实时任务时,可以适当增加该值(但不超过sched_rt_period_us的95%),默认值950000微秒可提高到990000微秒;2. 当非实时任务出现严重饥饿现象时,应减小该值(建议不低于800000微秒),为普通任务保留更多CPU时间", + "type": "continuous", + "range": [ + 950000, @@ -5329,7 +7778,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.sched_rt_runtime_us" + }, + "kernel.timer_migration": { -+ "desc": "1. 在NUMA架构服务器上运行低延迟应用时,若出现跨节点时钟中断导致的性能抖动,应禁用该参数以保持本地CPU处理时钟中断\n\n2. 当系统负载主要集中运行在单个NUMA节点且出现时钟中断处理不均衡时,可启用该参数允许时钟中断在CPU间迁移", ++ "desc": "1. 在NUMA架构服务器上运行低延迟应用时,若出现跨节点时钟中断导致的性能抖动,应禁用该参数以保持本地CPU处理时钟中断;2. 当系统负载主要集中运行在单个NUMA节点且出现时钟中断处理不均衡时,可启用该参数允许时钟中断在CPU间迁移", + "range": [ + "0", + "1" @@ -5340,7 +7789,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.timer_migration" + }, + "kernel.threads-max": { -+ "desc": "1. 当系统频繁出现\"fork: Cannot allocate memory\"错误或应用程序因无法创建新线程而崩溃时,需要增加该值。可通过计算系统内存容量和单个线程平均内存占用来确定合理上限,通常设置为物理内存(MB)/8。\n\n2. 在高并发容器环境或运行大量轻量级线程的应用(如Java微服务)时,若/proc/sys/kernel/pid_max已调高但仍有线程创建限制,应将该值提升至至少pid_max值的2倍。", ++ "desc": "1. 当系统频繁出现\"fork: Cannot allocate memory\"错误或应用程序因无法创建新线程而崩溃时,需要增加该值。可通过计算系统内存容量和单个线程平均内存占用来确定合理上限,通常设置为物理内存(MB)/8。;2. 在高并发容器环境或运行大量轻量级线程的应用(如Java微服务)时,若/proc/sys/kernel/pid_max已调高但仍有线程创建限制,应将该值提升至至少pid_max值的2倍。", + "type": "continuous", + "range": [ + 655360, @@ -5351,7 +7800,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.threads-max" + }, + "kernel.sysrq": { -+ "desc": "1. 生产环境中建议设置为1(仅启用基本功能)或0(完全禁用),避免通过SysRq组合键意外触发系统操作,降低安全风险\n\n2. 调试崩溃或死机问题时临时设置为1或更大值(如176/128),启用更多调试功能后需立即恢复默认安全配置", ++ "desc": "1. 生产环境中建议设置为1(仅启用基本功能)或0(完全禁用),避免通过SysRq组合键意外触发系统操作,降低安全风险;2. 调试崩溃或死机问题时临时设置为1或更大值(如176/128),启用更多调试功能后需立即恢复默认安全配置", + "range": [ + "0", + "1" @@ -5362,7 +7811,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.sysrq" + }, + "kernel.sched_autogroup_enabled": { -+ "desc": "1. 在服务器环境下建议禁用该参数(设为0),因为自动任务分组主要针对桌面交互程序优化,服务器工作负载通常不需要这种调度特性\n\n2. 当服务器运行大量短时交互式任务且出现调度延迟问题时,可尝试启用(设为1)观察效果,但需注意可能影响批处理任务的吞吐量", ++ "desc": "1. 在服务器环境下建议禁用该参数(设为0),因为自动任务分组主要针对桌面交互程序优化,服务器工作负载通常不需要这种调度特性;2. 当服务器运行大量短时交互式任务且出现调度延迟问题时,可尝试启用(设为1)观察效果,但需注意可能影响批处理任务的吞吐量", + "range": [ + "0", + "1" @@ -5373,7 +7822,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.sched_autogroup_enabled" + }, + "kernel.numa_balancing": { -+ "desc": "1. 当系统运行NUMA架构且应用存在跨节点内存访问时,应启用该参数(设置为1)以减少远程内存访问延迟\n2. 对于内存密集型且对延迟敏感的应用,若观察到较高比例的跨节点内存访问,建议禁用该参数(设置为0)以避免自动平衡带来的性能波动", ++ "desc": "1. 当系统运行NUMA架构且应用存在跨节点内存访问时,应启用该参数(设置为1)以减少远程内存访问延迟;2. 对于内存密集型且对延迟敏感的应用,若观察到较高比例的跨节点内存访问,建议禁用该参数(设置为0)以避免自动平衡带来的性能波动", + "range": [ + "0", + "1" @@ -5384,7 +7833,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.numa_balancing" + }, + "kernel.randomize_va_space": { -+ "desc": "1. 当系统运行安全性要求较高的服务时,建议保持默认值2(完全随机化),以增强对抗内存攻击的能力\n\n2. 若应用程序出现因地址随机化导致的兼容性问题,且运行环境可信,可临时调整为1(仅对数据段随机化)或0(禁用)进行测试", ++ "desc": "1. 当系统运行安全性要求较高的服务时,建议保持默认值2(完全随机化),以增强对抗内存攻击的能力;2. 若应用程序出现因地址随机化导致的兼容性问题,且运行环境可信,可临时调整为1(仅对数据段随机化)或0(禁用)进行测试", + "type": "continuous", + "range": [ + 0, @@ -5395,7 +7844,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.randomize_va_space" + }, + "kernel.dmesg_restrict": { -+ "desc": "1. 如果系统需要满足安全合规要求(如PCI-DSS、HIPAA等),建议设置为1以限制普通用户查看内核日志,防止敏感信息泄露\n2. 在需要开发调试或故障排查的环境中,建议设置为0以便非特权用户也能查看完整的系统日志信息", ++ "desc": "1. 如果系统需要满足安全合规要求(如PCI-DSS、HIPAA等),建议设置为1以限制普通用户查看内核日志,防止敏感信息泄露;2. 在需要开发调试或故障排查的环境中,建议设置为0以便非特权用户也能查看完整的系统日志信息", + "range": [ + "0", + "1" @@ -5406,7 +7855,7 @@ index 0000000..93438ea + "get": "sysctl -n kernel.dmesg_restrict" + }, + "vm.swappiness": { -+ "desc": "1. 对于内存密集型应用(如数据库、缓存服务),建议将 vm.swappiness 设置为 10-30 以减少交换空间使用,优先利用物理内存\n2. 当系统频繁发生 OOM (Out of Memory) 时,可适当提高 vm.swappiness 至 60-80 以增加交换空间使用,避免进程被强制终止", ++ "desc": "1. 对于内存密集型应用(如数据库、缓存服务),建议将 vm.swappiness 设置为 10-30 以减少交换空间使用,优先利用物理内存;2. 当系统频繁发生 OOM (Out of Memory) 时,可适当提高 vm.swappiness 至 60-80 以增加交换空间使用,避免进程被强制终止", + "type": "continuous", + "range": [ + 0, @@ -5417,7 +7866,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.swappiness" + }, + "vm.vfs_cache_pressure": { -+ "desc": "1. 当系统频繁进行目录和inode缓存回收导致性能下降时,可适当降低该值(如设为50-100),减少内核回收缓存内存的频率\n\n2. 当系统内存充足但缓存利用率不足时,可适当提高该值(如设为150-200),促使内核更积极地回收缓存内存", ++ "desc": "1. 当系统频繁进行目录和inode缓存回收导致性能下降时,可适当降低该值(如设为50-100),减少内核回收缓存内存的频率;2. 当系统内存充足但缓存利用率不足时,可适当提高该值(如设为150-200),促使内核更积极地回收缓存内存", + "type": "continuous", + "range": [ + 0, @@ -5428,7 +7877,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.vfs_cache_pressure" + }, + "vm.dirty_background_ratio": { -+ "desc": "1. 对于写入密集型应用(如数据库服务器),建议将值从默认的10%提高到15-20%,以减少频繁的后台刷写对I/O性能的影响\n\n2. 对于内存较小的系统(如低于8GB),建议保持默认值或降至5-10%,以避免过多脏页堆积导致内存压力", ++ "desc": "1. 对于写入密集型应用(如数据库服务器),建议将值从默认的10%提高到15-20%,以减少频繁的后台刷写对I/O性能的影响;2. 对于内存较小的系统(如低于8GB),建议保持默认值或降至5-10%,以避免过多脏页堆积导致内存压力", + "type": "continuous", + "range": [ + 0, @@ -5439,7 +7888,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.dirty_background_ratio" + }, + "vm.dirty_ratio": { -+ "desc": "1. 当系统频繁因脏页刷盘导致I/O瓶颈时,可适当降低该值(如从默认20%降至10%),以减少单次刷盘的数据量,但会增加刷盘频率\n\n2. 若系统内存较大且主要处理顺序写入负载,可适当提高该值(如升至30%-40%),利用内存缓冲更多脏数据,减少磁盘I/O次数", ++ "desc": "1. 当系统频繁因脏页刷盘导致I/O瓶颈时,可适当降低该值(如从默认20%降至10%),以减少单次刷盘的数据量,但会增加刷盘频率;2. 若系统内存较大且主要处理顺序写入负载,可适当提高该值(如升至30%-40%),利用内存缓冲更多脏数据,减少磁盘I/O次数", + "type": "continuous", + "range": [ + 0, @@ -5450,7 +7899,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.dirty_ratio" + }, + "vm.stat_interval": { -+ "desc": "1. 当系统需要更频繁监控内存使用情况(如内存压力大或频繁交换时),可适当减小该值(如从默认10秒降至5秒),但需注意增加的系统开销 \n\n2. 在内存使用稳定且低负载环境中,可增大该值(如调至30秒)以减少/proc/vmstat的更新频率,降低内核开销", ++ "desc": "1. 当系统需要更频繁监控内存使用情况(如内存压力大或频繁交换时),可适当减小该值(如从默认10秒降至5秒),但需注意增加的系统开销 ;2. 在内存使用稳定且低负载环境中,可增大该值(如调至30秒)以减少/proc/vmstat的更新频率,降低内核开销", + "type": "continuous", + "range": [ + 1, @@ -5461,7 +7910,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.stat_interval" + }, + "vm.dirty_expire_centisecs": { -+ "desc": "1. 对于需要快速持久化数据的应用(如数据库),建议将值调低至100-300(1-3秒),以减少数据丢失风险 \n2. 对于写入密集型负载且对延迟敏感的应用,可适当提高至1000-3000(10-30秒),通过合并更多写操作来提升I/O吞吐量", ++ "desc": "1. 对于需要快速持久化数据的应用(如数据库),建议将值调低至100-300(1-3秒),以减少数据丢失风险 ;2. 对于写入密集型负载且对延迟敏感的应用,可适当提高至1000-3000(10-30秒),通过合并更多写操作来提升I/O吞吐量", + "type": "continuous", + "range": [ + 100, @@ -5472,7 +7921,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.dirty_expire_centisecs" + }, + "vm.dirty_writeback_centisecs": { -+ "desc": "1. 当系统频繁出现I/O等待或磁盘写入延迟较高时,可适当降低该值(如从默认500调整为200-300),以加快脏页回写频率,减少突发写入导致的性能波动\n\n2. 对于写入密集型负载且使用电池供电的设备(如服务器UPS环境),可适当提高该值(如设置为1000-1500),通过减少磁盘写入次数来降低I/O开销和能耗", ++ "desc": "1. 当系统频繁出现I/O等待或磁盘写入延迟较高时,可适当降低该值(如从默认500调整为200-300),以加快脏页回写频率,减少突发写入导致的性能波动;2. 对于写入密集型负载且使用电池供电的设备(如服务器UPS环境),可适当提高该值(如设置为1000-1500),通过减少磁盘写入次数来降低I/O开销和能耗", + "type": "continuous", + "range": [ + 100, @@ -5483,7 +7932,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.dirty_writeback_centisecs" + }, + "vm.overcommit_ratio": { -+ "desc": "1. 当物理服务器内存使用率长期低于50%且需要运行大量内存申请不确定的应用程序时,可适当提高该比例(如设置为80-90%)以提升内存利用率\n\n2. 在内存密集型应用场景下,若频繁触发OOM killer且监控显示实际内存使用接近物理内存总量,应降低该比例(如设置为50-70%)以避免过度承诺内存", ++ "desc": "1. 当物理服务器内存使用率长期低于50%且需要运行大量内存申请不确定的应用程序时,可适当提高该比例(如设置为80-90%)以提升内存利用率;2. 在内存密集型应用场景下,若频繁触发OOM killer且监控显示实际内存使用接近物理内存总量,应降低该比例(如设置为50-70%)以避免过度承诺内存", + "type": "continuous", + "range": [ + 0, @@ -5494,7 +7943,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.overcommit_ratio" + }, + "vm.overcommit_memory": { -+ "desc": "1. 当系统运行内存密集型应用且频繁触发OOM killer时,建议将值设为0(保守策略)或2(严格策略)以避免过度分配\n\n2. 当系统主要运行已知内存需求的批量任务且需要最大化内存利用率时,可设为1(总是允许过度分配)以提升吞吐量", ++ "desc": "1. 当系统运行内存密集型应用且频繁触发OOM killer时,建议将值设为0(保守策略)或2(严格策略)以避免过度分配;2. 当系统主要运行已知内存需求的批量任务且需要最大化内存利用率时,可设为1(总是允许过度分配)以提升吞吐量", + "range": [ + "0", + "1" @@ -5505,7 +7954,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.overcommit_memory" + }, + "vm.min_free_kbytes": { -+ "desc": "1. 当系统频繁触发直接内存回收(direct reclaim)导致性能下降时,需要增加该值以减少直接回收频率,建议设置为物理内存的1-3%\n\n2. 当系统存在大量不可移动页(unmovable pages)导致内存碎片化严重时,需适当提高该值以预留更多连续内存空间", ++ "desc": "1. 当系统频繁触发直接内存回收(direct reclaim)导致性能下降时,需要增加该值以减少直接回收频率,建议设置为物理内存的1-3%;2. 当系统存在大量不可移动页(unmovable pages)导致内存碎片化严重时,需适当提高该值以预留更多连续内存空间", + "type": "continuous", + "range": [ + 10240, @@ -5516,7 +7965,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.min_free_kbytes" + }, + "vm.page-cluster": { -+ "desc": "1. 当系统频繁进行大块连续内存交换时,可适当增大该值(默认3,建议范围3-10),减少交换操作的I/O开销\n\n2. 在SSD存储的交换分区环境中,由于随机访问性能较好,可降低该值(建议1-3)以减少单次交换延迟", ++ "desc": "1. 当系统频繁进行大块连续内存交换时,可适当增大该值(默认3,建议范围3-10),减少交换操作的I/O开销;2. 在SSD存储的交换分区环境中,由于随机访问性能较好,可降低该值(建议1-3)以减少单次交换延迟", + "type": "continuous", + "range": [ + 0, @@ -5527,7 +7976,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.page-cluster" + }, + "vm.max_map_count": { -+ "desc": "增加该值当运行内存密集型应用(如Elasticsearch或数据库)时出现\"max virtual memory areas vm.max_map_count [65530] is too low\"错误\n\n将该值设置为262144或更高当运行需要大量内存映射的Java应用(如Hadoop或Spark)时", ++ "desc": "增加该值当运行内存密集型应用(如Elasticsearch或数据库)时出现\"max virtual memory areas vm.max_map_count [65530] is too low\"错误。将该值设置为262144或更高当运行需要大量内存映射的Java应用(如Hadoop或Spark)时", + "type": "continuous", + "range": [ + 100000, @@ -5538,7 +7987,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.max_map_count" + }, + "vm.zone_reclaim_mode": { -+ "desc": "1. 当系统运行在NUMA架构且存在跨节点内存访问延迟问题时,建议将vm.zone_reclaim_mode设置为1,优先尝试在本地节点回收内存以减少远程访问延迟\n\n2. 当系统内存压力较大且本地节点回收效果不佳时,建议将vm.zone_reclaim_mode设置为0,允许从其他节点回收内存以提高整体回收效率", ++ "desc": "1. 当系统运行在NUMA架构且存在跨节点内存访问延迟问题时,建议将vm.zone_reclaim_mode设置为1,优先尝试在本地节点回收内存以减少远程访问延迟;2. 当系统内存压力较大且本地节点回收效果不佳时,建议将vm.zone_reclaim_mode设置为0,允许从其他节点回收内存以提高整体回收效率", + "range": [ + "0", + "1", @@ -5551,7 +8000,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.zone_reclaim_mode" + }, + "vm.watermark_scale_factor": { -+ "desc": "1. 当系统频繁触发直接内存回收(direct reclaim)且kswapd进程活跃度不足时,可适当降低该值(如从默认的10调整至5-8),使kswapd更早介入内存回收\n\n2. 在内存压力较大且kswapd持续运行的场景下,若观察到系统响应延迟增加,可尝试增大该值(如调整至15-20),延迟kswapd休眠时机以提升回收效率", ++ "desc": "1. 当系统频繁触发直接内存回收(direct reclaim)且kswapd进程活跃度不足时,可适当降低该值(如从默认的10调整至5-8),使kswapd更早介入内存回收;2. 在内存压力较大且kswapd持续运行的场景下,若观察到系统响应延迟增加,可尝试增大该值(如调整至15-20),延迟kswapd休眠时机以提升回收效率", + "type": "continuous", + "range": [ + 10, @@ -5562,7 +8011,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.watermark_scale_factor" + }, + "vm.numa_stat": { -+ "desc": "1. 当系统内存资源紧张且NUMA统计对当前业务场景不重要时,可将该参数设为0以降低统计精度,减少内存开销\n2. 在需要精确监控NUMA内存行为的高性能计算场景中,应保持该参数启用(默认值1)以获得完整统计信息", ++ "desc": "1. 当系统内存资源紧张且NUMA统计对当前业务场景不重要时,可将该参数设为0以降低统计精度,减少内存开销;2. 在需要精确监控NUMA内存行为的高性能计算场景中,应保持该参数启用(默认值1)以获得完整统计信息", + "range": [ + "0", + "1" @@ -5573,7 +8022,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.numa_stat" + }, + "vm.drop_caches": { -+ "desc": "1. 当系统内存压力较大且缓存占用过高时,可临时设置为3释放pagecache、dentries和inodes缓存,但不宜频繁操作以免影响性能\n\n2. 在运行内存密集型应用前,可设置为1仅释放pagecache,避免缓存干扰应用性能测试结果", ++ "desc": "1. 当系统内存压力较大且缓存占用过高时,可临时设置为3释放pagecache、dentries和inodes缓存,但不宜频繁操作以免影响性能;2. 在运行内存密集型应用前,可设置为1仅释放pagecache,避免缓存干扰应用性能测试结果", + "range": [ + "1", + "2", @@ -5585,7 +8034,7 @@ index 0000000..93438ea + "get": "sysctl -n vm.drop_caches" + }, + "fs.inotify.max_user_watches": { -+ "desc": "1. 当监控大量文件或目录时(如日志目录、代码仓库等),若出现\"Too many open files\"或\"User limit of inotify watches reached\"错误,需增加该值\n\n2. 对于高并发文件监控场景(如实时日志分析、文件同步服务),建议将该值调整为默认值(通常8192)的4-8倍,具体数值应根据实际监控文件数量确定", ++ "desc": "1. 当监控大量文件或目录时(如日志目录、代码仓库等),若出现\"Too many open files\"或\"User limit of inotify watches reached\"错误,需增加该值;2. 对于高并发文件监控场景(如实时日志分析、文件同步服务),建议将该值调整为默认值(通常8192)的4-8倍,具体数值应根据实际监控文件数量确定", + "type": "continuous", + "range": [ + 4096, @@ -5596,7 +8045,7 @@ index 0000000..93438ea + "get": "sysctl -n fs.inotify.max_user_watches" + }, + "fs.nr_open": { -+ "desc": "1. 当应用(如数据库、Web服务器)频繁报告\"too many open files\"错误且ulimit -n已调高时,需增加该值至大于等于进程实际需要的最大文件描述符数\n\n2. 在内存资源紧张的系统中,若该值设置过高(如接近memlock限制),应适当降低以防止内存耗尽", ++ "desc": "1. 当应用(如数据库、Web服务器)频繁报告\"too many open files\"错误且ulimit -n已调高时,需增加该值至大于等于进程实际需要的最大文件描述符数;2. 在内存资源紧张的系统中,若该值设置过高(如接近memlock限制),应适当降低以防止内存耗尽", + "type": "continuous", + "range": [ + 10240, @@ -5607,7 +8056,7 @@ index 0000000..93438ea + "get": "sysctl -n fs.nr_open" + }, + "fs.file-max": { -+ "desc": "1. 当系统频繁出现\"Too many open files\"错误或监控显示文件句柄使用率持续接近当前限制时,需要增加该值\n\n2. 对于高并发服务(如Web服务器、数据库等),建议将该值设置为物理内存大小(KB)的10%-20%(例如64GB内存可设置为6,400,000-12,800,000)", ++ "desc": "1. 当系统频繁出现\"Too many open files\"错误或监控显示文件句柄使用率持续接近当前限制时,需要增加该值;2. 对于高并发服务(如Web服务器、数据库等),建议将该值设置为物理内存大小(KB)的10%-20%(例如64GB内存可设置为6,400,000-12,800,000)", + "type": "continuous", + "range": [ + 102400, @@ -5618,7 +8067,7 @@ index 0000000..93438ea + "get": "sysctl -n fs.file-max" + }, + "fs.aio-max-nr": { -+ "desc": "- 当系统日志频繁出现\"aio-max-nr reached\"警告或应用程序因异步I/O请求被拒绝而报错时,需要增加该值\n- 对于高并发数据库服务器(如MySQL/PostgreSQL)或大规模文件处理应用,建议将该值设置为(并发线程数×每个线程可能持有的未完成AIO请求数)×2", ++ "desc": "- 当系统日志频繁出现\"aio-max-nr reached\"警告或应用程序因异步I/O请求被拒绝而报错时,需要增加该值, - 对于高并发数据库服务器(如MySQL/PostgreSQL)或大规模文件处理应用,建议将该值设置为(并发线程数×每个线程可能持有的未完成AIO请求数)×2", + "type": "continuous", + "range": [ + 102400, @@ -5629,7 +8078,7 @@ index 0000000..93438ea + "get": "sysctl -n fs.aio-max-nr" + }, + "fs.inotify.max_user_instances": { -+ "desc": "1. 当系统日志频繁出现\"inotify instance limit reached\"或类似错误时,表明当前用户运行的监控进程(如文件同步工具、开发热加载工具等)数量超过限制,需要增加该值\n\n2. 对于运行大量容器或微服务的环境,每个容器实例可能需要独立的inotify实例监控文件变化,此时应根据实际容器数量合理调高该参数", ++ "desc": "1. 当系统日志频繁出现\"inotify instance limit reached\"或类似错误时,表明当前用户运行的监控进程(如文件同步工具、开发热加载工具等)数量超过限制,需要增加该值;2. 对于运行大量容器或微服务的环境,每个容器实例可能需要独立的inotify实例监控文件变化,此时应根据实际容器数量合理调高该参数", + "type": "continuous", + "range": [ + 64, @@ -5640,7 +8089,7 @@ index 0000000..93438ea + "get": "sysctl -n fs.inotify.max_user_instances" + }, + "fs.suid_dumpable": { -+ "desc": "1. 当系统需要调试setuid程序崩溃问题时,建议将值设为1(debug模式),允许生成核心转储文件用于故障分析\n\n2. 在注重安全性的生产环境中,建议保持默认值0,避免潜在的安全风险,防止敏感信息通过核心转储泄露", ++ "desc": "1. 当系统需要调试setuid程序崩溃问题时,建议将值设为1(debug模式),允许生成核心转储文件用于故障分析;2. 在注重安全性的生产环境中,建议保持默认值0,避免潜在的安全风险,防止敏感信息通过核心转储泄露", + "type": "continuous", + "range": [ + 0, @@ -5651,7 +8100,7 @@ index 0000000..93438ea + "get": "sysctl -n fs.suid_dumpable" + }, + "blockdev": { -+ "desc": "增大预读值(如设置为8192)可提升顺序读性能,适用于频繁大文件顺序读场景\n\n降低预读值(如设置为128)可减少IO开销,适用于随机访问为主的场景", ++ "desc": "增大预读值(如设置为8192)可提升顺序读性能,适用于频繁大文件顺序读场景。降低预读值(如设置为128)可减少IO开销,适用于随机访问为主的场景", + "type": "continuous", + "range": [ + 0, @@ -5662,7 +8111,7 @@ index 0000000..93438ea + "get": "/sbin/blockdev --getra /dev/sda" + }, + "block.fifo_batch": { -+ "desc": "1. 当系统需要更高吞吐量且能容忍更高延迟时(如批量数据处理场景),可适当增大该值(如32-64) \n2. 当系统对延迟敏感(如实时数据库)且当前吞吐量足够时,可降低该值(如8-12)以减少单个批次的处理延迟", ++ "desc": "1. 当系统需要更高吞吐量且能容忍更高延迟时(如批量数据处理场景),可适当增大该值(如32-64) ;2. 当系统对延迟敏感(如实时数据库)且当前吞吐量足够时,可降低该值(如8-12)以减少单个批次的处理延迟", + "type": "continuous", + "range": [ + 1, @@ -5673,7 +8122,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/iosched/fifo_batch" + }, + "block.front_merges": { -+ "desc": "1. 在I/O负载主要来自顺序写入且存储设备性能良好时,建议保持默认值1以允许前向合并,这能减少请求数量提升吞吐量\n\n2. 当系统处理大量随机I/O或使用某些特定存储设备时出现性能下降,可尝试将该参数设为0禁用前向合并,减少不必要的合并操作开销", ++ "desc": "1. 在I/O负载主要来自顺序写入且存储设备性能良好时,建议保持默认值1以允许前向合并,这能减少请求数量提升吞吐量;2. 当系统处理大量随机I/O或使用某些特定存储设备时出现性能下降,可尝试将该参数设为0禁用前向合并,减少不必要的合并操作开销", + "range": [ + "0", + "1" @@ -5684,7 +8133,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/iosched/front_merges" + }, + "block.read_expire": { -+ "desc": "1. 当系统主要处理高优先级读操作(如数据库查询)且存在读延迟敏感型应用时,可适当降低该值(如从默认的125ms降至50-100ms),确保读请求能更快得到响应\n\n2. 若系统频繁出现读请求超时丢弃现象(可通过监控deadline调度器的统计信息发现),且存储设备实际响应能力优于当前设置,应适当调高该值(如增至150-200ms)以避免不必要的请求重试", ++ "desc": "1. 当系统主要处理高优先级读操作(如数据库查询)且存在读延迟敏感型应用时,可适当降低该值(如从默认的125ms降至50-100ms),确保读请求能更快得到响应;2. 若系统频繁出现读请求超时丢弃现象(可通过监控deadline调度器的统计信息发现),且存储设备实际响应能力优于当前设置,应适当调高该值(如增至150-200ms)以避免不必要的请求重试", + "type": "continuous", + "range": [ + 100, @@ -5695,7 +8144,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/iosched/read_expire" + }, + "block.writes_starved": { -+ "desc": "1. 当系统主要处理随机读取密集型负载(如数据库服务)且需要低延迟响应时,可适当提高该值(默认2-5范围),优先处理读请求以减少读延迟\n\n2. 当系统存在大量顺序写操作(如日志写入、数据备份)且写性能成为瓶颈时,应降低该值(最小可设为1),防止读请求过度抢占I/O带宽影响写入吞吐量", ++ "desc": "1. 当系统主要处理随机读取密集型负载(如数据库服务)且需要低延迟响应时,可适当提高该值(默认2-5范围),优先处理读请求以减少读延迟;2. 当系统存在大量顺序写操作(如日志写入、数据备份)且写性能成为瓶颈时,应降低该值(最小可设为1),防止读请求过度抢占I/O带宽影响写入吞吐量", + "type": "continuous", + "range": [ + 1, @@ -5706,7 +8155,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/iosched/writes_starved" + }, + "block.max_sectors_kb": { -+ "desc": "1. 当使用高性能存储设备(如NVMe SSD)且存在大量大块I/O操作时,可适当增大该值(如1024-4096 KB)以提高吞吐量\n2. 当出现I/O错误或设备驱动不稳定时,应降低该值至默认值512 KB或更小以增强稳定性", ++ "desc": "1. 当使用高性能存储设备(如NVMe SSD)且存在大量大块I/O操作时,可适当增大该值(如1024-4096 KB)以提高吞吐量;2. 当出现I/O错误或设备驱动不稳定时,应降低该值至默认值512 KB或更小以增强稳定性", + "type": "continuous", + "range": [ + 64, @@ -5717,7 +8166,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/max_sectors_kb" + }, + "block.queue_depth": { -+ "desc": "1. 当使用高性能存储设备(如NVMe SSD)且系统负载较高时,若观察到存储设备利用率不足或IOPS未达预期,可适当增加该值(通常建议从默认32逐步上调至64-256范围),但需确保不超过设备硬件队列深度限制\n\n2. 对于虚拟机环境或低性能旋转磁盘(如HDD),若延迟显著增加或出现请求超时,应将值降低至16-32范围以减少IO堆积", ++ "desc": "1. 当使用高性能存储设备(如NVMe SSD)且系统负载较高时,若观察到存储设备利用率不足或IOPS未达预期,可适当增加该值(通常建议从默认32逐步上调至64-256范围),但需确保不超过设备硬件队列深度限制;2. 对于虚拟机环境或低性能旋转磁盘(如HDD),若延迟显著增加或出现请求超时,应将值降低至16-32范围以减少IO堆积", + "type": "continuous", + "range": [ + 64, @@ -5728,7 +8177,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/device/queue_depth" + }, + "block.nr_requests": { -+ "desc": "1. 当系统有高性能存储设备(如NVMe SSD)且IOPS吞吐量不足时,可适当增加该值(默认128),建议范围256-1024,以充分发挥设备并行处理能力\n\n2. 当系统出现高延迟或请求堆积时,若存储设备为机械硬盘,应降低该值(建议64-128),避免单个设备队列过深导致寻道时间增加", ++ "desc": "1. 当系统有高性能存储设备(如NVMe SSD)且IOPS吞吐量不足时,可适当增加该值(默认128),建议范围256-1024,以充分发挥设备并行处理能力;2. 当系统出现高延迟或请求堆积时,若存储设备为机械硬盘,应降低该值(建议64-128),避免单个设备队列过深导致寻道时间增加", + "type": "continuous", + "range": [ + 128, @@ -5739,7 +8188,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/nr_requests" + }, + "block.read_ahead_kb": { -+ "desc": "1. 当系统主要运行顺序读取大文件的应用(如数据库、视频流服务)且内存充足时,可适当增大该值(如从默认128KB调整为512KB-1MB),以减少I/O等待时间\n\n2. 当系统内存压力较大或主要处理随机访问负载时,应降低该值(如调整为64KB或更低),避免预读过多无用数据占用宝贵的内存资源", ++ "desc": "1. 当系统主要运行顺序读取大文件的应用(如数据库、视频流服务)且内存充足时,可适当增大该值(如从默认128KB调整为512KB-1MB),以减少I/O等待时间;2. 当系统内存压力较大或主要处理随机访问负载时,应降低该值(如调整为64KB或更低),避免预读过多无用数据占用宝贵的内存资源", + "type": "continuous", + "range": [ + 0, @@ -5750,7 +8199,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/read_ahead_kb" + }, + "block.rq_affinity": { -+ "desc": "1. 当系统在高I/O负载下出现CPU利用率不均衡时,建议调整该参数以提高本地CPU处理I/O请求的效率\n\n2. 在使用多队列存储设备时,若发现I/O性能未达到预期,建议调整此参数以充分利用多核CPU的并行处理能力", ++ "desc": "1. 当系统在高I/O负载下出现CPU利用率不均衡时,建议调整该参数以提高本地CPU处理I/O请求的效率;2. 在使用多队列存储设备时,若发现I/O性能未达到预期,建议调整此参数以充分利用多核CPU的并行处理能力", + "range": [ + "0", + "1", @@ -5762,7 +8211,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/rq_affinity" + }, + "block.add_random": { -+ "desc": "1. 当系统对随机数质量要求极高且性能开销可接受时,建议启用该参数以增强熵池的随机性来源\n\n2. 在高性能计算或低延迟要求的场景下,若系统已有足够熵源,建议禁用该参数以避免I/O事件带来的额外开销", ++ "desc": "1. 当系统对随机数质量要求极高且性能开销可接受时,建议启用该参数以增强熵池的随机性来源;2. 在高性能计算或低延迟要求的场景下,若系统已有足够熵源,建议禁用该参数以避免I/O事件带来的额外开销", + "range": [ + "0", + "1" @@ -5773,7 +8222,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/add_random" + }, + "block.rotational": { -+ "desc": "1. 当存储设备为SSD时,必须将该参数设置为0,以避免系统错误地应用针对机械硬盘的I/O调度策略\n\n2. 当存储设备为机械硬盘时,该参数应保持默认值1,以确保系统能正确应用适合旋转介质的相关优化", ++ "desc": "1. 当存储设备为SSD时,必须将该参数设置为0,以避免系统错误地应用针对机械硬盘的I/O调度策略;2. 当存储设备为机械硬盘时,该参数应保持默认值1,以确保系统能正确应用适合旋转介质的相关优化", + "range": [ + "0", + "1" @@ -5784,7 +8233,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/rotational" + }, + "block.scheduler": { -+ "desc": "1. 对于MySQL数据库场景,建议将block.scheduler设置为deadline,该调度算法能更好地处理数据库的随机I/O负载,减少I/O延迟\n\n2. 如果系统使用的是SSD存储设备,可以考虑设置为noop调度器,因为SSD没有机械磁盘的寻道时间,简单的FIFO队列调度即可发挥最佳性能", ++ "desc": "1. 对于MySQL数据库场景,建议将block.scheduler设置为deadline,该调度算法能更好地处理数据库的随机I/O负载,减少I/O延迟;2. 如果系统使用的是SSD存储设备,可以考虑设置为noop调度器,因为SSD没有机械磁盘的寻道时间,简单的FIFO队列调度即可发挥最佳性能", + "range": [ + "mq-deadline", + "kyber", @@ -5797,7 +8246,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/scheduler | sed -n 's/.*\\[\\(.*\\)\\].*/\\1/p'" + }, + "block.write_cache": { -+ "desc": "1. 当系统需要更高的写入性能且能容忍少量数据丢失风险时,建议设置为 write back 模式\n\n2. 当数据安全性要求极高且性能不是首要考虑时,建议设置为 write through 模式", ++ "desc": "1. 当系统需要更高的写入性能且能容忍少量数据丢失风险时,建议设置为 write back 模式;2. 当数据安全性要求极高且性能不是首要考虑时,建议设置为 write through 模式", + "range": [ + "write back", + "write through" @@ -5808,7 +8257,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/write_cache" + }, + "block.nomerges": { -+ "desc": "1. 仅在调试I/O请求合并相关问题时设置为0,生产环境应保持默认值1以获得合并带来的性能优势 \n2. 当使用blktrace等工具进行底层块设备分析时,可临时禁用合并(设为0)以获取更精确的请求跟踪数据", ++ "desc": "1. 仅在调试I/O请求合并相关问题时设置为0,生产环境应保持默认值1以获得合并带来的性能优势 ;2. 当使用blktrace等工具进行底层块设备分析时,可临时禁用合并(设为0)以获取更精确的请求跟踪数据", + "range": [ + "0", + "1", @@ -5820,7 +8269,7 @@ index 0000000..93438ea + "get": "cat /sys/block/sda/queue/nomerges" + }, + "net.core.netdev_budget": { -+ "desc": "1. 当网络接口频繁出现丢包(ifconfig显示RX dropped增加)且CPU软中断(softirq)占用过高时,建议适当增大该值(默认300可尝试调整为600-800)以提升单次软中断处理的包数量,减少中断次数\n\n2. 在低吞吐量但延迟敏感型场景(如高频交易系统)中,若网络延迟出现波动,可尝试降低该值(如调整为150-200)以减少单次软中断处理时间,降低处理延迟", ++ "desc": "1. 当网络接口频繁出现丢包(ifconfig显示RX dropped增加)且CPU软中断(softirq)占用过高时,建议适当增大该值(默认300可尝试调整为600-800)以提升单次软中断处理的包数量,减少中断次数;2. 在低吞吐量但延迟敏感型场景(如高频交易系统)中,若网络延迟出现波动,可尝试降低该值(如调整为150-200)以减少单次软中断处理时间,降低处理延迟", + "type": "continuous", + "range": [ + 100, @@ -5831,7 +8280,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.netdev_budget" + }, + "net.core.optmem_max": { -+ "desc": "- 当应用程序(如高性能网络服务)需要处理大量并发连接或大容量数据时,若出现 socket 缓冲区不足导致的性能瓶颈,可适当增加该值\n- 在内存资源充足的服务器上,若默认值(通常为 20480)无法满足特定应用(如视频流、大数据传输)的需求,可按 2 的幂次方逐步调高至合理范围(如 65536 或 131072)", ++ "desc": "- 当应用程序(如高性能网络服务)需要处理大量并发连接或大容量数据时,若出现 socket 缓冲区不足导致的性能瓶颈,可适当增加该值, - 在内存资源充足的服务器上,若默认值(通常为 20480)无法满足特定应用(如视频流、大数据传输)的需求,可按 2 的幂次方逐步调高至合理范围(如 65536 或 131072)", + "type": "continuous", + "range": [ + 20480, @@ -5842,7 +8291,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.optmem_max" + }, + "net.core.wmem_max": { -+ "desc": "1. 当服务器处理大量高吞吐量网络连接(如视频流、大文件传输等场景)时出现写缓冲区不足导致的性能瓶颈,建议将值从默认229376调整为16777216\n\n2. 在高并发TCP长连接场景(如WebSocket服务、消息队列等)中观察到因写缓冲区溢出导致的连接异常或数据丢失时,建议采用16777216值", ++ "desc": "1. 当服务器处理大量高吞吐量网络连接(如视频流、大文件传输等场景)时出现写缓冲区不足导致的性能瓶颈,建议将值从默认229376调整为16777216;2. 在高并发TCP长连接场景(如WebSocket服务、消息队列等)中观察到因写缓冲区溢出导致的连接异常或数据丢失时,建议采用16777216值", + "type": "continuous", + "range": [ + 1048576, @@ -5853,7 +8302,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.wmem_max" + }, + "net.core.wmem_default": { -+ "desc": "1. 当应用主要处理大量小数据包传输时,若网络吞吐量低于预期且系统监控显示发送缓冲区频繁填满,可适当增大该值至32768-65535字节范围,减少频繁缓冲区填满导致的延迟 \n\n2. 在高带宽高延迟网络环境下(如跨数据中心传输),若TCP窗口缩放功能已启用但实际窗口仍受限于默认值,应将该值提升至至少163840字节(160KB)以匹配BDP(带宽延迟积)需求", ++ "desc": "1. 当应用主要处理大量小数据包传输时,若网络吞吐量低于预期且系统监控显示发送缓冲区频繁填满,可适当增大该值至32768-65535字节范围,减少频繁缓冲区填满导致的延迟 ;2. 在高带宽高延迟网络环境下(如跨数据中心传输),若TCP窗口缩放功能已启用但实际窗口仍受限于默认值,应将该值提升至至少163840字节(160KB)以匹配BDP(带宽延迟积)需求", + "type": "continuous", + "range": [ + 8192, @@ -5864,7 +8313,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.wmem_default" + }, + "net.core.rmem_default": { -+ "desc": "1. 当应用需要处理大量网络数据流(如视频流、大数据传输)且观察到频繁的TCP窗口缩放或重传时,建议将值从默认的212992字节提升至1-4MB范围(1048576-4194304字节),需配合net.core.rmem_max同步调整\n\n2. 在高吞吐低延迟网络环境(如10Gbps以上)中,若netstat -s显示\"pruned\"或\"collapsed\"包统计持续增长,建议将值设置为BDP(带宽延迟积)的1/4至1/2,计算公式为:(带宽(bps) × 往返时延(s)) / 8 × 0.25", ++ "desc": "1. 当应用需要处理大量网络数据流(如视频流、大数据传输)且观察到频繁的TCP窗口缩放或重传时,建议将值从默认的212992字节提升至1-4MB范围(1048576-4194304字节),需配合net.core.rmem_max同步调整;2. 在高吞吐低延迟网络环境(如10Gbps以上)中,若netstat -s显示\"pruned\"或\"collapsed\"包统计持续增长,建议将值设置为BDP(带宽延迟积)的1/4至1/2,计算公式为:(带宽(bps) × 往返时延(s)) / 8 × 0.25", + "type": "continuous", + "range": [ + 8192, @@ -5875,7 +8324,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.rmem_default" + }, + "net.core.rmem_max": { -+ "desc": "1. 当应用需要处理高吞吐量网络数据流(如视频流、大数据传输)时,应将此值调整为16777216以提升接收性能 \n2. 在存在大量TCP长连接且频繁出现接收缓冲区不足警告(如内核日志报\"TCP: too much of memory\")时,应增大该值", ++ "desc": "1. 当应用需要处理高吞吐量网络数据流(如视频流、大数据传输)时,应将此值调整为16777216以提升接收性能 ;2. 在存在大量TCP长连接且频繁出现接收缓冲区不足警告(如内核日志报\"TCP: too much of memory\")时,应增大该值", + "type": "continuous", + "range": [ + 1048576, @@ -5886,7 +8335,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.rmem_max" + }, + "net.core.netdev_max_backlog": { -+ "desc": "1. 当服务器频繁出现网络丢包或高负载时,且通过监控发现 netdev_backlog 值持续接近或达到当前 netdev_max_backlog 设置值,应适当增大该参数值(例如从默认的1000调整为2000-3000) \n\n2. 对于10Gbps及以上高速网络接口,若默认值导致数据包处理延迟增加,需根据实际网络吞吐量和CPU处理能力按比例提升该参数值", ++ "desc": "1. 当服务器频繁出现网络丢包或高负载时,且通过监控发现 netdev_backlog 值持续接近或达到当前 netdev_max_backlog 设置值,应适当增大该参数值(例如从默认的1000调整为2000-3000) ;2. 对于10Gbps及以上高速网络接口,若默认值导致数据包处理延迟增加,需根据实际网络吞吐量和CPU处理能力按比例提升该参数值", + "type": "continuous", + "range": [ + 1000, @@ -5897,7 +8346,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.netdev_max_backlog" + }, + "net.ipv4.tcp_thin_linear_timeouts": { -+ "desc": "1. 当服务器处理大量短生命周期TCP连接且频繁出现超时重传时,建议启用该参数(tcp_thin_linear_timeouts=1)以更精确检测瘦流并减少不必要的重传等待时间 \n\n2. 若服务器主要处理大文件传输或视频流等持续高吞吐连接,建议保持默认值(tcp_thin_linear_timeouts=0)以避免对正常数据流产生误判", ++ "desc": "1. 当服务器处理大量短生命周期TCP连接且频繁出现超时重传时,建议启用该参数(tcp_thin_linear_timeouts=1)以更精确检测瘦流并减少不必要的重传等待时间 ;2. 若服务器主要处理大文件传输或视频流等持续高吞吐连接,建议保持默认值(tcp_thin_linear_timeouts=0)以避免对正常数据流产生误判", + "range": [ + "0", + "1" @@ -5908,7 +8357,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_thin_linear_timeouts" + }, + "net.unix.max_dgram_qlen": { -+ "desc": "1. 当系统频繁处理大量UDP数据报且出现丢包现象时,应考虑增加该值以提高队列容量\n2. 在高吞吐量UDP应用场景中,若观察到应用处理速度跟不上数据接收速度导致队列溢出,应适当调高此参数", ++ "desc": "1. 当系统频繁处理大量UDP数据报且出现丢包现象时,应考虑增加该值以提高队列容量;2. 在高吞吐量UDP应用场景中,若观察到应用处理速度跟不上数据接收速度导致队列溢出,应适当调高此参数", + "type": "continuous", + "range": [ + 128, @@ -5919,7 +8368,7 @@ index 0000000..93438ea + "get": "sysctl -n net.unix.max_dgram_qlen" + }, + "net.core.somaxconn": { -+ "desc": "1. 当服务器需要处理大量并发连接请求(如高负载Web服务器)且出现连接被丢弃或排队延迟时,应将此值从默认128增大到1024或更高\n\n2. 在运行需要频繁建立短连接的服务(如反向代理、负载均衡器)时,建议将该值调整为至少等于或大于服务的worker_processes与worker_connections乘积的1/4", ++ "desc": "1. 当服务器需要处理大量并发连接请求(如高负载Web服务器)且出现连接被丢弃或排队延迟时,应将此值从默认128增大到1024或更高;2. 在运行需要频繁建立短连接的服务(如反向代理、负载均衡器)时,建议将该值调整为至少等于或大于服务的worker_processes与worker_connections乘积的1/4", + "type": "continuous", + "range": [ + 128, @@ -5930,7 +8379,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.somaxconn" + }, + "net.core.busy_poll": { -+ "desc": "1. 在高吞吐量网络环境中(如10Gbps以上),若CPU利用率不足且存在延迟敏感型应用,可适当增加该值(如50-100微秒)以减少中断频率,但需监控CPU负载避免过度占用\n\n2. 在低延迟网络环境(如高频交易系统)中,若网络延迟指标不达标且CPU资源充足,可尝试设置为0禁用该功能,强制使用中断模式降低延迟", ++ "desc": "1. 在高吞吐量网络环境中(如10Gbps以上),若CPU利用率不足且存在延迟敏感型应用,可适当增加该值(如50-100微秒)以减少中断频率,但需监控CPU负载避免过度占用;2. 在低延迟网络环境(如高频交易系统)中,若网络延迟指标不达标且CPU资源充足,可尝试设置为0禁用该功能,强制使用中断模式降低延迟", + "type": "continuous", + "range": [ + 0, @@ -5941,7 +8390,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.busy_poll" + }, + "net.core.busy_read": { -+ "desc": "1. 当网络设备处理高吞吐量小包时出现频繁读超时或性能下降,可尝试增加该值至100-200微秒范围,需结合具体硬件性能测试确定最优值\n\n2. 在低延迟网络环境中若观察到CPU使用率异常升高且与网络中断处理相关,可测试降低该值至20-30微秒以减少等待时间", ++ "desc": "1. 当网络设备处理高吞吐量小包时出现频繁读超时或性能下降,可尝试增加该值至100-200微秒范围,需结合具体硬件性能测试确定最优值;2. 在低延迟网络环境中若观察到CPU使用率异常升高且与网络中断处理相关,可测试降低该值至20-30微秒以减少等待时间", + "type": "continuous", + "range": [ + 0, @@ -5952,7 +8401,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.busy_read" + }, + "net.core.dev_weight": { -+ "desc": "1. 当网络中断处理成为性能瓶颈时(通过监控发现CPU软中断时间占比过高),可适当增加该值以提高单次中断处理的数据包数量,但需注意避免单个CPU过载\n\n2. 对于高吞吐量网卡(如10G/25G以上)或大量小包场景,建议将该值从默认的64提高到128-256范围,需结合具体硬件和负载测试确定最优值", ++ "desc": "1. 当网络中断处理成为性能瓶颈时(通过监控发现CPU软中断时间占比过高),可适当增加该值以提高单次中断处理的数据包数量,但需注意避免单个CPU过载;2. 对于高吞吐量网卡(如10G/25G以上)或大量小包场景,建议将该值从默认的64提高到128-256范围,需结合具体硬件和负载测试确定最优值", + "type": "continuous", + "range": [ + 16, @@ -5963,7 +8412,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.dev_weight" + }, + "net.ipv4.tcp_keepalive_intvl": { -+ "desc": "1. 当服务器需要检测长时间空闲连接的健康状态时,若默认值75秒导致故障检测延迟过高,可适当减小至30-60秒范围以加快故障发现,但需权衡网络负载增加的影响\n\n2. 在高延迟网络环境中,若频繁出现误判连接中断的情况,可考虑增大该值至90-120秒范围以减少不必要的探测流量,同时需配合调整tcp_keepalive_probes确保总体检测窗口合理", ++ "desc": "1. 当服务器需要检测长时间空闲连接的健康状态时,若默认值75秒导致故障检测延迟过高,可适当减小至30-60秒范围以加快故障发现,但需权衡网络负载增加的影响;2. 在高延迟网络环境中,若频繁出现误判连接中断的情况,可考虑增大该值至90-120秒范围以减少不必要的探测流量,同时需配合调整tcp_keepalive_probes确保总体检测窗口合理", + "type": "continuous", + "range": [ + 30, @@ -5974,7 +8423,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_keepalive_intvl" + }, + "net.ipv4.tcp_keepalive_probes": { -+ "desc": "1. 当服务器需要快速检测并释放失效连接(如负载均衡器后端健康检查场景)时,可适当减少该值(默认9),建议调整为3-5次以加快失效连接回收\n\n2. 在高延迟或不可靠网络环境中(如跨国VPN),为防止误判活跃连接为失效,应增大该值至12-15次,同时配合调整tcp_keepalive_time和tcp_keepalive_intvl参数", ++ "desc": "1. 当服务器需要快速检测并释放失效连接(如负载均衡器后端健康检查场景)时,可适当减少该值(默认9),建议调整为3-5次以加快失效连接回收;2. 在高延迟或不可靠网络环境中(如跨国VPN),为防止误判活跃连接为失效,应增大该值至12-15次,同时配合调整tcp_keepalive_time和tcp_keepalive_intvl参数", + "type": "continuous", + "range": [ + 3, @@ -5985,7 +8434,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_keepalive_probes" + }, + "net.ipv4.tcp_keepalive_time": { -+ "desc": "1. 当服务器需要检测长时间空闲连接的有效性时(如负载均衡器或反向代理场景),若默认值(7200秒)过长可能导致无效连接占用资源,可适当调低至300-600秒区间 \n\n2. 在高并发短连接业务场景下,若出现大量TIME_WAIT状态连接导致端口耗尽,可配合减小tcp_keepalive_probes和tcp_keepalive_intvl参数,将本参数值提升至10800秒以上以减少keepalive探测频率", ++ "desc": "1. 当服务器需要检测长时间空闲连接的有效性时(如负载均衡器或反向代理场景),若默认值(7200秒)过长可能导致无效连接占用资源,可适当调低至300-600秒区间 ;2. 在高并发短连接业务场景下,若出现大量TIME_WAIT状态连接导致端口耗尽,可配合减小tcp_keepalive_probes和tcp_keepalive_intvl参数,将本参数值提升至10800秒以上以减少keepalive探测频率", + "type": "continuous", + "range": [ + 600, @@ -5996,7 +8445,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_keepalive_time" + }, + "net.ipv4.tcp_tw_reuse": { -+ "desc": "1. 当服务器面临大量短连接请求且TIME-WAIT状态连接过多导致端口耗尽时,建议启用该参数(设置为1)以复用TIME-WAIT套接字\n2. 在NAT网络环境下或需要严格保证TCP连接可靠性的场景下,建议保持该参数为默认值0以避免潜在连接混乱风险", ++ "desc": "1. 当服务器面临大量短连接请求且TIME-WAIT状态连接过多导致端口耗尽时,建议启用该参数(设置为1)以复用TIME-WAIT套接字;2. 在NAT网络环境下或需要严格保证TCP连接可靠性的场景下,建议保持该参数为默认值0以避免潜在连接混乱风险", + "range": [ + "0", + "1", @@ -6008,7 +8457,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_tw_reuse" + }, + "net.ipv4.tcp_window_scaling": { -+ "desc": "1. 在高带宽或高延迟网络环境下(如长距离传输或高速网络),应确保该参数值为1以启用窗口缩放功能,提升大窗口TCP连接性能\n\n2. 当网络设备不支持RFC 1323或存在兼容性问题时,应将该参数设为0以禁用窗口缩放功能", ++ "desc": "1. 在高带宽或高延迟网络环境下(如长距离传输或高速网络),应确保该参数值为1以启用窗口缩放功能,提升大窗口TCP连接性能;2. 当网络设备不支持RFC 1323或存在兼容性问题时,应将该参数设为0以禁用窗口缩放功能", + "range": [ + "0", + "1" @@ -6019,7 +8468,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_window_scaling" + }, + "net.ipv4.tcp_fin_timeout": { -+ "desc": "1. 当服务器需要处理大量短连接请求时,如果观察到大量连接处于FIN_WAIT_2状态导致端口耗尽,建议将该值从默认的60秒降低到30秒或更低,以加快连接资源释放\n\n2. 对于高延迟网络环境或需要保持长时间连接的应用场景,如果发现连接异常终止问题,建议适当增加该值至120秒以上,确保连接正常关闭", ++ "desc": "1. 当服务器需要处理大量短连接请求时,如果观察到大量连接处于FIN_WAIT_2状态导致端口耗尽,建议将该值从默认的60秒降低到30秒或更低,以加快连接资源释放;2. 对于高延迟网络环境或需要保持长时间连接的应用场景,如果发现连接异常终止问题,建议适当增加该值至120秒以上,确保连接正常关闭", + "type": "continuous", + "range": [ + 1, @@ -6030,7 +8479,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_fin_timeout" + }, + "net.ipv4.udp_mem": { -+ "desc": "1. 当服务器频繁处理大量UDP流量(如DNS服务器、视频流服务器)且出现丢包或性能下降时,可适当增加high值(如默认值的2-3倍),确保有足够内存缓冲队列数据包\n\n2. 若系统空闲内存充足但UDP应用仍频繁触发压力模式(可通过监控/proc/net/sockstat观察),应按比例同步提高low和assure值(如low设为总内存的1%,assure设为2%)以避免不必要的内存回收抖动", ++ "desc": "1. 当服务器频繁处理大量UDP流量(如DNS服务器、视频流服务器)且出现丢包或性能下降时,可适当增加high值(如默认值的2-3倍),确保有足够内存缓冲队列数据包;2. 若系统空闲内存充足但UDP应用仍频繁触发压力模式(可通过监控/proc/net/sockstat观察),应按比例同步提高low和assure值(如low设为总内存的1%,assure设为2%)以避免不必要的内存回收抖动", + "range": [ + "12582912 16777216 25165824", + "25165824 33554432 50331648", @@ -6042,7 +8491,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.udp_mem" + }, + "net.ipv4.tcp_mem": { -+ "desc": "1. 当系统在高并发TCP连接场景下出现内存不足或频繁触发OOM killer时,应适当增加三个值(最小压力值/压力阈值/最大值),建议按总物理内存的1%-3%计算,并确保最大值不超过系统可用内存的50%\n\n2. 若系统出现TCP性能下降或连接被拒绝(尤其在高吞吐量场景),需检查当前值是否过小,建议将最小值设为当前活跃连接内存占用的1.5倍,最大值设为系统空闲内存的30%-40%", ++ "desc": "1. 当系统在高并发TCP连接场景下出现内存不足或频繁触发OOM killer时,应适当增加三个值(最小压力值/压力阈值/最大值),建议按总物理内存的1%-3%计算,并确保最大值不超过系统可用内存的50%;2. 若系统出现TCP性能下降或连接被拒绝(尤其在高吞吐量场景),需检查当前值是否过小,建议将最小值设为当前活跃连接内存占用的1.5倍,最大值设为系统空闲内存的30%-40%", + "range": [ + "6168306 8224411 12336612", + "12336612 16448822 24673224" @@ -6053,7 +8502,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_mem" + }, + "net.ipv4.tcp_rmem": { -+ "desc": "1. 在高吞吐量网络环境中(如视频流服务器、大数据传输节点),当默认最大值6291456(6MB)导致TCP接收窗口成为瓶颈时,建议将第三个值调整为16777216(16MB)以提升吞吐量\n\n2. 对于内存资源受限的服务器(如云主机或容器环境),若默认值87380(85KB)的初始缓冲区导致内存压力,可将中间值降至65536(64KB)以平衡性能与资源消耗", ++ "desc": "1. 在高吞吐量网络环境中(如视频流服务器、大数据传输节点),当默认最大值6291456(6MB)导致TCP接收窗口成为瓶颈时,建议将第三个值调整为16777216(16MB)以提升吞吐量;2. 对于内存资源受限的服务器(如云主机或容器环境),若默认值87380(85KB)的初始缓冲区导致内存压力,可将中间值降至65536(64KB)以平衡性能与资源消耗", + "range": [ + "4096 16384 4194304", + "4096 32768 8388608", @@ -6065,7 +8514,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_rmem" + }, + "net.ipv4.tcp_wmem": { -+ "desc": "1. 在高吞吐量网络环境中(如视频流服务器、文件传输服务器),建议将参数调整为 4096 65536 16777216,以提升大流量场景下的TCP写缓冲区性能\n\n2. 对于内存资源受限的服务器(如云主机或容器环境),若出现内存压力时应适当降低最大值(如调整为 4096 32768 8388608),避免TCP写缓冲区占用过多内存", ++ "desc": "1. 在高吞吐量网络环境中(如视频流服务器、文件传输服务器),建议将参数调整为 4096 65536 16777216,以提升大流量场景下的TCP写缓冲区性能;2. 对于内存资源受限的服务器(如云主机或容器环境),若出现内存压力时应适当降低最大值(如调整为 4096 32768 8388608),避免TCP写缓冲区占用过多内存", + "range": [ + "4096 16384 4194304", + "4096 32768 8388608", @@ -6077,7 +8526,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_wmem" + }, + "net.ipv4.tcp_fastopen": { -+ "desc": "1. 当服务器主要处理大量短连接请求(如HTTP API服务)且需要降低TCP握手延迟时,建议启用该参数(值为3同时支持客户端和服务器端)\n\n2. 当服务器处于严格安全环境或处理敏感数据时,建议禁用该参数(值为0)以避免潜在的安全风险", ++ "desc": "1. 当服务器主要处理大量短连接请求(如HTTP API服务)且需要降低TCP握手延迟时,建议启用该参数(值为3同时支持客户端和服务器端);2. 当服务器处于严格安全环境或处理敏感数据时,建议禁用该参数(值为0)以避免潜在的安全风险", + "range": [ + "1", + "2", @@ -6089,7 +8538,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_fastopen" + }, + "net.ipv4.tcp_synack_retries": { -+ "desc": "1. 当服务器处于高并发连接场景且出现大量SYN_RECV状态连接时,若网络延迟较高,可适当增加该值(默认5)至7-10次,确保在拥塞环境下完成三次握手\n\n2. 若服务器遭受SYN Flood攻击或处于高负载状态,可降低该值至2-3次以快速释放半连接资源,减少SYN队列占用时间", ++ "desc": "1. 当服务器处于高并发连接场景且出现大量SYN_RECV状态连接时,若网络延迟较高,可适当增加该值(默认5)至7-10次,确保在拥塞环境下完成三次握手;2. 若服务器遭受SYN Flood攻击或处于高负载状态,可降低该值至2-3次以快速释放半连接资源,减少SYN队列占用时间", + "type": "continuous", + "range": [ + 3, @@ -6100,7 +8549,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_synack_retries" + }, + "net.ipv4.tcp_syn_retries": { -+ "desc": "1. 在延迟较高或不稳定的网络环境中(如跨国网络或移动网络),建议将默认值6适当增加到8-10,以应对可能出现的SYN丢包情况,但需注意这会延长连接建立失败时的等待时间\n\n2. 对于内网或低延迟高可靠网络环境,建议降低到3-4以减少连接建立超时等待时间,提高应用响应速度", ++ "desc": "1. 在延迟较高或不稳定的网络环境中(如跨国网络或移动网络),建议将默认值6适当增加到8-10,以应对可能出现的SYN丢包情况,但需注意这会延长连接建立失败时的等待时间;2. 对于内网或低延迟高可靠网络环境,建议降低到3-4以减少连接建立超时等待时间,提高应用响应速度", + "type": "continuous", + "range": [ + 3, @@ -6111,7 +8560,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_syn_retries" + }, + "net.ipv4.tcp_moderate_rcvbuf": { -+ "desc": "1. 当应用需要处理大量突发流量时,建议启用该参数(设置为1),系统会自动调整接收缓冲区大小以适应流量变化\n2. 在内存资源受限的环境中,建议禁用该参数(设置为0),避免系统自动扩大接收缓冲区导致内存压力增加", ++ "desc": "1. 当应用需要处理大量突发流量时,建议启用该参数(设置为1),系统会自动调整接收缓冲区大小以适应流量变化;2. 在内存资源受限的环境中,建议禁用该参数(设置为0),避免系统自动扩大接收缓冲区导致内存压力增加", + "range": [ + "0", + "1" @@ -6122,7 +8571,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_moderate_rcvbuf" + }, + "net.ipv4.tcp_timestamps": { -+ "desc": "1. 在存在NAT设备或负载均衡器的网络环境中,建议禁用该参数(设置为0),以避免可能的时间戳冲突导致的连接问题\n2. 在高速低延迟的内网环境中,建议启用该参数(设置为1),以获得更精确的RTT计算和更好的TCP性能", ++ "desc": "1. 在存在NAT设备或负载均衡器的网络环境中,建议禁用该参数(设置为0),以避免可能的时间戳冲突导致的连接问题;2. 在高速低延迟的内网环境中,建议启用该参数(设置为1),以获得更精确的RTT计算和更好的TCP性能", + "range": [ + "0", + "1" @@ -6133,7 +8582,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_timestamps" + }, + "net.ipv4.tcp_dsack": { -+ "desc": "1. 在延迟敏感型应用环境中(如高频交易系统),建议设置为0(禁用)以减少不必要的ACK确认包带来的网络开销\n\n2. 在常规Web服务或文件传输场景下保持默认值1(启用),可帮助处理网络丢包情况下的数据重传效率", ++ "desc": "1. 在延迟敏感型应用环境中(如高频交易系统),建议设置为0(禁用)以减少不必要的ACK确认包带来的网络开销;2. 在常规Web服务或文件传输场景下保持默认值1(启用),可帮助处理网络丢包情况下的数据重传效率", + "range": [ + "0", + "1" @@ -6144,7 +8593,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_dsack" + }, + "net.ipv4.tcp_fack": { -+ "desc": "1. 在存在高延迟或高丢包率的网络环境中,建议启用该参数以改善TCP重传性能,通过选择性确认减少不必要的重传\n\n2. 当服务器作为高性能网络应用(如视频流、大文件传输)的接收端时,建议保持启用状态以优化TCP拥塞控制机制", ++ "desc": "1. 在存在高延迟或高丢包率的网络环境中,建议启用该参数以改善TCP重传性能,通过选择性确认减少不必要的重传;2. 当服务器作为高性能网络应用(如视频流、大文件传输)的接收端时,建议保持启用状态以优化TCP拥塞控制机制", + "range": [ + "0", + "1" @@ -6155,7 +8604,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_fack" + }, + "net.ipv4.tcp_sack": { -+ "desc": "1. 在广域网(WAN)通信环境下应保持启用(1),可显著改善高延迟或丢包网络中的TCP传输性能,即使会略微增加CPU负载\n2. 在低延迟、高带宽的局域网(LAN)环境中可考虑禁用(0),特别是当系统CPU资源已高度饱和且网络质量极佳时", ++ "desc": "1. 在广域网(WAN)通信环境下应保持启用(1),可显著改善高延迟或丢包网络中的TCP传输性能,即使会略微增加CPU负载;2. 在低延迟、高带宽的局域网(LAN)环境中可考虑禁用(0),特别是当系统CPU资源已高度饱和且网络质量极佳时", + "range": [ + "0", + "1" @@ -6166,7 +8615,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_sack" + }, + "net.ipv4.tcp_low_latency": { -+ "desc": "在高吞吐量且对延迟敏感的集群环境(如Beowulf集群)中应启用(设置为1) \n在普通网络环境下保持禁用(设置为0)以避免不必要的开销", ++ "desc": "在高吞吐量且对延迟敏感的集群环境(如Beowulf集群)中应启用(设置为1) , 在普通网络环境下保持禁用(设置为0)以避免不必要的开销", + "range": [ + "0", + "1" @@ -6177,7 +8626,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_low_latency" + }, + "net.ipv4.tcp_adv_win_scale": { -+ "desc": "1. 当应用需要处理大量小包网络流量时,若观察到TCP接收窗口利用率不足,可考虑增大该值(如设为2或3),以减少缓冲区开销比例,提升小包传输效率\n\n2. 在内存资源紧张的服务器环境中,若发现TCP内存消耗过高导致系统频繁OOM,可适当降低该值(如设为1或0),增加缓冲区开销比例以降低内存使用量", ++ "desc": "1. 当应用需要处理大量小包网络流量时,若观察到TCP接收窗口利用率不足,可考虑增大该值(如设为2或3),以减少缓冲区开销比例,提升小包传输效率;2. 在内存资源紧张的服务器环境中,若发现TCP内存消耗过高导致系统频繁OOM,可适当降低该值(如设为1或0),增加缓冲区开销比例以降低内存使用量", + "type": "continuous", + "range": [ + 0, @@ -6188,7 +8637,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_adv_win_scale" + }, + "net.ipv4.route.max_size": { -+ "desc": "1. 当服务器频繁处理大量网络连接或作为路由器转发大量数据包时,若观察到路由缓存频繁刷新导致性能下降,可适当增加该值(默认值4096可逐步倍增测试)\n\n2. 在高内存压力环境下,若路由表占用内存过高影响其他服务,且实际活跃路由条目远低于当前设置值,可适当降低该值以释放内存", ++ "desc": "1. 当服务器频繁处理大量网络连接或作为路由器转发大量数据包时,若观察到路由缓存频繁刷新导致性能下降,可适当增加该值(默认值4096可逐步倍增测试);2. 在高内存压力环境下,若路由表占用内存过高影响其他服务,且实际活跃路由条目远低于当前设置值,可适当降低该值以释放内存", + "type": "continuous", + "range": [ + 67108864, @@ -6199,7 +8648,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.route.max_size" + }, + "net.ipv4.tcp_max_tw_buckets": { -+ "desc": "1. 当服务器出现大量TIME_WAIT状态的TCP连接导致端口耗尽或性能下降时,建议将net.ipv4.tcp_max_tw_buckets从默认值2048调整为360000。\n\n2. 在高并发短连接场景下,若监控发现TIME_WAIT连接数频繁达到上限,可适当增大该值至360000以提升连接处理能力。", ++ "desc": "1. 当服务器出现大量TIME_WAIT状态的TCP连接导致端口耗尽或性能下降时,建议将net.ipv4.tcp_max_tw_buckets从默认值2048调整为360000。;2. 在高并发短连接场景下,若监控发现TIME_WAIT连接数频繁达到上限,可适当增大该值至360000以提升连接处理能力。", + "type": "continuous", + "range": [ + 32768, @@ -6210,7 +8659,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_max_tw_buckets" + }, + "net.ipv4.tcp_max_syn_backlog": { -+ "desc": "1. 当服务器频繁处理大量新连接请求且出现 SYN 包丢弃时,应考虑增大该值至 8192 或更高\n\n2. 在高并发短连接场景下,若监控发现 SYN_RECV 状态连接数常接近默认值 2048,应调整该参数以避免连接建立延迟", ++ "desc": "1. 当服务器频繁处理大量新连接请求且出现 SYN 包丢弃时,应考虑增大该值至 8192 或更高;2. 在高并发短连接场景下,若监控发现 SYN_RECV 状态连接数常接近默认值 2048,应调整该参数以避免连接建立延迟", + "type": "continuous", + "range": [ + 1024, @@ -6221,7 +8670,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_max_syn_backlog" + }, + "net.ipv4.tcp_max_orphans": { -+ "desc": "1. 当服务器频繁出现\"Out of socket memory\"错误或日志中出现大量orphaned sockets警告时,需要增加该值。建议根据当前系统内存容量调整,通常设置为内存容量的1/4对应的socket数量(每个orphan约占用64KB内存)\n\n2. 对于高并发短连接服务(如HTTP服务器、负载均衡器),若观察到tcp_max_orphans限制成为性能瓶颈(通过监控/proc/net/sockstat中orphan数量接近上限),应适当调高该值至并发连接数的1.2-1.5倍", ++ "desc": "1. 当服务器频繁出现\"Out of socket memory\"错误或日志中出现大量orphaned sockets警告时,需要增加该值。建议根据当前系统内存容量调整,通常设置为内存容量的1/4对应的socket数量(每个orphan约占用64KB内存);2. 对于高并发短连接服务(如HTTP服务器、负载均衡器),若观察到tcp_max_orphans限制成为性能瓶颈(通过监控/proc/net/sockstat中orphan数量接近上限),应适当调高该值至并发连接数的1.2-1.5倍", + "type": "continuous", + "range": [ + 65536, @@ -6232,7 +8681,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_max_orphans" + }, + "net.ipv4.tcp_ecn": { -+ "desc": "1. 当网络中存在不支持ECN的老旧网络设备时,建议保持默认值0(禁用),以避免潜在的数据包丢弃问题\n\n2. 在确认网络设备完全支持ECN且需要降低TCP重传率的环境中,建议设置为1(启用)以获得更好的拥塞控制性能", ++ "desc": "1. 当网络中存在不支持ECN的老旧网络设备时,建议保持默认值0(禁用),以避免潜在的数据包丢弃问题;2. 在确认网络设备完全支持ECN且需要降低TCP重传率的环境中,建议设置为1(启用)以获得更好的拥塞控制性能", + "range": [ + "0", + "1", @@ -6244,7 +8693,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_ecn" + }, + "net.ipv4.ip_forward": { -+ "desc": "- 当服务器需要作为路由器或VPN网关时,应设置为1以启用IPv4转发功能\n- 当服务器仅作为终端主机使用时,应保持默认值0以禁用转发功能,减少潜在安全风险", ++ "desc": "- 当服务器需要作为路由器或VPN网关时,应设置为1以启用IPv4转发功能, - 当服务器仅作为终端主机使用时,应保持默认值0以禁用转发功能,减少潜在安全风险", + "range": [ + "0", + "1" @@ -6255,7 +8704,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.ip_forward" + }, + "net.ipv4.conf.default.rp_filter": { -+ "desc": "1. 当服务器作为路由器或需要处理多路径网络流量时,建议将rp_filter设置为2(宽松模式),以避免严格的反向路径验证导致合法流量被丢弃\n2. 在单网卡服务器且网络环境可信的情况下,可以设置为0(关闭验证)以减少内核处理开销,但需确保网络环境无IP欺骗风险", ++ "desc": "1. 当服务器作为路由器或需要处理多路径网络流量时,建议将rp_filter设置为2(宽松模式),以避免严格的反向路径验证导致合法流量被丢弃;2. 在单网卡服务器且网络环境可信的情况下,可以设置为0(关闭验证)以减少内核处理开销,但需确保网络环境无IP欺骗风险", + "range": [ + "0", + "1" @@ -6266,7 +8715,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.default.rp_filter" + }, + "net.ipv4.tcp_no_metrics_save": { -+ "desc": "1. 在高并发短连接场景下,建议设置为1以禁用TCP连接参数保存,避免因大量无效参数缓存导致内存浪费和性能下降\n2. 在需要保持长连接稳定性的场景下,建议保持默认值0,允许重用之前连接的有效参数来优化新连接建立性能", ++ "desc": "1. 在高并发短连接场景下,建议设置为1以禁用TCP连接参数保存,避免因大量无效参数缓存导致内存浪费和性能下降;2. 在需要保持长连接稳定性的场景下,建议保持默认值0,允许重用之前连接的有效参数来优化新连接建立性能", + "range": [ + "0", + "1" @@ -6277,7 +8726,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_no_metrics_save" + }, + "net.ipv4.ip_default_ttl": { -+ "desc": "1. 当网络中存在多层NAT或复杂路由环境时,若出现数据包提前被丢弃的情况,可考虑将TTL值提高到128,确保数据包能到达更远的网络节点\n\n2. 对于需要限制数据包传播范围的场景(如内部测试网络),可降低TTL值至32以下,防止数据包在网络中过度传播", ++ "desc": "1. 当网络中存在多层NAT或复杂路由环境时,若出现数据包提前被丢弃的情况,可考虑将TTL值提高到128,确保数据包能到达更远的网络节点;2. 对于需要限制数据包传播范围的场景(如内部测试网络),可降低TTL值至32以下,防止数据包在网络中过度传播", + "type": "continuous", + "range": [ + 8, @@ -6288,7 +8737,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.ip_default_ttl" + }, + "net.ipv4.ip_no_pmtu_disc": { -+ "desc": "1. 当网络中存在路径MTU发现(PMTUD)问题导致连接超时或性能下降时,建议将该参数设为1以禁用PMTUD,避免因ICMP黑洞或防火墙丢弃数据包导致的连接问题\n\n2. 在高速网络环境(如10Gbps以上)且网络设备可靠支持PMTUD时,建议保持默认值0以启用PMTUD,确保TCP能动态发现最优MTU值提升吞吐量", ++ "desc": "1. 当网络中存在路径MTU发现(PMTUD)问题导致连接超时或性能下降时,建议将该参数设为1以禁用PMTUD,避免因ICMP黑洞或防火墙丢弃数据包导致的连接问题;2. 在高速网络环境(如10Gbps以上)且网络设备可靠支持PMTUD时,建议保持默认值0以启用PMTUD,确保TCP能动态发现最优MTU值提升吞吐量", + "range": [ + "0", + "1" @@ -6299,7 +8748,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.ip_no_pmtu_disc" + }, + "net.ipv4.tcp_retries2": { -+ "desc": "1. 对于高延迟或不稳定网络环境中的服务器,可考虑将值降低到5-8,减少因网络临时故障导致的连接长时间挂起问题\n2. 对于需要快速检测连接失效的金融交易类服务器,建议设置为3-5,确保能更快释放失效连接资源", ++ "desc": "1. 对于高延迟或不稳定网络环境中的服务器,可考虑将值降低到5-8,减少因网络临时故障导致的连接长时间挂起问题;2. 对于需要快速检测连接失效的金融交易类服务器,建议设置为3-5,确保能更快释放失效连接资源", + "type": "continuous", + "range": [ + 3, @@ -6310,7 +8759,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_retries2" + }, + "net.ipv4.tcp_orphan_retries": { -+ "desc": "1. 当服务器面临大量半连接(orphaned sockets)导致资源占用过高时,可适当降低该值(如3-5),加速释放资源,但需注意过低可能导致正常长延迟网络下的连接被过早丢弃\n\n2. 若服务器主要处理本地或低延迟网络通信,且出现过多重试浪费资源的情况,可降至2-3次以减少不必要的等待时间", ++ "desc": "1. 当服务器面临大量半连接(orphaned sockets)导致资源占用过高时,可适当降低该值(如3-5),加速释放资源,但需注意过低可能导致正常长延迟网络下的连接被过早丢弃;2. 若服务器主要处理本地或低延迟网络通信,且出现过多重试浪费资源的情况,可降至2-3次以减少不必要的等待时间", + "type": "continuous", + "range": [ + 0, @@ -6321,7 +8770,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_orphan_retries" + }, + "net.ipv4.tcp_syncookies": { -+ "desc": "1. 当服务器频繁遭受SYN flood攻击时,应启用该参数(设置为1),以保护系统资源不被耗尽\n2. 在正常网络环境下且未遭受攻击时,建议保持默认值(通常为1),因为启用syncookies可能导致TCP连接性能略微下降", ++ "desc": "1. 当服务器频繁遭受SYN flood攻击时,应启用该参数(设置为1),以保护系统资源不被耗尽;2. 在正常网络环境下且未遭受攻击时,建议保持默认值(通常为1),因为启用syncookies可能导致TCP连接性能略微下降", + "range": [ + "0", + "1" @@ -6332,7 +8781,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_syncookies" + }, + "net.ipv4.tcp_reordering": { -+ "desc": "1. 当网络路径存在较高丢包率或频繁重排序时,若观察到TCP重传率明显上升且吞吐量下降,应考虑适当增大该值(默认3可尝试调整为9-12),以容忍更多乱序数据包而非错误触发快速重传\n\n2. 在低延迟网络环境(如数据中心内部)且使用TSO/GRO等卸载技术时,若内核日志频繁出现\"TCP: too many of order packets\"警告,可将该值适度降低(如调整为6-8),减少乱序队列内存占用", ++ "desc": "1. 当网络路径存在较高丢包率或频繁重排序时,若观察到TCP重传率明显上升且吞吐量下降,应考虑适当增大该值(默认3可尝试调整为9-12),以容忍更多乱序数据包而非错误触发快速重传;2. 在低延迟网络环境(如数据中心内部)且使用TSO/GRO等卸载技术时,若内核日志频繁出现\"TCP: too many of order packets\"警告,可将该值适度降低(如调整为6-8),减少乱序队列内存占用", + "type": "continuous", + "range": [ + 2, @@ -6343,7 +8792,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_reordering" + }, + "net.ipv4.tcp_retrans_collapse": { -+ "desc": "1. 在Linux服务器环境中,若确认无老旧打印机设备需要兼容,建议禁用此参数以优化TCP重传性能\n\n2. 当网络吞吐量出现异常下降且排查其他因素无果时,可尝试禁用此参数观察是否由打印机兼容性功能引起", ++ "desc": "1. 在Linux服务器环境中,若确认无老旧打印机设备需要兼容,建议禁用此参数以优化TCP重传性能;2. 当网络吞吐量出现异常下降且排查其他因素无果时,可尝试禁用此参数观察是否由打印机兼容性功能引起", + "range": [ + "0", + "1" @@ -6354,7 +8803,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_retrans_collapse" + }, + "net.ipv4.tcp_congestion_control": { -+ "desc": "1. 在高带宽、高延迟网络环境下(如跨数据中心通信),建议将默认的\"cubic\"算法切换为\"bbr\",可更充分利用带宽并减少排队延迟\n\n2. 在无线网络或移动网络环境中,若出现频繁丢包,建议使用\"vegas\"或\"westwood\"算法,这些算法对丢包区分更准确,能避免误判拥塞", ++ "desc": "1. 在高带宽、高延迟网络环境下(如跨数据中心通信),建议将默认的\"cubic\"算法切换为\"bbr\",可更充分利用带宽并减少排队延迟;2. 在无线网络或移动网络环境中,若出现频繁丢包,建议使用\"vegas\"或\"westwood\"算法,这些算法对丢包区分更准确,能避免误判拥塞", + "range": [ + "cubic", + "reno", @@ -6366,7 +8815,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.tcp_congestion_control" + }, + "net.ipv4.conf.default.promote_secondaries": { -+ "desc": "1. 当服务器需要保持高可用性且依赖多个IP地址时,建议设置为1,确保主IP被移除时次IP能自动提升为主IP,避免服务中断\n\n2. 在安全性要求严格的环境中建议设置为0,确保主IP被移除时所有关联IP都被清除,防止潜在的安全风险", ++ "desc": "1. 当服务器需要保持高可用性且依赖多个IP地址时,建议设置为1,确保主IP被移除时次IP能自动提升为主IP,避免服务中断;2. 在安全性要求严格的环境中建议设置为0,确保主IP被移除时所有关联IP都被清除,防止潜在的安全风险", + "range": [ + "0", + "1" @@ -6377,7 +8826,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.default.promote_secondaries" + }, + "net.ipv4.conf.all.promote_secondaries": { -+ "desc": "1. 当服务器需要保持高可用性且依赖多个IP地址时,建议设置为1,以确保主IP被移除时次IP能自动提升为主IP,避免服务中断\n\n2. 当服务器IP地址管理需要严格遵循变更控制流程时,建议保持默认值0,以确保任何IP地址变更都需要明确操作,防止意外配置变更", ++ "desc": "1. 当服务器需要保持高可用性且依赖多个IP地址时,建议设置为1,以确保主IP被移除时次IP能自动提升为主IP,避免服务中断;2. 当服务器IP地址管理需要严格遵循变更控制流程时,建议保持默认值0,以确保任何IP地址变更都需要明确操作,防止意外配置变更", + "range": [ + "0", + "1" @@ -6388,7 +8837,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.all.promote_secondaries" + }, + "net.ipv4.conf.all.accept_redirects": { -+ "desc": "1. 对于作为网关或路由器的Linux服务器,建议设置为0以禁用ICMP重定向消息,防止潜在的网络拓扑欺骗攻击\n2. 对于普通主机服务器,若网络环境可信且需要ICMP重定向功能优化路由,可保持默认值1;否则建议设置为0增强安全性", ++ "desc": "1. 对于作为网关或路由器的Linux服务器,建议设置为0以禁用ICMP重定向消息,防止潜在的网络拓扑欺骗攻击;2. 对于普通主机服务器,若网络环境可信且需要ICMP重定向功能优化路由,可保持默认值1;否则建议设置为0增强安全性", + "range": [ + "0", + "1" @@ -6399,7 +8848,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.all.accept_redirects" + }, + "net.ipv4.conf.default.accept_redirects": { -+ "desc": "1. 在作为路由器使用时,建议设置为0以禁用ICMP重定向消息,防止潜在的网络拓扑混淆和安全风险\n2. 在作为终端主机使用时,可保持默认值1以接受重定向消息,但若网络环境安全要求较高,建议同样设置为0", ++ "desc": "1. 在作为路由器使用时,建议设置为0以禁用ICMP重定向消息,防止潜在的网络拓扑混淆和安全风险;2. 在作为终端主机使用时,可保持默认值1以接受重定向消息,但若网络环境安全要求较高,建议同样设置为0", + "range": [ + "0", + "1" @@ -6410,7 +8859,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.default.accept_redirects" + }, + "net.ipv4.conf.all.secure_redirects": { -+ "desc": "- 在安全要求较高的生产环境中,建议设置为0禁用,避免潜在的安全风险\n- 若网络环境需要接收特定ICMP重定向且信任网关,可设置为1但需配合其他安全措施", ++ "desc": "- 在安全要求较高的生产环境中,建议设置为0禁用,避免潜在的安全风险, - 若网络环境需要接收特定ICMP重定向且信任网关,可设置为1但需配合其他安全措施", + "range": [ + "0", + "1" @@ -6421,7 +8870,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.all.secure_redirects" + }, + "net.ipv4.conf.default.secure_redirects": { -+ "desc": "1. 在安全要求较高的生产环境中建议设置为0,防止潜在的网络重定向攻击\n2. 如果服务器需要接收来自可信网关的ICMP重定向消息以优化路由,可设置为1", ++ "desc": "1. 在安全要求较高的生产环境中建议设置为0,防止潜在的网络重定向攻击;2. 如果服务器需要接收来自可信网关的ICMP重定向消息以优化路由,可设置为1", + "range": [ + "0", + "1" @@ -6432,7 +8881,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.default.secure_redirects" + }, + "net.ipv4.icmp_echo_ignore_broadcasts": { -+ "desc": "1. 如果服务器处于可能遭受ICMP广播风暴攻击的网络环境(如公开网络或DMZ区域),建议设置为1以避免资源耗尽\n\n2. 如果服务器位于受保护的内网且需要接收ICMP广播(如网络设备发现等场景),建议设置为0以保持功能正常", ++ "desc": "1. 如果服务器处于可能遭受ICMP广播风暴攻击的网络环境(如公开网络或DMZ区域),建议设置为1以避免资源耗尽;2. 如果服务器位于受保护的内网且需要接收ICMP广播(如网络设备发现等场景),建议设置为0以保持功能正常", + "range": [ + "0", + "1" @@ -6443,7 +8892,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.icmp_echo_ignore_broadcasts" + }, + "net.nf_conntrack_max": { -+ "desc": "1. 当服务器处理大量网络连接(如高并发代理、NAT网关或防火墙)且频繁出现\"nf_conntrack: table full\"日志时,需要增加该值以避免连接跟踪表溢出\n\n2. 当系统内存不足且连接跟踪表利用率持续低于50%时,可适当降低该值以释放内存资源", ++ "desc": "1. 当服务器处理大量网络连接(如高并发代理、NAT网关或防火墙)且频繁出现\"nf_conntrack: table full\"日志时,需要增加该值以避免连接跟踪表溢出;2. 当系统内存不足且连接跟踪表利用率持续低于50%时,可适当降低该值以释放内存资源", + "type": "continuous", + "range": [ + 65536, @@ -6454,7 +8903,7 @@ index 0000000..93438ea + "get": "sysctl -n net.nf_conntrack_max" + }, + "net.netfilter.nf_conntrack_tcp_timeout_established": { -+ "desc": "1. 当服务器处理大量持久TCP连接(如长连接服务、代理服务器等)且观察到nf_conntrack表频繁满导致丢包时,可适当增大该值(默认43200秒/12小时),但需确保不超过客户端实际连接保持时间,避免无效连接占用资源\n\n2. 对于短连接为主的Web服务器环境,若系统内存压力较大且连接跟踪表占用过高,可适当降低该值(但不应小于1800秒),以加速连接表项回收", ++ "desc": "1. 当服务器处理大量持久TCP连接(如长连接服务、代理服务器等)且观察到nf_conntrack表频繁满导致丢包时,可适当增大该值(默认43200秒/12小时),但需确保不超过客户端实际连接保持时间,避免无效连接占用资源;2. 对于短连接为主的Web服务器环境,若系统内存压力较大且连接跟踪表占用过高,可适当降低该值(但不应小于1800秒),以加速连接表项回收", + "type": "continuous", + "range": [ + 108000, @@ -6465,7 +8914,7 @@ index 0000000..93438ea + "get": "sysctl -n net.netfilter.nf_conntrack_tcp_timeout_established" + }, + "net.netfilter.nf_conntrack_tcp_timeout_close_wait": { -+ "desc": "1. 当服务器处理大量短连接且频繁出现close_wait状态时,若该值过大(默认240秒)会导致连接资源长时间占用,可适当降低至60-120秒范围\n\n2. 对于长连接为主的服务器环境,若发现连接异常断开导致资源泄漏,可考虑增大该值至300-600秒范围", ++ "desc": "1. 当服务器处理大量短连接且频繁出现close_wait状态时,若该值过大(默认240秒)会导致连接资源长时间占用,可适当降低至60-120秒范围;2. 对于长连接为主的服务器环境,若发现连接异常断开导致资源泄漏,可考虑增大该值至300-600秒范围", + "type": "continuous", + "range": [ + 15, @@ -6476,7 +8925,7 @@ index 0000000..93438ea + "get": "sysctl -n net.netfilter.nf_conntrack_tcp_timeout_close_wait" + }, + "net.netfilter.nf_conntrack_tcp_timeout_fin_wait": { -+ "desc": "1. 当服务器处理大量短连接且频繁出现FIN_WAIT状态连接堆积时,若系统日志显示nf_conntrack表频繁满导致丢包,可适当减少该值至30-60秒范围以加速连接回收\n\n2. 若服务器主要处理长连接且并发连接数远低于nf_conntrack_max的80%,出现FIN_WAIT状态连接过早超时导致异常断开时,可增大该值至120-300秒范围确保正常连接终止流程完成", ++ "desc": "1. 当服务器处理大量短连接且频繁出现FIN_WAIT状态连接堆积时,若系统日志显示nf_conntrack表频繁满导致丢包,可适当减少该值至30-60秒范围以加速连接回收;2. 若服务器主要处理长连接且并发连接数远低于nf_conntrack_max的80%,出现FIN_WAIT状态连接过早超时导致异常断开时,可增大该值至120-300秒范围确保正常连接终止流程完成", + "type": "continuous", + "range": [ + 30, @@ -6487,7 +8936,7 @@ index 0000000..93438ea + "get": "sysctl -n net.netfilter.nf_conntrack_tcp_timeout_fin_wait" + }, + "net.netfilter.nf_conntrack_tcp_timeout_time_wait": { -+ "desc": "1. 当服务器处理大量短连接且出现大量TIME_WAIT状态连接导致nf_conntrack表满时,可适当降低该值(默认120秒),建议调整为30-60秒以更快释放连接跟踪条目\n\n2. 若服务器作为反向代理或负载均衡器且出现端口耗尽问题,在确认无重传包风险后可考虑将该值降至15-30秒,但需确保大于TCP的2MSL时间", ++ "desc": "1. 当服务器处理大量短连接且出现大量TIME_WAIT状态连接导致nf_conntrack表满时,可适当降低该值(默认120秒),建议调整为30-60秒以更快释放连接跟踪条目;2. 若服务器作为反向代理或负载均衡器且出现端口耗尽问题,在确认无重传包风险后可考虑将该值降至15-30秒,但需确保大于TCP的2MSL时间", + "type": "continuous", + "range": [ + 30, @@ -6498,7 +8947,7 @@ index 0000000..93438ea + "get": "sysctl -n net.netfilter.nf_conntrack_tcp_timeout_time_wait" + }, + "net.ipv4.conf.default.forwarding": { -+ "desc": "1. 当服务器需要作为路由器或网关转发IPv4流量时,应将该参数设置为1,否则保持默认值0以关闭转发功能提升安全性\n2. 在容器或虚拟化环境中,若宿主机需要为虚拟机/容器提供网络转发功能,则需启用该参数", ++ "desc": "1. 当服务器需要作为路由器或网关转发IPv4流量时,应将该参数设置为1,否则保持默认值0以关闭转发功能提升安全性;2. 在容器或虚拟化环境中,若宿主机需要为虚拟机/容器提供网络转发功能,则需启用该参数", + "range": [ + "0", + "1" @@ -6509,7 +8958,7 @@ index 0000000..93438ea + "get": "sysctl -n net.ipv4.conf.default.forwarding" + }, + "net.core.rps_sock_flow_entries": { -+ "desc": "1. 当服务器处理大量网络连接且RPS/RFS功能开启时,若出现CPU缓存命中率下降或网络延迟增加,应考虑增加该值(通常建议设置为32768或65536)\n\n2. 在高吞吐量网络环境下(如10Gbps以上),若网络性能未达预期且/proc/net/softnet_stat显示drop计数增长,应将该值调整为至少等于或大于预期并发连接数", ++ "desc": "1. 当服务器处理大量网络连接且RPS/RFS功能开启时,若出现CPU缓存命中率下降或网络延迟增加,应考虑增加该值(通常建议设置为32768或65536);2. 在高吞吐量网络环境下(如10Gbps以上),若网络性能未达预期且/proc/net/softnet_stat显示drop计数增长,应将该值调整为至少等于或大于预期并发连接数", + "type": "continuous", + "range": [ + 0, @@ -6520,7 +8969,7 @@ index 0000000..93438ea + "get": "sysctl -n net.core.rps_sock_flow_entries" + }, + "net.ipv4.tcp_min_tso_segs": { -+ "desc": "1. 当服务器主要处理大量小数据包(如小于1460字节)且TSO利用率低时,可适当降低该值(默认2)以减少延迟,但需确保不低于1以避免性能下降\n\n2. 对于高速网络(10Gbps+)且处理大数据传输的场景,若观察到TSO分段不足导致CPU利用率过高,可适当增大该值(建议不超过8)以提升吞吐量", ++ "desc": "1. 当服务器主要处理大量小数据包(如小于1460字节)且TSO利用率低时,可适当降低该值(默认2)以减少延迟,但需确保不低于1以避免性能下降;2. 对于高速网络(10Gbps+)且处理大数据传输的场景,若观察到TSO分段不足导致CPU利用率过高,可适当增大该值(建议不超过8)以提升吞吐量", + "type": "continuous", + "range": [ + 1, @@ -9738,15 +12187,1525 @@ index 0000000..0cd10e9 + } +] \ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/api_server.py b/copilot-tune/src/knowledge_extractor/api_server.py +new file mode 100644 +index 0000000..5027dcd +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/api_server.py +@@ -0,0 +1,390 @@ ++from openai import OpenAI ++import re ++import httpx ++import yaml ++ ++#读取配置文件 ++def load_llm_config(file_path): ++ with open(file_path, 'r', encoding='utf-8') as file: ++ config = yaml.safe_load(file) ++ return config.get('llm', {}) ++ ++llm_config = load_llm_config("config/llm_config.yaml") ++ ++# 创建 OpenAI 客户端实例 ++client = OpenAI( ++ base_url=llm_config.get('base_url', ''), ++ api_key=llm_config.get('api_key', ''), ++ http_client=httpx.Client(verify=False) ++) ++role_prompt = "你是一个专业的文本分析专家,擅长从复杂的技术文档中精准提取关键的应用参数信息,并以清晰、规范的方式呈现提取结果。" ++role_prompt2 = "你是一位资深的性能调优专家,拥有丰富的操作系统应用参数调优经验,并能给出有效的建议。" ++ ++json_example = """ ++[ ++ { ++ "name": "innodb_write_io_threads", ++ "info": { ++ "desc": "The number of I/O threads for write operations in InnoDB. ", ++ "needrestart": "true", ++ "type": "continuous", ++ "min_value":2, ++ "max_value":200, ++ "default_value":4, ++ "dtype": "int", ++ "version":"8.0", ++ "related_param":[], ++ "options":null ++ } ++ }, ++ { ++ "name": "innodb_read_io_threads", ++ "info": { ++ "desc": "MySQL [mysqld] parameters 'innodb_read_io_threads'.", ++ "needrestart": "true", ++ "type": "continuous", ++ "min_value": 1, ++ "max_value": 64, ++ "default_value": 4, ++ "dtype": "int", ++ "version":"8.0", ++ "related_param":[], ++ "options":null ++ } ++ } ++] ++""" ++ ++def get_messages( ++ role_prompt: str, ++ history: str, ++ usr_prompt: str, ++) -> list: ++ """ ++ 构建消息列表,用于与OpenAI模型进行对话。 ++ ++ Parameters: ++ role_prompt (str): 系统角色提示。 ++ history (str): 历史对话内容。 ++ usr_prompt (str): 用户当前请求。 ++ ++ Returns: ++ list: 包含系统角色提示、历史对话和当前请求的消息列表。 ++ """ ++ messages = [] ++ if role_prompt != "": ++ messages.append({"role": "system", "content": role_prompt}) ++ if len(history) > 0: ++ messages.append({"role": "assistant", "content":history}) ++ if usr_prompt != "": ++ messages.append({"role": "user", "content": usr_prompt}) ++ return messages ++ ++ ++ ++def parameter_official_knowledge_preparation(text: str, app: str)-> str: ++ """ ++ 从官方文档文本中提取参数信息并返回JSON格式的参数列表。 ++ ++ Parameters: ++ text (str): 官方文档文本内容。 ++ example (str): 示例JSON格式。 ++ app (str): 应用程序名称。 ++ ++ Returns: ++ str: 包含提取参数信息的JSON字符串,若提取失败则返回None。 ++ """ ++ prompt = ''' ++ 你是一个专业的文本分析助手,擅长从技术文档中精准提取关键信息。现在,我给你一段关于{app}数据库参数配置相关的文本内容。你的任务是从这段文本中提取所有包含的{app}配置参数的信息,并按照指定格式输出。 ++ ++ <文本内容> ++ {text} ++ ++ <任务要求> ++ 请将提取的信息以JSON格式返回,其中每个参数的信息应包含以下字段(如果文本中未提及某字段,请设置为null,不要自行生成信息): ++ name(参数名称) ++ desc(参数描述) ++ needrestart(设置参数后是否需要重启,布尔值) ++ type(参数是否连续,可以为continuous或discrete) ++ min_value(参数的最小值) ++ max_value(参数的最大值) ++ default_value(参数的默认值) ++ dtype(参数的数据类型,如int、string、boolean、float,该字段请只在给定的这几个中选择) ++ version(参数的生效版本) ++ related_param(与该参数存在关联的参数) ++ options(参数的离散值集合) ++ ++ <注意事项> ++ 如果参数取值为连续值,请将options字段设置为null。 ++ 如果参数取值为离散值(如ON/OFF),请将min_value字段和max_value字段均设置为null,将options设置为离散值集合(如["ON", "OFF"])。 ++ 如果文本中未提及某个字段,请在JSON中将该字段设置为null。 ++ 如果文本中未提及某个参数,请不要在JSON中输出该参数。 ++ 最大值和最小值可以从“Permitted values”或“Range”等描述中获取。 ++ needrestart字段可以参考Dynamic内容,Dynamic表示是否能动态调整该参数,该值为yes时needrestart值为false;该值为no时needrestart为true。 ++ related_param字段可以在参数的描述中查找,若描述中提到其他的参数,则可以进一步判断是不是一个相关参数,如果是,请在该字段用列表输出。若没有,输出一个空列表。 ++ ++ <输出示例> ++ 请按照以下格式输出JSON数据: ++ {example} ++ ++ ''' ++ example = json_example ++ messages = get_messages(role_prompt,[],prompt.format(app=app, example=example, text=text)) ++ chat_completion = client.chat.completions.create( ++ messages=messages, ++ model=llm_config.get('model', ''), ++ temperature=1 ++ ) ++ ++ # 打印响应内容 ++ print(chat_completion.choices[0].message.content) ++ ans = chat_completion.choices[0].message.content ++ ans = re.sub(r".*?", "", ans, flags=re.DOTALL) ++ json_pattern = r'\[.*\]' ++ json_str = re.search(json_pattern, ans, re.DOTALL) ++ ++ if json_str: ++ # 提取匹配到的JSON字符串 ++ json_str = json_str.group(0) ++ return json_str ++ else: ++ print("没有找到JSON数据") ++ return ++ ++ ++#从文本中提取参数信息 ++def parameter_knowledge_preparation(text: str, params: list, app: str) -> str: ++ """ ++ 从web等文本中提取给定参数列表中的参数信息并返回JSON格式的参数列表。 ++ ++ Parameters: ++ text (str): 文本内容。 ++ example (str): 示例JSON格式。 ++ params (list): 参数列表。 ++ app (str): 应用程序名称。 ++ ++ Returns: ++ str: 包含提取参数信息的JSON字符串,若提取失败则返回None。 ++ """ ++ prompt = ''' ++ 你是一个专业的文本分析助手,擅长从技术文档中精准提取关键信息。现在,我给你一段关于{app}参数配置相关的文本内容。你的任务是从这段文本中提取以下参数的信息,请将给定参数列表的参数信息尽可能详细地提取。 ++ ++ <文本内容> ++ {text} ++ ++ <任务要求> ++ 注意,我只需要这些给定参数的信息,其他参数的信息请不要输出: ++ 给定的参数列表是:{params} ++ 请将提取的信息以 JSON 格式返回,其中每个参数的信息应包含在对应的键下。如果文本中没有提到的参数,请不要在 JSON 中将该参数输出。 ++ ++ 参考的执行步骤是: ++ 1. 首先匹配是否有给定参数列表中的{app}参数 ++ 2. 将该参数的值或者描述等信息找到,需要的信息包括参数名称(name),参数描述(desc),参数设置后是否需要重启(needrestart),参数是否连续(type),参数最小值(min_value),参数最大值(max_value),参数默认值(default_value),参数的数据类型(dtype),参数的生效版本(version),与该参数存在关联的参数(related_param),参数的离散值集合(options)。注意:Dynamic表示是否能动态调整该参数,其为yes时needrestart为false。 ++ 3. 注意如果参数取值为连续值,请将options字段设置为null。如果参数取值为离散值(如ON/OFF),请将min_value字段和max_value字段均设置为null,将options设置为离散值集合(如["ON", "OFF"])。 ++ 4. 将找到的信息保存,未找到的信息项设置为null,请不要自己生成相关的信息,要在文本中查找。 ++ 5. 将你从文本中获取到的信息以 json 格式输出,一个输出的示例为: ++ {example} ++ 其中没有获取到的信息请设置为null ++ ++ 注意:只输出一个包括参数信息的 json。如果文本中没有提到的参数,请不要在 JSON 中输出。不在example中的信息项,请不要输出。 ++ ++''' ++ example = json_example ++ messages = get_messages(role_prompt,[],prompt.format(app=app, params=params,example=example, text=text)) ++ chat_completion = client.chat.completions.create( ++ messages=messages, ++ model=llm_config.get('model', ''), ++ temperature=0.1, ++ ) ++ ++ # 打印响应内容 ++ print(chat_completion.choices[0].message.content) ++ ans = chat_completion.choices[0].message.content ++ ans = re.sub(r".*?", "", ans, flags=re.DOTALL) ++ ++ json_pattern = r'\[.*\]' ++ json_str = re.search(json_pattern, ans, re.DOTALL) ++ ++ if json_str: ++ # 提取匹配到的JSON字符串 ++ json_str = json_str.group(0) ++ return json_str ++ else: ++ print("没有找到JSON数据") ++ return ++ ++ ++#参数信息的gpt来源 ++def get_gpt_knowledge(params: list, app: str) -> str: ++ """ ++ GPTT获取给定参数列表中每个参数的详细信息,包括名称、描述、获取命令、设置命令等,并返回JSON格式的参数列表。 ++ ++ Parameters: ++ params (list): 参数列表。 ++ example (str): 示例JSON格式。 ++ app (str): 应用程序名称。 ++ ++ Returns: ++ str: 包含参数信息的JSON字符串,若获取失败则返回None。 ++ """ ++ prompt = ''' ++ 请根据以下{app}参数列表,详细描述每个参数的相关知识,包括包括参数名称(name),参数描述(desc),参数获取命令(get),参数设置命令(set),参数设置后是否需要重启(needrestart),参数是否连续(type),参数最小值(min_value),参数最大值(max_value),参数默认值(default_value),参数的数据类型(dtype),参数的生效版本(version),参数的互相关联参数(related_param),参数的离散值集合(options)。 ++ {params} ++ ++ <任务要求>: ++ 1.对于每个参数,提供清晰的定义和作用描述。 ++ 2.参数的描述(desc)要求至少包括:1)这个参数的详细作用;2)可以缓解系统的哪一方面瓶颈,从CPU,disk IO,network,memory中选择相关的瓶颈给出;3)取值的描述,如果取值只有几个,分别描述每个取值的意义。如果取值为范围,则说明增大或减小该值的意义和作用。 ++ 3.参数的最小值(min_value)和最大值(max_value)及默认值(default_value),请直接给出数值,不要出现计算,数值不需要引号。若参数取值为连续值,则参数的离散值(options)字段设置为null;若参数取值为离散值,请将最大值最小值设置为null,将参数离散值(options)字段设置为离散取值,例如参数取值为ON/OFF,则将options设置为["ON","OFF"] ++ 4.参数的互相关联参数(related_param)是与该参数相互影响的参数,一般需要同时调整配置。该字段请用列表输出,若没有,则输出一个空列表。 ++ 5.使用准确的技术术语,并确保描述的准确性和可靠性。 ++ 6.输出格式将每个参数的所有信息总结为一个json格式的知识。 ++ 最终将结果以json格式输出,输出的json格式不要包含注释,参数的描述用中文输出,描述中的瓶颈用英文表示,其余字段用英文输出,输出示例格式为: ++ {example} ++ ''' ++ ++ example = json_example ++ messages = get_messages(role_prompt2,[],prompt.format(app=app, params=params, example=example)) ++ chat_completion = client.chat.completions.create( ++ messages=messages, ++ model= llm_config.get('model', ''), ++ temperature=0.1 ++ ) ++ ++ # 打印响应内容 ++ print(chat_completion.choices[0].message.content) ++ ans = chat_completion.choices[0].message.content ++ ans = re.sub(r".*?", "", ans, flags=re.DOTALL) ++ ++ json_pattern = r'\[.*\]' ++ json_str = re.search(json_pattern, ans, re.DOTALL) ++ ++ if json_str: ++ # 提取匹配到的JSON字符串 ++ json_str = json_str.group(0) ++ print(json_str) ++ return json_str ++ else: ++ print("没有找到JSON数据") ++ return ++ ++def aggregate_web_result(text: str, param: str, app: str) -> str: ++ """ ++ 将多个JSON格式的参数描述整合成一个完整的JSON对象。 ++ ++ Parameters: ++ text (str): 多个JSON格式的参数描述文本。 ++ param (str): 参数名称。 ++ app (str): 应用程序名称。 ++ ++ Returns: ++ str: 整合后的JSON字符串,若整合失败则返回None。 ++ """ ++ prompt = ''' ++ 我有一些JSON格式的{app}参数结构化信息,这些JSON对象描述了同一个参数{param}的不同属性。这些JSON对象可能包含重复或部分信息,需要将它们整合成一个完整的JSON对象。 ++ 目标: ++ 将所有描述同一参数的JSON对象整合成一个完整的JSON对象,合并重复字段,并确保每个字段的值是准确且完整的。 ++ 要求: ++ 请根据输入,生成一个整合后的JSON对象,确保字段值完整,但是请不要添加你的知识,只根据提供的json对象填充。 ++ 输入:以下是输入的json格式的参数信息 ++ {text} ++ ''' ++ messages = get_messages(role_prompt2,[],prompt.format(app=app, param=param, text=text)) ++ chat_completion = client.chat.completions.create( ++ messages=messages, ++ model= llm_config.get('model', ''), ++ temperature=0.1 ++ ) ++ print(chat_completion.choices[0].message.content) ++ ans = chat_completion.choices[0].message.content ++ ans = re.sub(r".*?", "", ans, flags=re.DOTALL) ++ json_pattern = r'\{.*\}' ++ json_str = re.search(json_pattern, ans, re.DOTALL) ++ ++ if json_str: ++ # 提取匹配到的JSON字符串 ++ json_str = json_str.group(0) ++ return json_str ++ else: ++ print("没有找到JSON数据") ++ return ++ ++def aggregate_result(param: str, official: str, web: str, gpt: str, app: str) -> str: ++ """ ++ 汇总来自官方文档、web网页和GPT的参数信息,并整合成一个完整的JSON对象。 ++ ++ Parameters: ++ param (str): 参数名称。 ++ official (str): 官方文档中的参数信息。 ++ web (str): web网页中的参数信息。 ++ gpt (str): GPT中的参数信息。 ++ app (str): 应用程序名称。 ++ ++ Returns: ++ str: 整合后的JSON字符串,若整合失败则返回None。 ++ """ ++ prompt = ''' ++ 我有一些JSON格式的{app}参数结构化信息,这些JSON对象描述了同一个参数{param},JSON信息的来源分别为官方文档,web网页和GPT。 ++ 具体信息如下: ++ 官方文档:{official} ++ web网页:{web} ++ GPT:{gpt} ++ ++ 请根据以下要求和提示,汇总并处理来自 官方文档、web网页 和 GPT 的信息,并确保最终的描述准确、完整且一致。输出与输入结构相同的 JSON 格式参数信息: ++ ++ 1. 参数描述(desc) ++ 请综合官方文档、web网页和ChatGPT的描述,提取清晰、详细的参数功能描述。若来源中的描述有所不同,请优先参考GPT和官方文档中提供的详细说明,并与web网页中的实践建议进行对比,确保描述完整、详细且准确。如果有冲突,选择权威的描述。如无冲突,尽可能保留更多详细内容。描述最后总结为中文。 ++ ++ 2. 是否需要重启(needrestart) ++ 根据官方文档、web网页和ChatGPT提供的信息,判断该应用参数修改后是否需要重启{app}服务才能生效。若来源中存在不同的意见,优先参考官方文档和GPT来源中的内容。如果官方文档和GPT中的做法冲突,请重新分析是否需要重启并给出结果。 ++ ++ 3. 参数类型(type) ++ 请根据官方文档、web网页和ChatGPT提供的描述,确认该参数的类型。类型描述只包括`continuous`、`discrete`,优先参考官方文档中的类型定义,并与web网页中的使用实例进行对比,确保类型选择准确。如果不确定,请重新分析参数是离散还是连续。 ++ ++ 4. 最小值(min_value) ++ 请根据官方文档、web网页和ChatGPT提供的信息,确认该参数的最小值。若参数是离散的,该值设置为null。若来源中有不同的最小值,请优先参考官方文档中的说明,或者通过查看{app}官方文档来确认。如果web网页和ChatGPT中的值不同,确保选择的最小值符合实际环境配置需求。 ++ ++ 5. 最大值(max_value) ++ 根据官方文档、web网页和ChatGPT提供的信息,确认该参数的最大值。若参数是离散的,该值设置为null。如果不同来源给出的最大值有所不同,选择它们的交集或更具权威性的最大值。例如,如果官方文档明确给出了最大值范围,而web网页和ChatGPT提供的最大值偏大或偏小,请参照官方文档中的推荐值。 ++ ++ 6. 默认值(default_value) ++ 请根据官方文档、web网页和ChatGPT提供的信息,确认该参数的默认值。若不同来源提供的默认值不一致,请优先参考官方文档中的默认值。如果有多个来源提供相同的默认值,则采用该值作为最终结论。 ++ ++ 7. 数据类型(dtype) ++ 根据官方文档、web网页和ChatGPT提供的信息,确认该参数的数据类型(如`int`、`string`、`boolean`等)。如果不同来源对数据类型的定义不一致,请优先参考官方文档中的准确描述,确保数据类型与{app}的实际配置一致。如果有多个合理的选项,请选择最常见的类型并验证其准确性。 ++ ++ 8. 生效版本(version) ++ 根据官方文档、web网页和ChatGPT提供的信息,确认该参数的生效版本。请优先参考官方文档中的生效版本。如果有多个来源提供相同的生效版本,则采用该值作为最终结论。 ++ ++ 9.相关参数(related_param) ++ 根据官方文档、web网页和ChatGPT提供的信息,确认该参数的相关参数。该字段请将多个来源的值取并集输出为列表。 ++ ++ 10.参数的离散值(options) ++ 根据官方文档、web网页和ChatGPT提供的信息,确认该参数的离散取值。若参数是连续的,该值设置为null。若参数是离散的,请优先参考官方文档来源的内容,如果有多个来源提供相同的离散值,则采用该离散值为最终结论。 ++ ++ 注意:最终输出结构和输入结构相同,输出一个json格式的参数信息,请不要给出分析过程,只输出最后的json。 ++ ++ ''' ++ messages = get_messages(role_prompt2,[],prompt.format(app=app, param=param, official=official, web=web, gpt=gpt)) ++ chat_completion = client.chat.completions.create( ++ messages=messages, ++ model= llm_config.get('model', ''), ++ temperature=0.1 ++ ) ++ #print(chat_completion.choices[0].message.content) ++ ans = chat_completion.choices[0].message.content ++ ans = re.sub(r".*?", "", ans, flags=re.DOTALL) ++ json_pattern = r'\{.*\}' ++ json_str = re.search(json_pattern, ans, re.DOTALL) ++ ++ if json_str: ++ # 提取匹配到的JSON字符串 ++ json_str = json_str.group(0) ++ return json_str ++ else: ++ print("没有找到JSON数据") ++ return ++ ++ ++if __name__ == "__main__": ++ parameter_knowledge_preparation() ++ +diff --git a/copilot-tune/src/knowledge_extractor/code_extractor.py b/copilot-tune/src/knowledge_extractor/code_extractor.py +new file mode 100644 +index 0000000..4b00c69 +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/code_extractor.py +@@ -0,0 +1,90 @@ ++import os ++ ++def extract_code_snippets(param_list: list, folder_path: str, output_file: str, context_lines: int = 20) -> None: ++ """ ++ 提取指定文件夹中与给定参数列表相关的代码片段,并将结果写入到指定的输出文件中。 ++ ++ Parameters: ++ param_list (list): 参数列表,例如 ["spark.driver.memoryOverheadFactor", "spark.executor.memory"] ++ folder_path (str): 要查询的文件夹路径 ++ output_file (str): 输出文件路径 ++ context_lines (int, optional): 提取代码片段时包含的上下文行数。默认为20。 ++ ++ Returns: ++ None ++ """ ++ results = {} ++ ++ # 遍历文件夹中的所有文件 ++ for root, _, files in os.walk(folder_path): ++ for file_name in files: ++ file_path = os.path.join(root, file_name) ++ try: ++ with open(file_path, 'r', encoding='utf-8') as file: ++ lines = file.readlines() ++ except Exception as e: ++ print(f"无法读取文件 {file_path}: {e}") ++ continue ++ ++ # 检查文件内容 ++ related_ranges = [] ++ for i, line in enumerate(lines): ++ # 检查当前行是否包含参数 ++ for param in param_list: ++ if param in line: ++ # 计算上下文范围 ++ start = max(0, i - context_lines) ++ end = min(len(lines), i + context_lines + 1) ++ related_ranges.append((start, end)) ++ break ++ ++ ++ # 合并重叠的范围 ++ if related_ranges: ++ # 按起始行排序 ++ print(related_ranges) ++ related_ranges.sort(key=lambda x: x[0]) ++ merged_ranges = [] ++ for current_start, current_end in related_ranges: ++ if not merged_ranges: ++ merged_ranges.append((current_start, current_end)) ++ else: ++ last_start, last_end = merged_ranges[-1] ++ # 如果当前范围与最后一个合并范围有重叠或相邻,则合并 ++ if current_start <= last_end: ++ merged_ranges[-1] = (last_start, max(last_end, current_end)) ++ else: ++ merged_ranges.append((current_start, current_end)) ++ ++ #print(merged_ranges) ++ # 提取合并后的代码片段 ++ related_lines = [] ++ for start, end in merged_ranges: ++ related_lines.extend(lines[start:end]) ++ related_lines.extend("-" * 50+ "\n" ) ++ ++ unique_lines = [] ++ for line in related_lines: ++ unique_lines.append(line) ++ results[file_path] = unique_lines ++ ++ # 将结果写入到输出文件中 ++ with open(output_file, 'w', encoding='utf-8') as outfile: ++ for file_path, lines in results.items(): ++ for line in lines: ++ outfile.write(line) ++ ++ ++# 示例用法 ++if __name__ == "__main__": ++ # 示例参数列表 ++ params = ["spark.dynamicAllocation.minExecutors","spark.dynamicAllocation.maxExecutors","spark.dynamicAllocation.initialExecutors","spark.shuffle.service.db.enabled"] ++ ++ # 示例文件夹路径 ++ folder = "../code" ++ ++ # 输出文件路径 ++ output = "./output.txt" ++ ++ # 提取代码片段并写入到文件 ++ extract_code_snippets(params, folder, output) +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/config/app_config.yaml b/copilot-tune/src/knowledge_extractor/config/app_config.yaml +new file mode 100644 +index 0000000..4472b6f +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/config/app_config.yaml +@@ -0,0 +1,35 @@ ++# ============================================== ++# 知识库构建配置文件 ++# ============================================== ++ ++# 应用程序信息 ++app_name: mysql ++# 参数文件路径 ++params_file: ../mysql_knowledge/params.txt ++# 保存路径 ++save_path: ../mysql_knowledge/ ++# 滑动窗口大小 ++window_size: 5000 ++# 官方文档 URL 列表 ++official_url: ["https://mysql.net.cn/doc/refman/8.0/en/innodb-parameters.html"] ++# Web 网页 URL 列表 ++web_url: ["https://www.cnblogs.com/kevingrace/p/6133818.html"] ++# 代码路径(用于源代码参数提取) ++code_path: ../code ++# 额外补充文件列表(支持 Excel、PDF 等格式) ++append_file_paths: ["sample1.xlsx", "sample2.xlsx", "score.xlsx"] ++ ++# ============================================== ++# 步骤开关配置 ++# ============================================== ++ ++step1: true # 读取参数列表 ++step2: true # 获取官方文档信息 ++step3: true # 获取web网页信息 ++step4: true # 官方文档参数提取 ++step5: true # web信息参数提取 ++step6: false # 源代码参数提取 ++step7: true # GPT生成参数信息 ++step8: false # 补充文件生成参数信息 ++step9: true # 参数知识聚合 ++step10: true # 参数汇总文件生成 +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/config/llm_config.yaml b/copilot-tune/src/knowledge_extractor/config/llm_config.yaml +new file mode 100644 +index 0000000..366fe5c +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/config/llm_config.yaml +@@ -0,0 +1,4 @@ ++llm: ++ model: deepseek-chat #'deepseek-r1:14b' 'qwen2:72b' ++ api_key: ++ base_url: +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/document_loaders.py b/copilot-tune/src/knowledge_extractor/document_loaders.py +new file mode 100644 +index 0000000..600b649 +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/document_loaders.py +@@ -0,0 +1,171 @@ ++import pandas as pd ++import numpy as np ++import re ++import fitz # PyMuPDF ++from docx import Document ++ ++# 判断是否为数字 ++def is_number(s): ++ try: ++ float(s) ++ return True ++ except ValueError: ++ return False ++ ++# 判断是否为英文 ++def is_english(s): ++ return bool(re.match(r'^[a-zA-Z0-9_]+$', str(s))) ++ ++# 判断是否为中文 ++def is_chinese(s): ++ return bool(re.match(r'^[\u4e00-\u9fff]+$', str(s))) ++ ++# 分析数据类型 ++def analyze_data_types(df): ++ row_types = [] ++ col_types = [] ++ ++ # 分析每行的数据类型 ++ for i in range(len(df)): ++ row_type = set() ++ for value in df.iloc[i]: ++ if pd.isna(value): ++ continue ++ if is_number(value): ++ row_type.add('number') ++ elif is_english(value): ++ row_type.add('english') ++ elif is_chinese(value): ++ row_type.add('chinese') ++ else: ++ row_type.add('string') ++ row_types.append(row_type) ++ ++ # 分析每列的数据类型 ++ for j in range(len(df.columns)): ++ col_type = set() ++ for value in df.iloc[:, j]: ++ if pd.isna(value): ++ continue ++ if is_number(value): ++ col_type.add('number') ++ elif is_english(value): ++ col_type.add('english') ++ elif is_chinese(value): ++ col_type.add('chinese') ++ else: ++ col_type.add('string') ++ col_types.append(col_type) ++ ++ return row_types, col_types ++ ++# 计算行之间的相似度 ++def calculate_row_similarity(row_types): ++ similarities = [] ++ for i in range(len(row_types)): ++ row_similarities = [] ++ for j in range(len(row_types)): ++ if i != j: ++ sim = len(row_types[i].intersection(row_types[j])) / len(row_types[i].union(row_types[j])) ++ row_similarities.append(sim) ++ else: ++ row_similarities.append(1) # 自身相似度为1 ++ similarities.append(row_similarities) ++ return similarities ++ ++# 计算列之间的相似度 ++def calculate_column_similarity(col_types): ++ similarities = [] ++ for i in range(len(col_types)): ++ col_similarities = [] ++ for j in range(len(col_types)): ++ if i != j: ++ sim = len(col_types[i].intersection(col_types[j])) / len(col_types[i].union(col_types[j])) ++ col_similarities.append(sim) ++ else: ++ col_similarities.append(1) # 自身相似度为1 ++ similarities.append(col_similarities) ++ return similarities ++ ++# 判断数据排列方式并输出结构化数据到文本文件 ++def excel2text(file_path, sheet_name='Sheet1', output_file='../mysql_knowledge/temp/excel_out.txt'): ++ # 读取Excel文件 ++ df = pd.read_excel(file_path, sheet_name=sheet_name, header=None) ++ ++ # 分析数据类型 ++ row_types, col_types = analyze_data_types(df) ++ ++ # 计算行之间的相似度 ++ row_similarities = calculate_row_similarity(row_types) ++ row_avg_similarity = np.mean(row_similarities) ++ ++ # 计算列之间的相似度 ++ col_similarities = calculate_column_similarity(col_types) ++ col_avg_similarity = np.mean(col_similarities) ++ ++ # 判断排列方式 ++ if row_avg_similarity > col_avg_similarity: ++ orientation = "横向分布" ++ else: ++ orientation = "纵向分布" ++ ++ # 打开输出文件 ++ with open(output_file, 'w', encoding='utf-8') as f: ++ # 根据排列方式输出结构化数据 ++ if orientation == "横向分布": ++ # 读取Excel文件,指定标题行 ++ df = pd.read_excel(file_path, sheet_name=sheet_name, header=0) ++ for index, row in df.iterrows(): ++ for column in df.columns: ++ f.write(f"{column}: {row[column]}\n") ++ f.write("-" * 40 + "\n") ++ else: ++ # 读取Excel文件,不指定标题行 ++ df = pd.read_excel(file_path, sheet_name=sheet_name, header=None) ++ df_transposed = df.T ++ for index, row in df_transposed.iterrows(): ++ for column in df_transposed.columns: ++ f.write(f"{row[0]}: {row[column]}\n") ++ f.write("-" * 40 + "\n") ++ ++def pdf2text(pdf_path, output_file='../mysql_knowledge/temp/pdf_out.txt'): ++ # 打开PDF文件 ++ doc = fitz.open(pdf_path) ++ ++ # 打开输出文件 ++ with open(output_file, 'w', encoding='utf-8') as f: ++ # 遍历PDF的每一页 ++ for page_number in range(len(doc)): ++ page = doc.load_page(page_number) # 加载当前页 ++ page_text = page.get_text() # 提取当前页的文本 ++ ++ # 将当前页的文本写入文件 ++ f.write(f"Page {page_number + 1}:\n") ++ f.write(page_text) ++ f.write("\n\n") # 添加一个空行分隔各页的文本 ++ ++ # 关闭PDF文件 ++ doc.close() ++ ++ ++def docx2text(docx_path, output_file='../mysql_knowledge/temp/docx_out.txt'): ++ """ ++ 提取 .docx 文件中的文本内容并保存为 .txt 文件。 ++ ++ 参数: ++ docx_path (str): 输入的 .docx 文件路径。 ++ output_txt_path (str): 输出的 .txt 文件路径。 ++ """ ++ try: ++ # 打开 .docx 文件 ++ doc = Document(docx_path) ++ ++ # 提取所有段落的文本 ++ text = "\n".join([para.text for para in doc.paragraphs]) ++ ++ # 将文本写入 .txt 文件 ++ with open(output_file, "w", encoding="utf-8") as txt_file: ++ txt_file.write(text) ++ except Exception as e: ++ print(f"Error processing the file: {e}") ++ +diff --git a/copilot-tune/src/knowledge_extractor/main.py b/copilot-tune/src/knowledge_extractor/main.py +new file mode 100644 +index 0000000..b1c1bdc +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/main.py +@@ -0,0 +1,357 @@ ++from text_split import * ++from web_crawler import * ++from api_server import * ++from save_knowledge import * ++from code_extractor import * ++from document_loaders import * ++from merge_files import * ++from output import * ++import yaml ++import os ++import time ++ ++def load_config(file_path: str) -> dict: ++ """ ++ 加载配置文件。 ++ ++ Parameters: ++ file_path (str): 配置文件路径。 ++ ++ Returns: ++ dict: 配置信息。 ++ """ ++ with open(file_path, 'r', encoding='utf-8') as file: ++ config = yaml.safe_load(file) ++ return config ++ ++ ++def pipeline(param: str, app: str, config: dict) -> None: ++ """ ++ 处理多来源参数知识并汇总生成知识库内容。 ++ ++ Parameters: ++ param (str): 参数名称。 ++ app (str): 应用程序名称。 ++ config (dict): 配置信息。 ++ ++ Returns: ++ None ++ """ ++ save_path = config.get("save_path", "../{}_knowledge/".format(app)) # 存储文件的路径 ++ output_dir = save_path + "summary/" ++ if not os.path.exists(output_dir): ++ os.makedirs(output_dir) ++ official_doc_path = save_path + "official/" + param + ".json" ++ gpt_suggestion_path = save_path + "gpt/" + param + ".json" ++ web_suggestion_path = save_path + "web/" + param + ".json" ++ official_doc = "" ++ gpt_suggestion = "" ++ web_suggestion = "" ++ ++ try: ++ with open(official_doc_path, 'r', encoding='utf-8') as file: ++ official_doc = file.read() ++ print(official_doc) ++ except FileNotFoundError: ++ print("官方文档文件未找到,已设置为空。") ++ ++ try: ++ with open(gpt_suggestion_path, 'r', encoding='utf-8') as file: ++ gpt_suggestion = file.read() ++ print(gpt_suggestion) ++ except FileNotFoundError: ++ print("GPT建议文件未找到,已设置为空。") ++ ++ try: ++ with open(web_suggestion_path, 'r', encoding='utf-8') as file: ++ web_suggestion = file.read() ++ print(web_suggestion) ++ except FileNotFoundError: ++ print("WEB建议文件未找到,已设置为空。") ++ ++ # 总结官方文档及gpt和web建议 ++ sources_json = aggregate_result(param, official_doc, web_suggestion, gpt_suggestion, app) ++ ++ try: ++ json_data = json.loads(sources_json) ++ except json.JSONDecodeError as e: ++ print(f"JSON解析错误:{e}") ++ with open(save_path + "summary/" + param + ".json", 'w', encoding='utf-8') as json_file: ++ json.dump(json_data, json_file, indent=4, ensure_ascii=False) ++ ++ ++def main(config_file_path: str) -> None: ++ """ ++ 主函数,负责执行整个知识库生成流程。 ++ ++ Parameters: ++ config_file_path (str): 配置文件路径。 ++ ++ Returns: ++ None ++ """ ++ config = load_config(config_file_path) ++ app = config.get("app_name", "mysql") ++ params_file = config.get("params_file", "") ++ save_path = config.get("save_path", "../{}_knowledge/".format(app)) ++ ++ start_time = time.perf_counter() ++ ++ STEP = "====== {} ======" ++ ++ # step 1: 参数列表读取 ++ if config.get("step1", False): ++ print(STEP.format("步骤 1: 读取参数列表")) ++ if os.path.exists(params_file): ++ with open(params_file, 'r', encoding='utf-8') as f: ++ params = f.read() ++ params = [param.strip('"') for param in params.split()] ++ print(params) ++ time.sleep(2) ++ else: ++ print("提示:参数文件不存在,将提取官方文档中所有参数") ++ params = [] ++ else: ++ print(STEP.format("步骤 1: 跳过")) ++ ++ # step 2: official信息读取并存储文本 ++ if config.get("step2", False): ++ print(STEP.format("步骤 2: official信息读取并存储")) ++ urls = config.get("official_url",[]) ++ os.makedirs(save_path, exist_ok=True) ++ output_file = save_path + "official_text.txt" ++ for url in urls: ++ print(url) ++ convert_html_to_markdown(url,output_file) ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 2: 跳过")) ++ ++ # step 3: web信息读取并存储文本 ++ if config.get("step3", False): ++ print(STEP.format("步骤 3: web信息读取并存储")) ++ web_list = config.get("web_url",[]) ++ for web in web_list: ++ output_file = save_path + "web_text.txt" ++ convert_html_to_markdown(web,output_file) ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 3: 跳过")) ++ ++ # step 4: official信息转化为结构化数据(所有参数) ++ if config.get("step4", False): ++ print(STEP.format("步骤 4: official信息转化为结构化数据")) ++ # 进行滑窗划分和gpt提取知识,生成结构化数据 ++ with open(save_path+"official_text.txt", 'r', encoding='utf-8') as f: ++ text = f.read() ++ #找到最长的段落,将滑窗的滑动的步长设置为滑窗长度-longtext ++ long_text = find_longest_paragraph_length(text) ++ if long_text > 1000: ++ long_text = 1000 ++ elif long_text < 300: ++ long_text = 300 ++ window_size = config.get("window_size",5000) ++ step = window_size - long_text ++ # official信息的分割结果 ++ segments = sliding_window_split(text, window_size, step) ++ for segment in segments: ++ ans = parameter_official_knowledge_preparation(segment, app) ++ split_json_to_files(ans, save_path+"official") ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 4: 跳过")) ++ if len(params)==0: ++ for filename in os.listdir(save_path+"official"): ++ if filename.endswith(".json"): ++ params.append(filename[:-5]) ++ print(params) ++ ++ # step 5: web信息转化为分条的参数信息(根据给定参数列表) ++ if config.get("step5", False): ++ print(STEP.format("步骤 5: web信息转化为结构化数据")) ++ # web信息提取 信息已存在web_text.txt中 ++ with open(save_path + "web_text.txt", 'r', encoding='utf-8') as f: ++ text = f.read() ++ if not os.path.exists(save_path + "web/"): ++ os.makedirs(save_path + "web/") ++ #找到最长的段落,将滑窗的滑动的步长设置为滑窗长度-longtext ++ long_text = find_longest_paragraph_length(text) ++ if long_text > 1000: ++ long_text = 1000 ++ elif long_text < 300: ++ long_text = 300 ++ window_size = config.get("window_size",5000) ++ step = window_size - long_text ++ segments = sliding_window_split(text, window_size, step) ++ ans_list = [] ++ for segment in segments: ++ ans = parameter_knowledge_preparation(segment, params, app) ++ if ans is None: ++ continue ++ try: ++ ans_list.append(json.loads(ans)) ++ except json.JSONDecodeError as e: ++ print(f"JSON解析错误:{e}") ++ for param in params: ++ web_text = "" ++ for ans in ans_list: ++ for item in ans: ++ if item["name"] == param: ++ web_text += str(item) + "\n" ++ if web_text != "": ++ ans = aggregate_web_result(web_text,param,app) ++ try: ++ json_data = json.loads(ans) ++ except json.JSONDecodeError as e: ++ print(f"JSON解析错误:{e}") ++ with open(save_path + "web/"+param+".json", 'w', encoding='utf-8') as json_file: ++ json.dump(json_data, json_file, indent=4, ensure_ascii=False) ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 5: 跳过")) ++ ++ # step 6: 从源代码中获取参数知识 ++ if config.get("step6", False): ++ print(STEP.format("步骤 6: 从源代码中获取参数知识")) ++ folder = config.get("code_path","../code") ++ out_file = save_path + "code_text.txt" ++ # 提取代码片段 ++ extract_code_snippets(params, folder,out_file) ++ # code信息已存在code_text.txt中 ++ with open(save_path + "code_text.txt", 'r', encoding='utf-8') as f: ++ text = f.read() ++ if not os.path.exists(save_path + "code/"): ++ os.makedirs(save_path + "code/") ++ #找到最长的段落,将滑窗的滑动的步长设置为滑窗长度-longtext ++ long_text = find_longest_paragraph_length(text) ++ if long_text > 1000: ++ long_text = 1000 ++ elif long_text < 300: ++ long_text = 300 ++ window_size = config.get("window_size",5000) ++ step = window_size - long_text ++ segments = sliding_window_split(text, window_size, step) ++ ans_list = [] ++ for segment in segments: ++ ans = parameter_knowledge_preparation(segment, params, app) ++ if ans is None: ++ continue ++ try: ++ ans_list.append(json.loads(ans)) ++ except json.JSONDecodeError as e: ++ print(f"JSON解析错误:{e}") ++ for param in params: ++ web_text = "" ++ for ans in ans_list: ++ for item in ans: ++ if item["name"] == param: ++ web_text += str(item) + "\n" ++ if web_text != "": ++ ans = aggregate_web_result(web_text,param,app) ++ try: ++ json_data = json.loads(ans) ++ except json.JSONDecodeError as e: ++ print(f"JSON解析错误:{e}") ++ with open(save_path + "code/"+param+".json", 'w', encoding='utf-8') as json_file: ++ json.dump(json_data, json_file, indent=4, ensure_ascii=False) ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 6: 跳过")) ++ ++ # step 7: GPT直接生成结构化数据 ++ if config.get("step7", False): ++ print(STEP.format("步骤 7: GPT直接生成结构化数据")) ++ # gpt数据获取 ++ batch_size = 15 # 每批次处理15个元素 ++ for i in range(0, len(params), batch_size): # 按批次循环处理 ++ batch_params = params[i:i+batch_size] # 提取当前批次 ++ gpt_data = get_gpt_knowledge(batch_params,app) ++ split_json_to_files(gpt_data, save_path + "gpt") ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 7: 跳过")) ++ ++ # step 8: 通过补充文件作为补充的参数信息输入。 ++ if config.get("step8", False): ++ print(STEP.format("步骤 8: 通过补充文件作为补充的参数信息输入")) ++ append_file_paths = config.get("append_file_paths",[]) ++ # 根据文件类型进行处理 ++ for file_path in append_file_paths: ++ # 获取文件扩展名并转换为小写 ++ _, file_extension = os.path.splitext(file_path) ++ file_extension = file_extension.lower() ++ ++ # 判断文件类型 ++ if file_extension == ".pdf": ++ pdf2text(file_path, save_path+"temp/pdf_out.txt") ++ with open(save_path+"temp/pdf_out.txt", 'r', encoding='utf-8') as f: ++ text = f.read() ++ elif file_extension == ".docx": ++ docx2text(file_path, save_path+"temp/docx_out.txt") ++ with open(save_path+"temp/docx_out.txt", 'r', encoding='utf-8') as f: ++ text = f.read() ++ elif file_extension in [".xlsx", ".xls"]: ++ excel2text(file_path, "Sheet1", save_path+"temp/excel_out.txt") ++ with open(save_path+"temp/excel_out.txt", 'r', encoding='utf-8') as f: ++ text = f.read() ++ else: ++ print("Unsupported File Type") ++ continue ++ ++ if(len(text) < config.get("window_size",5000)): ++ ans = parameter_official_knowledge_preparation(segment) ++ split_json_to_files(ans, save_path+"addition") ++ else: ++ long_text = find_longest_paragraph_length(text) ++ if long_text > 1000: ++ long_text = 1000 ++ window_size = config.get("window_size",5000) ++ step = window_size - long_text ++ # 文档补充信息的分割结果 ++ segments = sliding_window_split(text, window_size, step) ++ for segment in segments: ++ ans = parameter_official_knowledge_preparation(segment) ++ split_json_to_files(ans, save_path+"addition") ++ time.sleep(2) ++ else: ++ print(STEP.format("步骤 8: 跳过")) ++ ++ # step 9: 结构化数据的信息聚合 ++ if config.get("step9", False): ++ print(STEP.format("步骤 9: 结构化数据的信息聚合")) ++ time.sleep(2) ++ for param in params: ++ pipeline(param, app, config) ++ else: ++ print(STEP.format("步骤 9: 跳过")) ++ ++ # step 10: 生成参数知识库总文件 ++ if config.get("step10", False): ++ print(STEP.format("步骤 10: 生成参数知识库总文件")) ++ time.sleep(2) ++ # # 合并json ++ input_directory = save_path+"summary" # 替换为包含JSON文件的目录路径 ++ output_file = save_path+ app+ "_param.json" # 替换为输出文件的路径 ++ merge_json_files(input_directory, output_file) ++ output_file_json = save_path+ app+ ".json" # 替换为输出文件的路径 ++ process_json(output_file, output_file_json) ++ ++ # # json转换为jsonl ++ # input_file = save_path+ app+ "_param.json" ++ # output_file = save_path+ app+ "_param.jsonl" ++ # json_to_jsonl(input_file, output_file) ++ else: ++ print(STEP.format("步骤 10: 跳过")) ++ ++ ++ end_time = time.perf_counter() ++ execution_time = end_time - start_time ++ print(f"生成的参数知识库内容:{params}") ++ print("-----------------------") ++ print(f"程序执行时间:{execution_time} 秒") ++ print(f"一共生成了参数知识:{len(params)} 条") ++ ++if __name__ == "__main__": ++ ++ config_file_path = "config/app_config.yaml" ++ main(config_file_path) +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/merge_files.py b/copilot-tune/src/knowledge_extractor/merge_files.py +new file mode 100644 +index 0000000..3e9e10d +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/merge_files.py +@@ -0,0 +1,46 @@ ++import json ++import os ++ ++def merge_json_files(input_directory, output_file): ++ """ ++ Merge multiple JSON files into a single JSON file. ++ ++ :param input_directory: Directory containing JSON files to be merged. ++ :param output_file: Path to the output JSON file. ++ """ ++ merged_data = [] ++ ++ # 遍历输入目录中的所有文件 ++ for filename in os.listdir(input_directory): ++ if filename.endswith(".json"): ++ file_path = os.path.join(input_directory, filename) ++ try: ++ # 打开并读取JSON文件 ++ with open(file_path, 'r', encoding='utf-8') as file: ++ data = json.load(file) ++ # 将内容添加到合并列表中 ++ merged_data.append(data) ++ except json.JSONDecodeError as e: ++ print(f"Error reading {file_path}: {e}") ++ except Exception as e: ++ print(f"An error occurred: {e}") ++ ++ # 将合并后的数据写入输出文件 ++ with open(output_file, 'w', encoding='utf-8') as outfile: ++ json.dump(merged_data, outfile, indent=4, ensure_ascii=False) ++ ++ print(f"Merged data has been written to {output_file}") ++ ++def json_to_jsonl(input_file, output_file): ++ """ ++ 将 JSON 文件转换为 JSON Lines 文件 ++ """ ++ # 读取 JSON 文件 ++ with open(input_file, 'r', encoding='utf-8') as f: ++ data = json.load(f) ++ ++ # 将列表中的每个对象写入 JSON Lines 文件 ++ with open(output_file, 'w', encoding='utf-8') as f: ++ for item in data: ++ json.dump(item, f, ensure_ascii=False) ++ f.write('\n') # 每个 JSON 对象占一行 +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/output.py b/copilot-tune/src/knowledge_extractor/output.py +new file mode 100644 +index 0000000..0347b9d +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/output.py +@@ -0,0 +1,58 @@ ++import json ++ ++def process_data(data): ++ processed_data = {} ++ for item in data: ++ param_name = item["name"] ++ param_info = item["info"] ++ ++ processed_data[param_name] = {} ++ processed_data[param_name]["desc"] = param_info["desc"] ++ processed_data[param_name]["type"] = param_info["type"] ++ processed_data[param_name]["dtype"] = param_info["dtype"] ++ processed_data[param_name]["range"] = param_info["min_value"] if isinstance(param_info["min_value"], list) else [param_info["min_value"], param_info["max_value"]] ++ return processed_data ++ ++def process_data1(data): ++ processed_data = {} ++ for item in data: ++ param_name = item["name"] ++ param_info = item["info"] ++ ++ processed_data[param_name] = {} ++ processed_data[param_name]["desc"] = param_info["desc"] ++ processed_data[param_name]["type"] = param_info["type"] ++ processed_data[param_name]["dtype"] = param_info["dtype"] ++ processed_data[param_name]["range"] = param_info["options"] if param_info["type"] == "discrete" else [param_info["min_value"], param_info["max_value"]] ++ return processed_data ++ ++def process_json(input_path, output_path): ++ """ ++ 读取input_path指定的JSON文件,对数据进行处理,然后将处理后的数据保存到output_path指定的文件中。 ++ """ ++ try: ++ # 读取JSON文件 ++ with open(input_path, 'r', encoding='utf-8') as infile: ++ data = json.load(infile) ++ ++ processed_data = process_data1(data) ++ ++ # 将处理后的数据保存到output_path ++ with open(output_path, 'w', encoding='utf-8') as outfile: ++ json.dump(processed_data, outfile, ensure_ascii=False, indent=4) ++ ++ print(f"处理完成,结果已保存到 {output_path}") ++ ++ except FileNotFoundError: ++ print(f"错误:文件 {input_path} 未找到。") ++ except json.JSONDecodeError: ++ print(f"错误:文件 {input_path} 不是有效的JSON格式。") ++ except Exception as e: ++ print(f"发生错误:{e}") ++ ++ ++# 示例调用 ++if __name__ == "__main__": ++ input_path = "../mysql_knowledge/spark_param.json" # 输入文件路径 ++ output_path = "../mysql_knowledge/spark.json" # 输出文件路径 ++ process_json(input_path, output_path) +diff --git a/copilot-tune/src/knowledge_extractor/requirements.txt b/copilot-tune/src/knowledge_extractor/requirements.txt +new file mode 100644 +index 0000000..2a69d10 +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/requirements.txt +@@ -0,0 +1,11 @@ ++PyMuPDF==1.25.2 ++html2text==2024.2.26 ++httpx==0.28.1 ++jieba==0.42.1 ++numpy==2.3.2 ++openai==1.98.0 ++pandas==2.3.1 ++python-docx==1.1.2 ++PyYAML==6.0.2 ++readability_lxml==0.8.1 ++requests==2.32.4 +diff --git a/copilot-tune/src/knowledge_extractor/save_knowledge.py b/copilot-tune/src/knowledge_extractor/save_knowledge.py +new file mode 100644 +index 0000000..722dd0c +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/save_knowledge.py +@@ -0,0 +1,53 @@ ++import json ++import re ++import os ++ ++def split_json_to_files(json_text: str, output_dir: str) -> None: ++ """ ++ 将包含多个参数的JSON文本分割并存储到不同的JSON文件中。 ++ 如果文件已存在,比较新旧内容,选择内容较多的那个进行保存。 ++ ++ Parameters: ++ json_text (str): 包含多个参数的JSON文本。 ++ output_dir (str): 输出文件的目录。 ++ ++ Returns: ++ None ++ """ ++ # 确保输出目录存在 ++ if not os.path.exists(output_dir): ++ os.makedirs(output_dir) ++ ++ # 解析JSON文本 ++ if json_text is None: ++ return ++ try: ++ json_data = json.loads(json_text) ++ except json.JSONDecodeError as e: ++ print(f"JSON解析错误:{e}") ++ return ++ ++ # 遍历JSON对象并为每个参数创建或更新JSON文件 ++ for param in json_data: ++ # 创建文件名 ++ if "/" in param['name'] or "\\" in param['name']: ++ continue ++ file_name = f"{param['name']}.json" ++ file_path = os.path.join(output_dir, file_name) ++ ++ # 读取文件现有内容(如果存在) ++ existing_content = "" ++ if os.path.exists(file_path): ++ with open(file_path, 'r', encoding='utf-8') as file: ++ existing_content = file.read() ++ ++ # 将参数转换为JSON字符串 ++ new_content = json.dumps(param, ensure_ascii=False, indent=4) ++ ++ # 比较新旧内容长度,选择较长的内容进行保存 ++ if len(new_content) > len(existing_content): ++ with open(file_path, 'w', encoding='utf-8') as file: ++ file.write(new_content) ++ print(f"已将参数 {param['name']} 更新到文件 {file_path}") ++ else: ++ print(f"文件 {file_path} 已存在且内容较多,未更新") +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/text_split.py b/copilot-tune/src/knowledge_extractor/text_split.py +new file mode 100644 +index 0000000..6e60298 +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/text_split.py +@@ -0,0 +1,161 @@ ++import jieba ++import re ++ ++def is_chinese(text): ++ """ ++ 判断文本是否为中文(如果包含非中文字符则返回False) ++ ++ Parameters: ++ text (str): 输入文本 ++ ++ Returns: ++ bool: 如果文本全为中文字符,返回True;否则返回False ++ """ ++ return all('\u4e00' <= char <= '\u9fff' for char in text) ++ ++ ++def split_text_into_segments(text: str, max_length: int = 5000) -> list: ++ """ ++ 将文本分割成多个段落,每个段落不超过max_length字符,并尽量保留语义完整性。 ++ 中文段落使用jieba分词,英文段落直接输出。 ++ ++ Parameters: ++ text (str): 输入文本 ++ max_length (int): 每个段落的最大字符数,默认为5000 ++ ++ Returns: ++ list: 分割后的文本段落列表 ++ """ ++ paragraphs = text.split('\n') # 按行分段 ++ segments = [] ++ current_segment = "" ++ ++ for para in paragraphs: ++ if len(current_segment) + len(para) + 1 <= max_length: ++ # 如果当前段落加上新段落不超过最大字符数,就加入当前段落 ++ if current_segment: ++ current_segment += '\n' + para ++ else: ++ current_segment = para ++ else: ++ # 当前段落超过限制,保存并重新开始一个新的段落 ++ segments.append(current_segment) ++ current_segment = para ++ ++ # 添加最后一个段落 ++ if current_segment: ++ segments.append(current_segment) ++ ++ return segments ++ ++ ++def tokenize_text(text: str) -> str: ++ """ ++ 对中文文本进行分词,英文文本保持原样 ++ ++ Parameters: ++ text (str): 输入文本 ++ ++ Returns: ++ str: 分词后的文本(中文)或原样文本(英文) ++ """ ++ if is_chinese(text): ++ # 中文段落进行jieba分词 ++ words = jieba.cut(text) ++ return " ".join(words) ++ else: ++ # 英文段落保持原样输出 ++ return text ++ ++ ++def process_text_file(input_file: str, output_prefix: str, max_length: int = 5000): ++ """ ++ 处理文本文件,进行分段和分词,并输出为多个文件。 ++ 中文段落分词,英文段落保持原样输出 ++ ++ Parameters: ++ input_file (str): 输入文件路径 ++ output_prefix (str): 输出文件前缀 ++ max_length (int): 每个段落的最大字符数,默认为5000 ++ """ ++ # 读取输入文件 ++ with open(input_file, 'r', encoding='utf-8') as f: ++ text = f.read() ++ ++ # 将文本分割成多个段落 ++ segments = split_text_into_segments(text, max_length) ++ ++ # 输出分词后的段落到多个文件 ++ for i, segment in enumerate(segments): ++ tokenized_text = tokenize_text(segment) ++ output_file = f"{output_prefix}_{i + 1}.txt" ++ ++ with open(output_file, 'w', encoding='utf-8') as f: ++ f.write(tokenized_text) ++ print(f"文件 {output_file} 已创建,包含 {len(segment)} 字符。") ++ ++ ++def sliding_window_split(text: str, window_size: int, step: int) -> list: ++ """ ++ 使用滑动窗口对文本进行分割。 ++ ++ Parameters: ++ text (str): 待分割的文本 ++ window_size (int): 窗口大小,即每个分割片段的字符数 ++ step (int): 滑动窗口的步长 ++ ++ Returns: ++ list: 分割后的文本片段列表 ++ ++ Raises: ++ ValueError: 如果窗口大小或步长不大于0,或者步长大于窗口大小 ++ """ ++ if window_size <= 0 or step <= 0: ++ raise ValueError("窗口大小和步长必须为正整数") ++ ++ if window_size > len(text): ++ return [text] # 或者返回空列表 [] ++ ++ if step > window_size: ++ raise ValueError("步长不能大于窗口大小") ++ ++ # 初始化一个空列表来存储分割后的文本片段 ++ segments = [] ++ ++ # 使用滑动窗口进行文本分割 ++ for i in range(0, len(text) - window_size + 1, step): ++ # 提取当前窗口内的文本片段 ++ segment = text[i:i + window_size] ++ # 将文本片段添加到列表中 ++ segments.append(segment) ++ ++ # 检查是否还有剩余的文本 ++ if len(text) % step != 0: ++ segments.append(text[-window_size:]) ++ ++ return segments ++ ++def find_longest_paragraph_length(text: str, delimiter: str = '\n') -> int: ++ """ ++ 找出文本中最长段落的长度 ++ ++ Parameters: ++ text (str): 输入文本 ++ delimiter (str): 段落分隔符,默认为换行符 ++ ++ Returns: ++ int: 最长段落的长度 ++ """ ++ # 根据段落分隔符分割文本 ++ if delimiter == '\n': ++ paragraphs = text.split(delimiter) ++ else: ++ paragraphs = re.split(delimiter, text) ++ ++ # 计算每个段落的长度 ++ paragraph_lengths = [len(paragraph) for paragraph in paragraphs] ++ ++ # 找出最长段落的长度 ++ max_paragraph_length = max(paragraph_lengths) ++ ++ return max_paragraph_length +\ No newline at end of file +diff --git a/copilot-tune/src/knowledge_extractor/web_crawler.py b/copilot-tune/src/knowledge_extractor/web_crawler.py +new file mode 100644 +index 0000000..23a3d14 +--- /dev/null ++++ b/copilot-tune/src/knowledge_extractor/web_crawler.py +@@ -0,0 +1,55 @@ ++import requests ++import html2text ++from readability import Document ++ ++ ++def convert_html_to_markdown(url: str, output_file_path: str) -> None: ++ """ ++ 将指定URL的HTML网页内容转换为Markdown,并保存到指定的文件中。 ++ ++ Parameters: ++ url (str): 目标网页的URL。 ++ output_file_path (str): 保存Markdown内容的文件路径。 ++ ++ Returns: ++ None ++ """ ++ headers = { ++ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0' ++ } ++ # 发送HTTP GET请求获取网页内容 ++ response = requests.get(url, headers=headers, verify=False) ++ print(response.text) ++ ++ # 检查请求是否成功 ++ if response.status_code == 200: ++ # 获取HTML内容 ++ html_content = response.text ++ doc = Document(html_content) ++ content = doc.summary(html_partial=False) ++ ++ content = response.text ++ # 创建html2text对象 ++ h = html2text.HTML2Text() ++ ++ # 配置转换器(可选) ++ h.ignore_links = True # 是否忽略链接 ++ h.ignore_images = True # 是否忽略图片 ++ h.ignore_emphasis = True # 是否忽略强调(如斜体、粗体) ++ ++ # 转换HTML为Markdown ++ markdown_content = h.handle(content) ++ ++ # 打印Markdown内容 ++ print(markdown_content) ++ # 将Markdown内容保存到文件中 ++ with open(output_file_path, 'a', encoding='utf-8') as file: ++ file.write(markdown_content) ++ ++ return markdown_content ++ ++ else: ++ print(f"请求失败,状态码:{response.status_code}") ++ print("请检查网页链接的合法性,并适当重试。") ++ ++ diff --git a/copilot-tune/src/performance_analyzer/__init__.py b/copilot-tune/src/performance_analyzer/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/performance_analyzer/app_analyzer.py b/copilot-tune/src/performance_analyzer/app_analyzer.py new file mode 100644 -index 0000000..927621e +index 0000000..f27ce62 --- /dev/null +++ b/copilot-tune/src/performance_analyzer/app_analyzer.py -@@ -0,0 +1,77 @@ +@@ -0,0 +1,75 @@ +from src.performance_analyzer.base_analyzer import BaseAnalyzer + + @@ -9760,29 +13719,28 @@ index 0000000..927621e + report = f"基于采集的系统指标, {self.app}初步的性能分析如下:\n" + for cmd, result in self.data.items(): + profile_prompt = f""" -+ # CONTEXT # -+ 以下内容是linux命令<{cmd}>的输出: -+ {result} -+ -+ # OBJECTIVE # -+ 请根据上述信息,简要分析{self.app}应用的性能状况。 -+ 要求: -+ 1.答案不超过200字。 -+ 2.答案中不要包含任何优化建议。 -+ 3.答案中尽可能保留信息中真实有效的数据。 ++# CONTEXT # ++以下内容是linux命令<{cmd}>的输出: ++{result} + -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# OBJECTIVE # ++请根据上述信息,简要分析{self.app}应用的性能状况。 ++要求: ++1.答案不超过200字。 ++2.答案中不要包含任何优化建议。 ++3.答案中尽可能保留信息中真实有效的数据。 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # RESPONSE FORMAT # -+ 如果有多条分析结论,请用数字编号分点作答。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + ++# RESPONSE FORMAT # ++如果有多条分析结论,请用数字编号分点作答。 + """ + report += self.ask_llm(profile_prompt) + @@ -9797,31 +13755,30 @@ index 0000000..927621e + if app_report == "当前系统没有运行ceph应用,无需分析ceph性能。\n": + return app_report + report_prompt = f""" -+ # CONTEXT # -+ linux系统中正在运行{self.app}应用, 以下内容是{self.app}相关的性能信息: -+ {app_report} -+ 信息中所涉及到的数据准确无误,真实可信。 -+ -+ # OBJECTIVE # -+ 请根据上述信息,分析{self.app}应用的性能状况。 -+ 要求: -+ 1.答案中不要包含任何优化建议。 -+ 2.答案中尽可能保留信息中真实有效的数据。 -+ 3.不要遗漏任何值得分析的信息。 -+ -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 -+ -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 -+ -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 -+ -+ # RESPONSE FORMAT # -+ 回答以"{self.app}分析如下:"开头,然后另起一行逐条分析。 -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# CONTEXT # ++linux系统中正在运行{self.app}应用, 以下内容是{self.app}相关的性能信息: ++{app_report} ++信息中所涉及到的数据准确无误,真实可信。 ++ ++# OBJECTIVE # ++请根据上述信息,分析{self.app}应用的性能状况。 ++要求: ++1.答案中不要包含任何优化建议。 ++2.答案中尽可能保留信息中真实有效的数据。 ++3.不要遗漏任何值得分析的信息。 ++ ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++ ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 ++ ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++ ++# RESPONSE FORMAT # ++回答以"{self.app}分析如下:"开头,然后另起一行逐条分析。 ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(report_prompt) + "\n" diff --git a/copilot-tune/src/performance_analyzer/base_analyzer.py b/copilot-tune/src/performance_analyzer/base_analyzer.py @@ -9870,10 +13827,10 @@ index 0000000..4865039 + return report diff --git a/copilot-tune/src/performance_analyzer/cpu_analyzer.py b/copilot-tune/src/performance_analyzer/cpu_analyzer.py new file mode 100644 -index 0000000..472d0cd +index 0000000..765d0c9 --- /dev/null +++ b/copilot-tune/src/performance_analyzer/cpu_analyzer.py -@@ -0,0 +1,176 @@ +@@ -0,0 +1,174 @@ +from .base_analyzer import BaseAnalyzer + +class CpuAnalyzer(BaseAnalyzer): @@ -9988,30 +13945,29 @@ index 0000000..472d0cd + def pid_info_analysis(self) -> str: + pid_info_report = "基于采集的系统指标,系统进程初步的性能分析如下:\n" + pid_prompt = """ -+ # CONTEXT # -+ 当前有linux系统进程的数据,性能指标是在linux系统中执行 pidstat -d | head -6 获得的输出,内容如下: -+ {pid_info} ++# CONTEXT # ++当前有linux系统进程的数据,性能指标是在linux系统中执行 pidstat -d | head -6 获得的输出,内容如下: ++{pid_info} + -+ # OBJECTIVE # -+ 请根据这些性能指标,生成一份逻辑清晰、条理清楚的系统进程的性能总结报告。 -+ 要求: -+ 1.答案中只分析可能对系统性能产生影响的指标数据。 -+ 2.答案中不要包含任何优化建议。 -+ 3.答案中尽可能保留信息中真实有效的数据。 -+ 4.答案不超过200字。 ++# OBJECTIVE # ++请根据这些性能指标,生成一份逻辑清晰、条理清楚的系统进程的性能总结报告。 ++要求: ++1.答案中只分析可能对系统性能产生影响的指标数据。 ++2.答案中不要包含任何优化建议。 ++3.答案中尽可能保留信息中真实有效的数据。 ++4.答案不超过200字。 + -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + -+ # RESPONSE FORMAT # -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# RESPONSE FORMAT # ++如果有多条分析结论,请用数字编号分点作答。 + """ + pid_info = self.data["进程信息"] + pid_info_report += self.ask_llm(pid_prompt.format(pid_info=pid_info)) @@ -10024,39 +13980,38 @@ index 0000000..472d0cd + # TO DO + # 要有一个报告模板,指明包含哪些信息,以及报告格式 + report_prompt = f""" -+ 以下内容是linux系统中cpu相关的性能信息: -+ {cpu_report} -+ 信息中所涉及到的数据准确无误,真实可信。 ++以下内容是linux系统中cpu相关的性能信息: ++{cpu_report} ++信息中所涉及到的数据准确无误,真实可信。 + -+ # OBJECTIVE # -+ 请根据上述信息,分析系统cpu的性能状况。 -+ 要求: -+ 1.答案中不要包含任何优化建议。 -+ 2.答案中尽可能保留信息中真实有效的数据。 -+ 3.不要遗漏任何值得分析的信息。 ++# OBJECTIVE # ++请根据上述信息,分析系统cpu的性能状况。 ++要求: ++1.答案中不要包含任何优化建议。 ++2.答案中尽可能保留信息中真实有效的数据。 ++3.不要遗漏任何值得分析的信息。 + -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + -+ # RESPONSE FORMAT # -+ 回答以"CPU分析如下:"开头,然后另起一行逐条分析。 -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# RESPONSE FORMAT # ++回答以"CPU分析如下:"开头,然后另起一行逐条分析。 ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(report_prompt) + "\n" \ No newline at end of file diff --git a/copilot-tune/src/performance_analyzer/disk_analyzer.py b/copilot-tune/src/performance_analyzer/disk_analyzer.py new file mode 100644 -index 0000000..2fa06f5 +index 0000000..e09d128 --- /dev/null +++ b/copilot-tune/src/performance_analyzer/disk_analyzer.py -@@ -0,0 +1,109 @@ +@@ -0,0 +1,108 @@ +from .base_analyzer import BaseAnalyzer + +class DiskAnalyzer(BaseAnalyzer): @@ -10140,38 +14095,37 @@ index 0000000..2fa06f5 + # TO DO + # 要有一个报告模板,指明包含哪些信息,以及报告格式 + report_prompt = f""" -+ 以下内容是linux系统中磁盘相关的性能信息: -+ {disk_report} -+ 信息中所涉及到的数据准确无误,真实可信。 ++以下内容是linux系统中磁盘相关的性能信息: ++{disk_report} ++信息中所涉及到的数据准确无误,真实可信。 + -+ # OBJECTIVE # -+ 请根据上述信息,分析系统磁盘的性能状况。 -+ 要求: -+ 1.答案中不要包含任何优化建议。 -+ 2.答案中尽可能保留信息中真实有效的数据。 -+ 3.不要遗漏任何值得分析的信息。 -+ -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# OBJECTIVE # ++请根据上述信息,分析系统磁盘的性能状况。 ++要求: ++1.答案中不要包含任何优化建议。 ++2.答案中尽可能保留信息中真实有效的数据。 ++3.不要遗漏任何值得分析的信息。 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # RESPONSE FORMAT # -+ 回答以"磁盘分析如下:"开头,然后另起一行逐条分析。 -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++ ++# RESPONSE FORMAT # ++回答以"磁盘分析如下:"开头,然后另起一行逐条分析。 ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(report_prompt) + "\n" diff --git a/copilot-tune/src/performance_analyzer/memory_analyzer.py b/copilot-tune/src/performance_analyzer/memory_analyzer.py new file mode 100644 -index 0000000..aede6d7 +index 0000000..e13a2b0 --- /dev/null +++ b/copilot-tune/src/performance_analyzer/memory_analyzer.py -@@ -0,0 +1,72 @@ +@@ -0,0 +1,71 @@ +from .base_analyzer import BaseAnalyzer + +class MemoryAnalyzer(BaseAnalyzer): @@ -10196,7 +14150,7 @@ index 0000000..aede6d7 + def omm_kill_analysis( + self, + oom_kill: float, -+) -> str: ++ ) -> str: + return self.generate_report_line(oom_kill == 1, "系统近期发生过oom_kill行为, 即内存严重不足,需要杀死进程来释放内存") + + def swap_analysis( @@ -10218,39 +14172,38 @@ index 0000000..aede6d7 + # TO DO + # 要有一个报告模板,指明包含哪些信息,以及报告格式 + report_prompt = f""" -+ 以下内容是linux系统中内存相关的性能信息: -+ {memory_report} -+ 信息中所涉及到的数据准确无误,真实可信。 ++以下内容是linux系统中内存相关的性能信息: ++{memory_report} ++信息中所涉及到的数据准确无误,真实可信。 + -+ # OBJECTIVE # -+ 请根据上述信息,分析系统内存的性能状况。 -+ 要求: -+ 1.答案中不要包含任何优化建议。 -+ 2.答案中尽可能保留信息中真实有效的数据。 -+ 3.不要遗漏任何值得分析的信息。 ++# OBJECTIVE # ++请根据上述信息,分析系统内存的性能状况。 ++要求: ++1.答案中不要包含任何优化建议。 ++2.答案中尽可能保留信息中真实有效的数据。 ++3.不要遗漏任何值得分析的信息。 + -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + -+ # RESPONSE FORMAT # -+ 回答以"内存分析如下:"开头,然后另起一行逐条分析。 -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# RESPONSE FORMAT # ++回答以"内存分析如下:"开头,然后另起一行逐条分析。 ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(report_prompt) + "\n" \ No newline at end of file diff --git a/copilot-tune/src/performance_analyzer/micro_dep_analyzer.py b/copilot-tune/src/performance_analyzer/micro_dep_analyzer.py new file mode 100644 -index 0000000..a4619d5 +index 0000000..e2dfe10 --- /dev/null +++ b/copilot-tune/src/performance_analyzer/micro_dep_analyzer.py -@@ -0,0 +1,95 @@ +@@ -0,0 +1,94 @@ +from .base_analyzer import BaseAnalyzer +import logging +class MicroDepAnalyzer(BaseAnalyzer): @@ -10320,38 +14273,37 @@ index 0000000..a4619d5 + # TO DO + # 要有一个报告模板,指明包含哪些信息,以及报告格式 + report_prompt = f""" -+ 以下内容是linux系统中应用微架构相关的性能信息: -+ {micro_report} -+ 信息中所涉及到的数据准确无误,真实可信。 ++以下内容是linux系统中应用微架构相关的性能信息: ++{micro_report} ++信息中所涉及到的数据准确无误,真实可信。 + -+ # OBJECTIVE # -+ 请根据上述信息,分析系统应用微架构的性能状况。 -+ 要求: -+ 1.答案中不要包含任何优化建议。 -+ 2.答案中尽可能保留信息中真实有效的数据。 -+ 3.不要遗漏任何值得分析的信息。 ++# OBJECTIVE # ++请根据上述信息,分析系统应用微架构的性能状况。 ++要求: ++1.答案中不要包含任何优化建议。 ++2.答案中尽可能保留信息中真实有效的数据。 ++3.不要遗漏任何值得分析的信息。 + -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + -+ # RESPONSE FORMAT # -+ 回答以"应用微架构分析如下:"开头,然后另起一行逐条分析。 -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# RESPONSE FORMAT # ++回答以"应用微架构分析如下:"开头,然后另起一行逐条分析。 ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(report_prompt) + "\n" diff --git a/copilot-tune/src/performance_analyzer/network_analyzer.py b/copilot-tune/src/performance_analyzer/network_analyzer.py new file mode 100644 -index 0000000..729447c +index 0000000..e0c357b --- /dev/null +++ b/copilot-tune/src/performance_analyzer/network_analyzer.py -@@ -0,0 +1,103 @@ +@@ -0,0 +1,101 @@ +from .base_analyzer import BaseAnalyzer + +class NetworkAnalyzer(BaseAnalyzer): @@ -10395,30 +14347,29 @@ index 0000000..729447c + network_adapter: str + ) -> str: + network_adapter_prompt = f""" -+ # CONTEXT # -+ 当前有linux系统网卡的数据,性能指标是在linux系统中执行 netstat -i 获得的输出,内容如下: -+ {network_adapter} ++# CONTEXT # ++当前有linux系统网卡的数据,性能指标是在linux系统中执行 netstat -i 获得的输出,内容如下: ++{network_adapter} + -+ # OBJECTIVE # -+ 请根据这些性能指标,生成一份逻辑清晰、条理清楚的系统网卡的性能总结报告。 -+ 要求: -+ 1.答案中只分析可能对系统性能产生影响的指标数据。 -+ 2.答案中不要包含任何优化建议。 -+ 3.答案中尽可能保留信息中真实有效的数据。 -+ 4.答案不超过200字。 -+ -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# OBJECTIVE # ++请根据这些性能指标,生成一份逻辑清晰、条理清楚的系统网卡的性能总结报告。 ++要求: ++1.答案中只分析可能对系统性能产生影响的指标数据。 ++2.答案中不要包含任何优化建议。 ++3.答案中尽可能保留信息中真实有效的数据。 ++4.答案不超过200字。 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # RESPONSE FORMAT # -+ 如果有多条分析结论,请用数字编号分点作答。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + ++# RESPONSE FORMAT # ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(network_adapter_prompt) + @@ -10429,39 +14380,38 @@ index 0000000..729447c + # TO DO + # 要有一个报告模板,指明包含哪些信息,以及报告格式 + report_prompt = f""" -+ 以下内容是linux系统中网络传输相关的性能信息: -+ {network_report} -+ 信息中所涉及到的数据准确无误,真实可信。 ++以下内容是linux系统中网络传输相关的性能信息: ++{network_report} ++信息中所涉及到的数据准确无误,真实可信。 + -+ # OBJECTIVE # -+ 请根据上述信息,分析系统网络传输的性能状况。 -+ 要求: -+ 1.答案中不要包含任何优化建议。 -+ 2.答案中尽可能保留信息中真实有效的数据。 -+ 3.不要遗漏任何值得分析的信息。 ++# OBJECTIVE # ++请根据上述信息,分析系统网络传输的性能状况。 ++要求: ++1.答案中不要包含任何优化建议。 ++2.答案中尽可能保留信息中真实有效的数据。 ++3.不要遗漏任何值得分析的信息。 + -+ # STYLE # -+ 你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 ++# STYLE # ++你是一个专业的系统运维专家,你的回答应该逻辑严谨、表述客观、简洁易懂、条理清晰,让你的回答真实可信 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请尽可能提供真实有用的信息,不要胡编乱造。 + -+ # RESPONSE FORMAT # -+ 回答以"网络分析如下:"开头,然后另起一行逐条分析。 -+ 如果有多条分析结论,请用数字编号分点作答。 -+ ++# RESPONSE FORMAT # ++回答以"网络分析如下:"开头,然后另起一行逐条分析。 ++如果有多条分析结论,请用数字编号分点作答。 + """ + return self.ask_llm(report_prompt) + "\n" \ No newline at end of file diff --git a/copilot-tune/src/performance_analyzer/performance_analyzer.py b/copilot-tune/src/performance_analyzer/performance_analyzer.py new file mode 100644 -index 0000000..1b55851 +index 0000000..d069e5f --- /dev/null +++ b/copilot-tune/src/performance_analyzer/performance_analyzer.py -@@ -0,0 +1,111 @@ +@@ -0,0 +1,110 @@ +from .cpu_analyzer import CpuAnalyzer +from .disk_analyzer import DiskAnalyzer +from .memory_analyzer import MemoryAnalyzer @@ -10498,30 +14448,29 @@ index 0000000..1b55851 + + def analyze(self, report: str) -> str: + bottle_neck_prompt = f""" -+ # CONTEXT # -+ 当前linux系统的性能分析报告如下,报告中所涉及到的数据准确无误,真实可信: -+ {report} -+ -+ # OBJECTIVE # -+ 请根据系统性能分析报告,确定当前系统是否存在性能瓶颈;如果存在性能瓶颈,则该瓶颈主要是存在于系统的哪个方面。 -+ 你应该依据多条信息和多个指标的数据进行综合判断,不要基于单点信息轻易下结论,你最终的结论应该能找到多个佐证。 -+ 要求: -+ 1.你必须从[CPU,NETWORK,DISK,MEMORY,NONE]这五个选项中选择一项作为你的答案。 -+ 2.不要回答多余的文字,你的答案必须严格和上述选项描述一致。 -+ 3.如果你认为没有性能瓶颈,请选择NONE。 ++# CONTEXT # ++当前linux系统的性能分析报告如下,报告中所涉及到的数据准确无误,真实可信: ++{report} + -+ # STYLE # -+ 你是一个专业的系统运维专家,你只用回答上述五个选项之一 ++# OBJECTIVE # ++请根据系统性能分析报告,确定当前系统是否存在性能瓶颈;如果存在性能瓶颈,则该瓶颈主要是存在于系统的哪个方面。 ++你应该依据多条信息和多个指标的数据进行综合判断,不要基于单点信息轻易下结论,你最终的结论应该能找到多个佐证。 ++要求: ++1.你必须从[CPU,NETWORK,DISK,MEMORY,NONE]这五个选项中选择一项作为你的答案。 ++2.不要回答多余的文字,你的答案必须严格和上述选项描述一致。 ++3.如果你认为没有性能瓶颈,请选择NONE。 + -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 ++# STYLE # ++你是一个专业的系统运维专家,你只用回答上述五个选项之一 + -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请认真思考后给出你的答案。 ++# Tone # ++你应该尽可能秉承严肃、认真、严谨的态度 + -+ # RESPONSE FORMAT # -+ 请直接回答五个选项之一,不要包含多余文字 ++# AUDIENCE # ++你的答案将会是其他系统运维专家的重要参考意见,请认真思考后给出你的答案。 + ++# RESPONSE FORMAT # ++请直接回答五个选项之一,不要包含多余文字 + """ + result = self.ask_llm(bottle_neck_prompt) + bottlenecks = { @@ -10540,13 +14489,13 @@ index 0000000..1b55851 + # 如果没有找到明确的瓶颈,返回UNKNOWN BOTTLENECKS + return "UNKNOWN BOTTLENECKS" + -+ def generate_report(self) -> Tuple[str, str]: -+ cpu_analyzer_task = self.thread_pool.add_task(self.cpu_analyzer.run) -+ disk_analyzer_task = self.thread_pool.add_task(self.disk_analyzer.run) -+ memory_analyzer_task = self.thread_pool.add_task(self.memory_analyzer.run) -+ network_analyzer_task = self.thread_pool.add_task(self.network_analyzer.run) -+ micro_analyzer_task = self.thread_pool.add_task(self.micro_analyer.run) -+ app_analyzer_task = self.thread_pool.add_task(self.app_analyzer.run) ++ def generate_report(self) -> Tuple[str, str]: ++ cpu_analyzer_task = self.thread_pool.add_task(self.cpu_analyzer.run) if self.cpu_analyzer.data else None ++ disk_analyzer_task = self.thread_pool.add_task(self.disk_analyzer.run) if self.disk_analyzer.data else None ++ memory_analyzer_task = self.thread_pool.add_task(self.memory_analyzer.run) if self.memory_analyzer.data else None ++ network_analyzer_task = self.thread_pool.add_task(self.network_analyzer.run) if self.network_analyzer.data else None ++ micro_analyzer_task = self.thread_pool.add_task(self.micro_analyer.run) if self.micro_analyer.data else None ++ app_analyzer_task = self.thread_pool.add_task(self.app_analyzer.run) if self.app_analyzer.data else None + + self.thread_pool.run_all_tasks() + task_results = self.thread_pool.get_all_results() @@ -10559,26 +14508,26 @@ index 0000000..1b55851 + ) + report_results[task_result.uuid] = task_result.result + -+ os_performance_report = "" -+ os_performance_report += report_results[cpu_analyzer_task] -+ os_performance_report += report_results[disk_analyzer_task] -+ os_performance_report += report_results[memory_analyzer_task] -+ os_performance_report += report_results[network_analyzer_task] -+ os_performance_report += report_results[micro_analyzer_task] ++ os_performance_report = "\n" ++ os_performance_report += (report_results.get(cpu_analyzer_task, "") + "\n") if self.cpu_analyzer.data else "" ++ os_performance_report += (report_results.get(disk_analyzer_task, "") + "\n") if self.disk_analyzer.data else "" ++ os_performance_report += (report_results.get(memory_analyzer_task, "") + "\n") if self.memory_analyzer.data else "" ++ os_performance_report += (report_results.get(network_analyzer_task, "") + "\n") if self.network_analyzer.data else "" ++ os_performance_report += (report_results.get(micro_analyzer_task, "") + "\n") if self.micro_analyer.data else "" + app_performance_report = "" -+ app_performance_report += report_results[app_analyzer_task] ++ app_performance_report += (report_results.get(app_analyzer_task, "") + "\n") if self.app_analyzer.data else "" + return os_performance_report, app_performance_report + + def run(self) -> Tuple[str, str]: + os_performance_report, app_performance_report = self.generate_report() -+ bottleneck = self.analyze(os_performance_report) ++ bottleneck = self.analyze(os_performance_report + app_performance_report).rstrip("\n") + return os_performance_report + app_performance_report, bottleneck diff --git a/copilot-tune/src/performance_collector/__init__.py b/copilot-tune/src/performance_collector/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/performance_collector/app_collector.py b/copilot-tune/src/performance_collector/app_collector.py new file mode 100644 -index 0000000..31886a2 +index 0000000..c99114e --- /dev/null +++ b/copilot-tune/src/performance_collector/app_collector.py @@ -0,0 +1,45 @@ @@ -10591,7 +14540,7 @@ index 0000000..31886a2 + +def load_app_collector(app: str): + # 构建模块路径和类名 -+ module_path = f".application.{app.lower()}_collector" ++ module_path = f"src.performance_collector.application.{app.lower()}_collector" + try: + # 动态导入模块(当前模块是包内的,使用相对导入) + module = importlib.import_module(module_path, package=__package__) @@ -10627,6 +14576,9 @@ index 0000000..31886a2 + if not self.collector: + return {} + return self.collector.run() +diff --git a/copilot-tune/src/performance_collector/application/__init__.py b/copilot-tune/src/performance_collector/application/__init__.py +new file mode 100644 +index 0000000..e69de29 diff --git a/copilot-tune/src/performance_collector/application/ceph_collector.py b/copilot-tune/src/performance_collector/application/ceph_collector.py new file mode 100644 index 0000000..6a1b62a @@ -10800,21 +14752,20 @@ index 0000000..6a1b62a + return {"ceph tell osd.* perf dump": result} diff --git a/copilot-tune/src/performance_collector/application/flink_collector.py b/copilot-tune/src/performance_collector/application/flink_collector.py new file mode 100644 -index 0000000..ce0b8df +index 0000000..04cdf55 --- /dev/null +++ b/copilot-tune/src/performance_collector/application/flink_collector.py -@@ -0,0 +1,269 @@ +@@ -0,0 +1,268 @@ +import json +import logging + +from src.config import config +from src.utils.collector.metric_collector import snapshot_task, CollectMode + -+FLINK_HOST = config["servers"][0]["ip"] -+FLINK_API = f"http://{FLINK_HOST}:8081" -+ -+logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") -+ ++FLINK_HOST = config["servers"][0]["listening_address"] if config["servers"][0]["listening_address"] else \ ++ config["servers"][0]["ip"] ++FLINK_PORT = config["servers"][0]["listening_port"] if config["servers"][0]["listening_port"] else 8081 ++FLINK_API = f"http://{FLINK_HOST}:{FLINK_PORT}" + +@snapshot_task( + cmd=( @@ -10864,7 +14815,7 @@ index 0000000..ce0b8df + +@snapshot_task( + cmd=( -+ "curl -s {FLINK_API}/jobs | jq -r '.jobs[0].id' | xargs -I{{}} curl -s {FLINK_API}/jobs/{{}}/checkpoints" ++ f"curl -s {FLINK_API}/jobs | jq -r '.jobs[0].id' | xargs -I{{}} curl -s {FLINK_API}/jobs/{{}}/checkpoints" + ), + tag="flink checkpoint状态", + collect_mode=CollectMode.ASYNC @@ -11075,31 +15026,25 @@ index 0000000..ce0b8df + }} diff --git a/copilot-tune/src/performance_collector/application/gaussdb_collector.py b/copilot-tune/src/performance_collector/application/gaussdb_collector.py new file mode 100644 -index 0000000..7210719 +index 0000000..3ff6932 --- /dev/null +++ b/copilot-tune/src/performance_collector/application/gaussdb_collector.py -@@ -0,0 +1,206 @@ +@@ -0,0 +1,185 @@ +import logging -+from io import StringIO -+ +import pandas as pd -+ ++from io import StringIO +from src.utils.collector.metric_collector import ( + period_task, + snapshot_task, + CollectMode, +) + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ -+GAUSS_INTERVAL = 180 -+ ++GAUSS_INTERVAL = 60 + +# -------------------- 1. 后台写入与检查点(两次采样) -------------------- +@period_task( -+ cmd='gsql -d tpch -A -F , -c "SELECT * FROM pg_stat_bgwriter;"', ++ # cmd='gsql -p 17777 -d tpcc1000w_ustore -A -F , -c "SELECT * FROM pg_stat_bgwriter;"', ++ cmd='source ~/.bashrc && gsql -d tpcc -p 11111 -c "SELECT * FROM pg_stat_bgwriter;"', + collect_mode=CollectMode.ASYNC, + tag="GaussDB后台写入与检查点", + delay=0, @@ -11134,13 +15079,13 @@ index 0000000..7210719 + except (ValueError, TypeError): + delta = 0 + result[f"{GAUSS_INTERVAL // 60}分钟内{label}"] = max(delta, 0) -+ cmd = '''gsql -d tpch -A -F , -c "SELECT * FROM pg_stat_bgwriter;"''' -+ return {cmd: result} ++ print("GaussDB后台写入与检查点:", result) ++ return result + + +# -------------------- 2. 事务与IO(两次采样) -------------------- +@period_task( -+ cmd='''gsql -d tpch -A -F , -c " ++ cmd='''source ~/.bashrc && gsql -p 11111 -d tpcc -A -F , -c " + SELECT sum(xact_commit) as commits, + sum(xact_rollback) as rollbacks, + sum(blks_read) as blks_read, @@ -11163,34 +15108,27 @@ index 0000000..7210719 + return {} + + r1, r2 = df1.iloc[0].to_dict(), df2.iloc[0].to_dict() -+ result = {} ++ res = {} + for col in ("commits", "rollbacks", "blks_read", "blks_hit", "tup_returned", "tup_fetched"): + try: + delta = int(r2[col]) - int(r1[col]) + except (ValueError, TypeError): + delta = 0 -+ result[f"{GAUSS_INTERVAL // 60}分钟内{col}"] = max(delta, 0) ++ res[f"{GAUSS_INTERVAL // 60}分钟内{col}"] = max(delta, 0) + + # 计算命中率 -+ hit_delta = result[f"{GAUSS_INTERVAL // 60}分钟内blks_hit"] -+ read_delta = result[f"{GAUSS_INTERVAL // 60}分钟内blks_read"] -+ result[f"{GAUSS_INTERVAL // 60}分钟内Buffer命中率"] = ( -+ round(hit_delta * 100 / (hit_delta + read_delta), 2) if (hit_delta + read_delta) else 0 ++ hit_delta = res.get(f"{GAUSS_INTERVAL // 60}分钟内blks_hit", 0) ++ read_delta = res.get(f"{GAUSS_INTERVAL // 60}分钟内blks_read", 0) ++ res[f"{GAUSS_INTERVAL // 60}分钟内Buffer命中率"] = ( ++ round(hit_delta * 100 / (hit_delta + read_delta), ++ 2) if (hit_delta + read_delta) else 0 + ) -+ cmd = '''gsql -d tpch -A -F , -c " -+ SELECT sum(xact_commit) as commits, -+ sum(xact_rollback) as rollbacks, -+ sum(blks_read) as blks_read, -+ sum(blks_hit) as blks_hit, -+ sum(tup_returned) as tup_returned, -+ sum(tup_fetched) as tup_fetched -+ FROM pg_stat_database;"''' -+ return {cmd: result} ++ return res + + +# -------------------- 3. 会话信息(实时快照) -------------------- +@snapshot_task( -+ cmd='''gsql -d tpch -A -F , -c " ++ cmd='''source ~/.bashrc && gsql -p 11111 -d tpcc -A -F , -c " +SELECT datname, state, waiting, enqueue +FROM pg_stat_activity;"''', + collect_mode=CollectMode.ASYNC, @@ -11204,33 +15142,33 @@ index 0000000..7210719 + "waiting": "是否等待", + "enqueue": "排队/锁信息", + } -+ cmd = '''gsql -d tpch -A -F , -c "SELECT datname, state, waiting, enqueueFROM pg_stat_activity;"''' -+ result = [ -+ {mapping.get(k, k): v for k, v in row.items()} -+ for _, row in df.iterrows() -+ ] -+ return {cmd: result} ++ return { ++ "会话信息": [ ++ {mapping.get(k, k): v for k, v in row.items()} ++ for _, row in df.iterrows() ++ ] ++ } + + +# -------------------- 4. 锁信息(实时快照) -------------------- +@snapshot_task( -+ cmd='''gsql -d tpch -A -F , -c "SELECT mode, granted, COUNT(*) AS count FROM pg_locks GROUP BY mode, granted;"''', ++ cmd='source ~/.bashrc && gsql -p 11111 -d tpcc -A -F , -c "SELECT mode, granted, COUNT(*) AS count FROM pg_locks GROUP BY mode, granted;"', + collect_mode=CollectMode.ASYNC, + tag="GaussDB锁信息", +) +def gauss_locks_parser(output: str) -> dict: + df = pd.read_csv(StringIO(output)) + mapping = {"mode": "锁模式", "granted": "是否已授予", "count": "锁数量"} -+ cmd = '''gsql -d tpch -A -F , -c "SELECT mode, granted, COUNT(*) AS count FROM pg_locks GROUP BY mode, granted;"''' -+ result = [ -+ {mapping.get(k, k): v for k, v in row.items()} for _, row in df.iterrows() -+ ] -+ return {cmd: result} ++ return { ++ "锁信息": [ ++ {mapping.get(k, k): v for k, v in row.items()} for _, row in df.iterrows() ++ ] ++ } + + +# -------------------- 5. 数据库级统计(实时快照) -------------------- +@snapshot_task( -+ cmd='''gsql -d tpch -A -F , -c "SELECT datname, numbackends, xact_commit, xact_rollback, ++ cmd='''source ~/.bashrc && gsql -p 11111 -d tpcc -A -F , -c "SELECT datname, numbackends, xact_commit, xact_rollback, + blks_read, blks_hit, pg_database_size(datname) AS db_size_bytes + FROM pg_stat_database WHERE datname NOT IN ('template0', 'template1');"''', + collect_mode=CollectMode.ASYNC, @@ -11247,19 +15185,16 @@ index 0000000..7210719 + "blks_hit": "缓冲命中块数", + "db_size_bytes": "数据库大小(Bytes)", + } -+ cmd = '''gsql -d tpch -A -F , -c "SELECT datname, numbackends, xact_commit, xact_rollback, -+ blks_read, blks_hit, pg_database_size(datname) AS db_size_bytes -+ FROM pg_stat_database WHERE datname NOT IN ('template0', 'template1');"''' -+ result = [ -+ {mapping.get(k, k): v for k, v in row.items()} for _, row in df.iterrows() -+ ] -+ -+ return {cmd: result} ++ return { ++ "数据库统计": [ ++ {mapping.get(k, k): v for k, v in row.items()} for _, row in df.iterrows() ++ ] ++ } + + +# -------------------- 6. 内存使用(实时快照) -------------------- +@snapshot_task( -+ cmd='''gsql -d tpch -A -F , -c " ++ cmd='''source ~/.bashrc && gsql -p 11111 -d tpcc -A -F , -c " + SELECT + 'localhost' AS node_name, + SUM(usedsize) AS dynamic_used_memory_bytes, @@ -11275,16 +15210,11 @@ index 0000000..7210719 + "dynamic_used_memory": "已使用动态内存(MB)", + "dynamic_peak_memory": "动态内存峰值(MB)", + } -+ cmd = '''gsql -d tpch -A -F , -c " -+ SELECT -+ 'localhost' AS node_name, -+ SUM(usedsize) AS dynamic_used_memory_bytes, -+ MAX(usedsize) AS dynamic_peak_memory_bytes -+ FROM gs_session_memory_detail;"''' -+ result = [ -+ {mapping.get(k, k): v for k, v in row.items()} for _, row in df.iterrows() -+ ] -+ return {cmd: result} ++ return { ++ "内存信息": [ ++ {mapping.get(k, k): v for k, v in row.items()} for _, row in df.iterrows() ++ ] ++ } diff --git a/copilot-tune/src/performance_collector/application/mysql_collector.py b/copilot-tune/src/performance_collector/application/mysql_collector.py new file mode 100644 index 0000000..6559589 @@ -11438,21 +15368,21 @@ index 0000000..6559589 + return result diff --git a/copilot-tune/src/performance_collector/application/nginx_collector.py b/copilot-tune/src/performance_collector/application/nginx_collector.py new file mode 100644 -index 0000000..77c7f52 +index 0000000..035ca4d --- /dev/null +++ b/copilot-tune/src/performance_collector/application/nginx_collector.py -@@ -0,0 +1,88 @@ +@@ -0,0 +1,87 @@ +import logging +import re ++ ++from src.config import config +from src.utils.collector.metric_collector import ( + period_task, + CollectMode, +) + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ ++NINGX_HOST = config["servers"][0]["listening_address"] if config["servers"][0]["listening_address"] else "127.0.0.1" ++NINGX_PORT = config["servers"][0]["listening_port"] if config["servers"][0]["listening_port"] else 10000 + +NGINX_SAMPLE_INTERVAL = 5 # 每次采样间隔 +SAMPLE_COUNT = 13 # 采样次数 @@ -11484,7 +15414,7 @@ index 0000000..77c7f52 + + +@period_task( -+ cmd="curl -s http://127.0.0.1:10000/status", ++ cmd=f"curl -s http://{NINGX_HOST}:{NINGX_PORT}/status", + tag="nginx_status指标", + delay=0, + sample_count=SAMPLE_COUNT, @@ -11508,7 +15438,7 @@ index 0000000..77c7f52 + conn_sum[k] += item.get(k, 0) + + avg_conns = { -+ f"{DURATION}s内平均{k}": conn_sum[k] // valid_samples ++ f"{DURATION}s内平均{k}": conn_sum[k] // valid_samples if valid_samples > 0 else 0 + for k in conn_keys + } + @@ -11529,13 +15459,150 @@ index 0000000..77c7f52 + } + result.update(avg_conns) + return {"curl -s http://127.0.0.1:10000/status": result} +diff --git a/copilot-tune/src/performance_collector/application/oceanbase_collector.py b/copilot-tune/src/performance_collector/application/oceanbase_collector.py +new file mode 100644 +index 0000000..399ad4e +--- /dev/null ++++ b/copilot-tune/src/performance_collector/application/oceanbase_collector.py +@@ -0,0 +1,132 @@ ++""" ++OceanBase 性能指标采集模块 ++ ++作用: ++ - 用于 A-Tune Euler Copilot 调优系统采集 OceanBase 应用层指标 ++""" ++ ++from typing import Dict ++from src.utils.collector.metric_collector import snapshot_task, CollectMode ++from src.utils.config.global_config import env_config ++ ++ ++# 从配置中读取 OceanBase 登录信息 ++ob_config = env_config.get("app_config").get("oceanbase", {}) ++ob_user = ob_config.get("user", "root@sysbench_tenant") ++ob_password = ob_config.get("password", "") ++ob_port = ob_config.get("port", 2881) ++ob_host = ob_config.get("host", "127.0.0.1") ++ob_database = ob_config.get("database", "oceanbase") ++ ++def _obclient_base(cmd: str) -> str: ++ """ ++ 拼接 obclient 执行命令模板 ++ """ ++ pwd_option = f"-p{ob_password}" if ob_password else "" ++ return f"obclient -h{ob_host} -P{ob_port} -u{ob_user} {pwd_option} -D{ob_database} -A -e \"{cmd}\"" ++ ++ ++def _ob_parse(stdout: str) -> Dict: ++ """ ++ 通用 OceanBase 输出解析函数 ++ """ ++ result = {} ++ lines = stdout.strip().split("\n") ++ for line in lines: ++ parts = line.split("\t") ++ if len(parts) == 2: ++ key, value = parts ++ result[key.strip()] = value.strip() ++ return result ++ ++ ++# ----------------- 指标采集任务定义 ----------------- ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'connections';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase 当前连接数" ++) ++def parse_ob_connections(output: str) -> Dict: ++ return {"connections": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'uptime';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase 运行时间(秒)" ++) ++def parse_ob_uptime(output: str) -> Dict: ++ return {"uptime": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'com_select';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase SELECT 执行次数" ++) ++def parse_ob_com_select(output: str) -> Dict: ++ return {"com_select": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'com_insert';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase INSERT 执行次数" ++) ++def parse_ob_com_insert(output: str) -> Dict: ++ return {"com_insert": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'com_update';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase UPDATE 执行次数" ++) ++def parse_ob_com_update(output: str) -> Dict: ++ return {"com_update": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'com_delete';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase DELETE 执行次数" ++) ++def parse_ob_com_delete(output: str) -> Dict: ++ return {"com_delete": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show global status like 'slow_queries';"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase 慢查询次数" ++) ++def parse_ob_slow_queries(output: str) -> Dict: ++ return {"slow_queries": _ob_parse(output)} ++ ++ ++@snapshot_task( ++ cmd=_obclient_base("show processlist;"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase 当前进程列表" ++) ++def parse_ob_processlist(output: str) -> Dict: ++ return {"processlist": output} ++ + ++@snapshot_task( ++ cmd=_obclient_base("show parameters;"), ++ collect_mode=CollectMode.ASYNC, ++ tag="OceanBase 系统参数" ++) ++def parse_ob_parameters(output: str) -> Dict: ++ """ ++ 获取当前 OceanBase 参数列表(用于调优参考) ++ """ ++ result = {} ++ lines = output.strip().split("\n") ++ for line in lines: ++ parts = line.split("\t") ++ if len(parts) >= 2: ++ result[parts[0]] = parts[1] ++ return {"parameters": result} diff --git a/copilot-tune/src/performance_collector/application/pgsql_collector.py b/copilot-tune/src/performance_collector/application/pgsql_collector.py new file mode 100644 -index 0000000..0d2c04f +index 0000000..b9d606f --- /dev/null +++ b/copilot-tune/src/performance_collector/application/pgsql_collector.py -@@ -0,0 +1,128 @@ +@@ -0,0 +1,124 @@ +import logging +from io import StringIO + @@ -11547,10 +15614,6 @@ index 0000000..0d2c04f + CollectMode, +) + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ +BIG_WRITER_COLLECT_INTERVAL = 180 + + @@ -11779,29 +15842,31 @@ index 0000000..c36186a + return {cmd: result} diff --git a/copilot-tune/src/performance_collector/application/spark_collector.py b/copilot-tune/src/performance_collector/application/spark_collector.py new file mode 100644 -index 0000000..f0361ce +index 0000000..bcda936 --- /dev/null +++ b/copilot-tune/src/performance_collector/application/spark_collector.py -@@ -0,0 +1,146 @@ +@@ -0,0 +1,148 @@ ++import json +import logging ++ +import requests -+import json ++ ++from src.config import config +from src.utils.collector.metric_collector import ( + period_task, + snapshot_task, + CollectMode, +) -+from src.config import config + -+HOST_IP = config["servers"][0]["ip"] -+SPARK_HISTORY_SERVER = f"http://{HOST_IP}:18080" ++SPARK_HOST = config["servers"][0]["listening_address"] if config["servers"][0]["listening_address"] else \ ++ config["servers"][0]["ip"] ++SPARK_PORT = config["servers"][0]["listening_port"] if config["servers"][0]["listening_port"] else 18080 ++ ++SPARK_HISTORY_SERVER = f"http://{SPARK_HOST}:{SPARK_PORT}" +SAMPLE_INTERVAL = 60 +SAMPLE_COUNT = 2 +DURATION = SAMPLE_INTERVAL * (SAMPLE_COUNT - 1) + -+logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s") -+ -+ +@snapshot_task( + cmd="curl -s {}/api/v1/applications | jq -r '.[0].id'".format(SPARK_HISTORY_SERVER), + tag="spark作业信息", @@ -11931,13 +15996,14 @@ index 0000000..f0361ce + return {} diff --git a/copilot-tune/src/performance_collector/base_collector.py b/copilot-tune/src/performance_collector/base_collector.py new file mode 100644 -index 0000000..5e2fb27 +index 0000000..1cfe5d8 --- /dev/null +++ b/copilot-tune/src/performance_collector/base_collector.py -@@ -0,0 +1,63 @@ +@@ -0,0 +1,66 @@ +from abc import abstractmethod +from typing import Dict, Any +from typing import List, Optional ++import logging + +from pydantic import BaseModel, Field + @@ -11967,6 +16033,8 @@ index 0000000..5e2fb27 + cmd_res = self.args.ssh_client.run_cmd( + cmd=cmd + ) ++ if cmd_res.status_code != 0: ++ logging.warning(f"collector cmd {cmd} returns {cmd_res.status_code}: {cmd_res.err_msg}") + res = {cmd: cmd_res.output} + result = {**result, **res} + return result @@ -12000,10 +16068,10 @@ index 0000000..5e2fb27 + return processed_data diff --git a/copilot-tune/src/performance_collector/cpu_collector.py b/copilot-tune/src/performance_collector/cpu_collector.py new file mode 100644 -index 0000000..3b5e298 +index 0000000..4d9bb0d --- /dev/null +++ b/copilot-tune/src/performance_collector/cpu_collector.py -@@ -0,0 +1,304 @@ +@@ -0,0 +1,302 @@ +from .base_collector import BaseCollector +from typing import Dict, Any, List +import logging @@ -12015,8 +16083,6 @@ index 0000000..3b5e298 + FIVE_MINUTE_AVG_LOAD = "5min" + TEN_MINUTE_AVG_LOAD = "10min" + -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ +perf = "perf stat -e 'syscalls:*' -a sleep 1 2>&1 | grep syscalls| awk '{sum += $1} END {print sum}'" + +def get_cpu_cmd()-> List[str]: @@ -12310,10 +16376,10 @@ index 0000000..3b5e298 + return cpu_process_result diff --git a/copilot-tune/src/performance_collector/disk_collector.py b/copilot-tune/src/performance_collector/disk_collector.py new file mode 100644 -index 0000000..e35cd8c +index 0000000..9912b53 --- /dev/null +++ b/copilot-tune/src/performance_collector/disk_collector.py -@@ -0,0 +1,117 @@ +@@ -0,0 +1,115 @@ +from .base_collector import BaseCollector +from typing import Dict, Any, List +import logging @@ -12323,8 +16389,6 @@ index 0000000..e35cd8c +class DiskMetric(Enum): + TODO = "XX" + -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ +def get_disk_cmd()-> List[str]: + return list(DISK_PARSE_FUNCTIONS.keys()) + @@ -12433,10 +16497,10 @@ index 0000000..e35cd8c + return disk_process_result diff --git a/copilot-tune/src/performance_collector/memory_collector.py b/copilot-tune/src/performance_collector/memory_collector.py new file mode 100644 -index 0000000..80adbe5 +index 0000000..331ad2e --- /dev/null +++ b/copilot-tune/src/performance_collector/memory_collector.py -@@ -0,0 +1,151 @@ +@@ -0,0 +1,147 @@ +from .base_collector import BaseCollector +from typing import Dict, Any, List +import logging @@ -12445,9 +16509,6 @@ index 0000000..80adbe5 +class MemoryMetric(Enum): + TODO = "XX" + -+ -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ +omm_kill_cmd = "(oom_kill1=$(cat /proc/vmstat | grep oom_kill | awk '{print$2}'); sleep 5; oom_kill2=$(cat /proc/vmstat | grep oom_kill | awk '{print$2}')) && echo $((oom_kill2 - oom_kill1))" + +def get_memory_cmd()-> List[str]: @@ -12516,7 +16577,6 @@ index 0000000..80adbe5 + + try: + out = stdout.split("\n") -+ out.pop() + date = out[-1].split() + memory_usage = float(date[4]) + @@ -12590,7 +16650,7 @@ index 0000000..80adbe5 + return memory_process_result diff --git a/copilot-tune/src/performance_collector/metric_collector.py b/copilot-tune/src/performance_collector/metric_collector.py new file mode 100644 -index 0000000..d06fab2 +index 0000000..daf0988 --- /dev/null +++ b/copilot-tune/src/performance_collector/metric_collector.py @@ -0,0 +1,81 @@ @@ -12656,7 +16716,7 @@ index 0000000..d06fab2 + + if event_status == TriggerStatus.CLOSE: + raise RuntimeError( -+ f"[MetricCollector] waiting for trigger signale timeout, skip tasks" ++ f"[MetricCollector] waiting for trigger signal timeout, skip tasks" + ) + # 调用每个子收集器的 run 方法 + cpu_data = self.cpu_collector.run() @@ -12677,13 +16737,13 @@ index 0000000..d06fab2 + return combined_data diff --git a/copilot-tune/src/performance_collector/micro_dep_collector.py b/copilot-tune/src/performance_collector/micro_dep_collector.py new file mode 100644 -index 0000000..972e92b +index 0000000..80fb544 --- /dev/null +++ b/copilot-tune/src/performance_collector/micro_dep_collector.py -@@ -0,0 +1,453 @@ +@@ -0,0 +1,461 @@ +import logging +from abc import ABC, abstractmethod -+from typing import Dict, List ++from typing import Dict, List, Optional + +from src.utils.shell_execute import SshClient + @@ -12859,7 +16919,7 @@ index 0000000..972e92b + + def __init__( + self, -+ ssh_client: SshClient | None = None, ++ ssh_client: Optional[SshClient] = None, + duration: float = 0.1, + target_pid: int = 0 + ): @@ -12868,7 +16928,7 @@ index 0000000..972e92b + def process(self): + """处理TopDown性能数据""" + # 提取微架构参数 -+ if self.raw_data["cycle"] != 0 and self.raw_data["execstall_cycle"] != 0: ++ try: + dispatch_size = self.FW_CONFIG["dispatch_size"] + + # 计算各级指标 @@ -12926,6 +16986,8 @@ index 0000000..972e92b + self.processed_data['context_switches'] = self.raw_data['context_switches'] + self.processed_data['cpu_migrations'] = self.raw_data['cpu_migrations'] + self.processed_data['page_faults'] = self.raw_data['page_faults'] ++ except Exception as e: ++ return + + +class CacheCollector(PerfCollector): @@ -12933,7 +16995,7 @@ index 0000000..972e92b + + def __init__( + self, -+ ssh_client: SshClient | None = None, ++ ssh_client: Optional[SshClient] = None, + duration: float = 0.1, + target_pid: int = 0 + ): @@ -12941,7 +17003,7 @@ index 0000000..972e92b + + def process(self): + """处理缓存性能数据""" -+ if self.raw_data['l1i_access'] != 0: ++ try: + inst_retired = self.raw_data['inst_retired'] + + # 计算各级缓存指标 @@ -12956,6 +17018,8 @@ index 0000000..972e92b + + self.processed_data['l2i_mpki'] = self.raw_data['l2i_refill'] / inst_retired * 1000 + self.processed_data['l2d_mpki'] = self.raw_data['l2d_refill'] / inst_retired * 1000 ++ except Exception as e: ++ return + + +class BranchCollector(PerfCollector): @@ -12963,7 +17027,7 @@ index 0000000..972e92b + + def __init__( + self, -+ ssh_client: SshClient | None = None, ++ ssh_client: Optional[SshClient] = None, + duration: float = 0.1, + target_pid: int = 0 + ): @@ -12971,7 +17035,7 @@ index 0000000..972e92b + + def process(self): + """处理分支预测性能数据""" -+ if self.raw_data['cycle'] != 0 and self.raw_data['brmisspred'] != 0 and self.raw_data['brpred'] != 0: ++ try: + cycle = self.raw_data['cycle'] + brmisspred = self.raw_data['brmisspred'] + brpred = self.raw_data['brpred'] @@ -12981,6 +17045,8 @@ index 0000000..972e92b + self.processed_data['alu_isq_stall'] = self.raw_data['alu_isq_stall'] / cycle * 100 + self.processed_data['lsu_isq_stall'] = self.raw_data['lsu_isq_stall'] / cycle * 100 + self.processed_data['fsu_isq_stall'] = self.raw_data['fsu_isq_stall'] / cycle * 100 ++ except Exception as e: ++ return + + +class TlbCollector(PerfCollector): @@ -12988,7 +17054,7 @@ index 0000000..972e92b + + def __init__( + self, -+ ssh_client: SshClient | None = None, ++ ssh_client: Optional[SshClient] = None, + duration: float = 0.1, + target_pid: int = 0 + ): @@ -12996,7 +17062,7 @@ index 0000000..972e92b + + def process(self): + """处理TLB性能数据""" -+ if self.raw_data['inst_retired'] != 0 and self.raw_data['cycle'] != 0: ++ try: + inst_retired = self.raw_data['inst_retired'] + cycle = self.raw_data['cycle'] + @@ -13020,6 +17086,8 @@ index 0000000..972e92b + self.processed_data['dtlb_walk_mpki'] = self.raw_data['dtlb_walk'] / inst_retired * 1000 + + self.processed_data['div_stall'] = self.raw_data['divstall'] / cycle * 100 ++ except Exception as e: ++ return + + +class MicroDepCollector: @@ -13136,10 +17204,10 @@ index 0000000..972e92b + logging.info(f"{metric}: {value:.2f}") diff --git a/copilot-tune/src/performance_collector/network_collector.py b/copilot-tune/src/performance_collector/network_collector.py new file mode 100644 -index 0000000..8963ebd +index 0000000..4c20283 --- /dev/null +++ b/copilot-tune/src/performance_collector/network_collector.py -@@ -0,0 +1,139 @@ +@@ -0,0 +1,136 @@ +import logging +from enum import Enum +from typing import Dict, Any, List @@ -13150,9 +17218,6 @@ index 0000000..8963ebd +class NetworkMetric(Enum): + TODO = "XX" + -+ -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ +ListenOverflows = ("ListenOverflows1=$(cat /proc/net/netstat | grep 'TcpExt:' | awk '{print$20}' | tail -n 1); sleep 5;" + "ListenOverflows2=$(cat /proc/net/netstat | grep 'TcpExt:' | awk '{print$20}' | tail -n 1); " + "echo $((ListenOverflows2 - ListenOverflows1))") @@ -13281,19 +17346,17 @@ index 0000000..8963ebd + return network_process_result diff --git a/copilot-tune/src/performance_collector/static_metric_profile_collector.py b/copilot-tune/src/performance_collector/static_metric_profile_collector.py new file mode 100644 -index 0000000..569d47f +index 0000000..f58280c --- /dev/null +++ b/copilot-tune/src/performance_collector/static_metric_profile_collector.py -@@ -0,0 +1,52 @@ +@@ -0,0 +1,58 @@ +import logging ++from typing import Dict + +from src.performance_collector import static_profile_collector +from src.utils.shell_execute import get_registered_cmd_funcs +from src.utils.thread_pool import ThreadPoolManager + -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ -+ +class StaticMetricProfileCollector: + def __init__( + self, @@ -13311,10 +17374,14 @@ index 0000000..569d47f + + def _add_tasks(self, *args): + for module in args: -+ func_info_list = get_registered_cmd_funcs(module) -+ self.thread_pool.add_batch( -+ [(func_info["func"], (self.ssh_client,), {"tag": func_info["tag"]}) for func_info in func_info_list] -+ ) ++ func_info_list = get_registered_cmd_funcs(module, parallel = True) ++ task_batch = [] ++ for func_info in func_info_list: ++ func = func_info["func"] ++ func_args = (self.ssh_client,) ++ func_kwargs = {"tag": func_info["tag"]} ++ task_batch.append((func, func_args, func_kwargs)) ++ self.thread_pool.add_batch(task_batch) + + def sequential_tasks(self): + pass @@ -13323,36 +17390,35 @@ index 0000000..569d47f + logging.info( + "[StaticMetricProfileCollector] collecting static profile data ..." + ) -+ parsed_results = {} ++ parsed_results: Dict[str, Dict] = {} + + self.thread_pool.run_all_tasks() + task_results = self.thread_pool.get_all_results() + + for task_result in task_results: ++ if task_result.status_code != 0: ++ logging.warning(f"failed to execute task {task_result.func_name}, exception is {task_result.result}") ++ continue + if task_result.tag not in parsed_results: + parsed_results[task_result.tag] = {} ++ # each task returns ExecueteResult object, and its output is dict of matric key and values + if task_result.result.status_code == 0: + parsed_results[task_result.tag].update(task_result.result.output) + else: -+ logging.warning(f"error while execute task {task_result.func_name}, err_msg is {task_result.result}") ++ logging.warning(f"error while execute task {task_result.func_name}, err_msg is {task_result.result.err_msg}") + + return parsed_results diff --git a/copilot-tune/src/performance_collector/static_profile_collector.py b/copilot-tune/src/performance_collector/static_profile_collector.py new file mode 100644 -index 0000000..438d1df +index 0000000..22a4042 --- /dev/null +++ b/copilot-tune/src/performance_collector/static_profile_collector.py -@@ -0,0 +1,246 @@ +@@ -0,0 +1,248 @@ +import logging +import re + +from src.utils.shell_execute import cmd_pipeline + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ -+ +@cmd_pipeline(cmd="lscpu", tag="static", parallel=True) +def lscpu_parser(output: str) -> dict: + """解析 lscpu 输出:物理/逻辑核心、主频、L3 Cache、NUMA 拓扑""" @@ -13528,8 +17594,15 @@ index 0000000..438d1df + if len(lines) < 2: + return metrics + header = re.split(r"\s+", lines[0].strip()) -+ idx_fs = header.index("Type") -+ idx_mount = header.index("Mounted") ++ if "Type" and "Mounted" in header: ++ ++ idx_fs = header.index("Type") ++ idx_mount = header.index("Mounted") ++ elif "类型" and "挂载点" in header: ++ idx_fs = header.index("类型") ++ idx_mount = header.index("挂载点") ++ else: ++ return metrics + for l in lines[1:]: + cols = re.split(r"\s+", l.strip()) + fs = cols[idx_fs] @@ -13556,7 +17629,7 @@ index 0000000..438d1df + + +@cmd_pipeline( -+ cmd="ethtool -l $(ls /sys/class/net | grep -v lo | head -n1)", ++ cmd="for iface in $(ls /sys/class/net); do ethtool -l $iface &>/dev/null && ethtool $iface && break; done", + tag="static", + parallel=True, +) @@ -13571,7 +17644,7 @@ index 0000000..438d1df + return metrics + + -+@cmd_pipeline(cmd="lspci -vv | grep -i sriov -A5", tag="static", parallel=True) ++@cmd_pipeline(cmd="lspci -vv | grep -i sriov -A5 || echo ''", tag="static", parallel=True) +def sriov_parser(output: str) -> dict: + """ + 解析 lspci -vv | grep -i sriov -A5:是否支持 SR-IOV,最大 VF 数 @@ -13594,10 +17667,10 @@ new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/performance_optimizer/base_optimizer.py b/copilot-tune/src/performance_optimizer/base_optimizer.py new file mode 100644 -index 0000000..5a4421d +index 0000000..c97740c --- /dev/null +++ b/copilot-tune/src/performance_optimizer/base_optimizer.py -@@ -0,0 +1,185 @@ +@@ -0,0 +1,181 @@ +import logging +import os +from abc import abstractmethod @@ -13610,10 +17683,6 @@ index 0000000..5a4421d +from src.utils.llm import get_llm_response +from src.utils.shell_execute import SshClient + -+# 配置日志 -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ -+ +class OptimizerArgs(BaseModel): + bottle_neck: str = "" + application: str = "" @@ -13785,10 +17854,10 @@ index 0000000..5a4421d + return optimization_plan, isfinished, optimization_feedback["reason"] diff --git a/copilot-tune/src/performance_optimizer/knob_optimizer.py b/copilot-tune/src/performance_optimizer/knob_optimizer.py new file mode 100644 -index 0000000..f3dbf6d +index 0000000..3b68573 --- /dev/null +++ b/copilot-tune/src/performance_optimizer/knob_optimizer.py -@@ -0,0 +1,132 @@ +@@ -0,0 +1,130 @@ +import json +import logging +import os @@ -13798,8 +17867,6 @@ index 0000000..f3dbf6d +from .base_optimizer import BaseOptimizer +from ..utils.constant import KNOB_RAG_CONFIG_PATH + -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ +CORE_CPU_KNOBS = [ + "kernel.numa_balancing", + "kernel.sched_autogroup_enabled", @@ -13923,10 +17990,10 @@ index 0000000..f3dbf6d + return [] diff --git a/copilot-tune/src/performance_optimizer/param_knowledge.py b/copilot-tune/src/performance_optimizer/param_knowledge.py new file mode 100644 -index 0000000..1d2128f +index 0000000..835ad74 --- /dev/null +++ b/copilot-tune/src/performance_optimizer/param_knowledge.py -@@ -0,0 +1,117 @@ +@@ -0,0 +1,119 @@ +import logging +import threading +from typing import Iterable @@ -13991,6 +18058,7 @@ index 0000000..1d2128f + def describe_param_background_knob(self, app_name: str, params: Iterable): + logging.info(f"[ParamKnowledge] building param knowledge base ...") + params_describe_list = [] ++ params_default_info = {} + app_params = self.param_config.get(app_name.lower()) + system_params = self.param_config.get("system") + app = AppInterface(self.ssh_client).get(app_name) @@ -14019,8 +18087,9 @@ index 0000000..1d2128f + params_describe_list.append( + f"{param_name}:{item['desc']},参数数据类型为:{item['dtype']},参数的取值范围是:{param_range}, 当前环境取值为:{param_env_value}" + ) ++ params_default_info[param_name] = param_env_value + logging.info(f"[ParamKnowledge] initialize param knowledge base finished!") -+ return params_describe_list ++ return params_describe_list, params_default_info + + +if __name__ == "__main__": @@ -14046,23 +18115,18 @@ index 0000000..1d2128f + print(res) diff --git a/copilot-tune/src/performance_optimizer/param_optimizer.py b/copilot-tune/src/performance_optimizer/param_optimizer.py new file mode 100644 -index 0000000..8da3e21 +index 0000000..fb4b213 --- /dev/null +++ b/copilot-tune/src/performance_optimizer/param_optimizer.py -@@ -0,0 +1,215 @@ +@@ -0,0 +1,252 @@ +import logging + +from src.performance_optimizer.param_recommender import ParamRecommender ++from src.performance_optimizer.param_knowledge import ParamKnowledge +from src.performance_test.pressure_test import wait_for_pressure_test +from src.utils.config.app_config import AppInterface +from src.utils.shell_execute import SshClient + -+# 配置日志 -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ -+ +class ParamOptimizer: + + def __init__( @@ -14091,6 +18155,16 @@ index 0000000..8da3e21 + self.slo_calc_callback = slo_calc_callback + # 业务预期指标提升的目标 + self.slo_goal = slo_goal ++ # 可调参数知识库,用于给大模型描述应用参数背景知识 ++ self.param_knowledge = ParamKnowledge( ++ ssh_client=ssh_client, ++ tune_system_param=tune_system_param, ++ tune_app_param=tune_app_param ++ ) ++ self.all_params = self.param_knowledge.get_params(service_name) ++ self.params_set, self.current_params = self.param_knowledge.describe_param_background_knob( ++ service_name, self.all_params ++ ) + # 应用接口,包括应用参数下发、benchmark执行等操作 + self.app_interface = AppInterface(ssh_client).get(service_name) + self.system_interface = AppInterface(ssh_client).system @@ -14103,8 +18177,8 @@ index 0000000..8da3e21 + static_profile=static_profile, + performance_analysis_report=analysis_report, + ssh_client=ssh_client, -+ tune_system_param=tune_system_param, -+ tune_app_param=tune_app_param ++ all_params=self.all_params, ++ params_set=self.params_set + ) + self.first_restart_save = True + self.benchmark_timeout=benchmark_timeout @@ -14121,9 +18195,16 @@ index 0000000..8da3e21 + logging.info("🔄 正在验证benchmark性能...") + result = self.app_interface.benchmark() + if result.status_code == 0 and result.output: -+ return float(result.output) ++ try: ++ perf_value = float(result.output) ++ logging.info(f"Benchmark 成功,性能值: {perf_value}") ++ return perf_value ++ except Exception as e: ++ logging.warning(f"Benchmark 失败,结果是:{result.output}") ++ return None + else: -+ raise RuntimeError(f"failed to execute benchmark because {result.err_msg}") ++ logging.warning(f"Benchmark 执行失败: {result.err_msg}") ++ return None + + def apply_params(self, recommend_params): + for param_name, param_value in recommend_params.items(): @@ -14137,14 +18218,12 @@ index 0000000..8da3e21 + logging.info("🔄 正在重启应用 ...") + stop_result = self.app_interface.stop_workload() + if stop_result.status_code != 0: -+ raise RuntimeError( -+ f"failed to stop application because {stop_result.err_msg}" -+ ) ++ logging.warning(f"failed to stop application because {stop_result.err_msg}") + start_result = self.app_interface.start_workload() + if start_result.status_code != 0: -+ raise RuntimeError( -+ f"failed to start application because {start_result.err_msg}" -+ ) ++ logging.warning(f"failed to start application because {start_result.err_msg}") ++ return False ++ return True + + def recover_cluster(self): + print("🔄 正在恢复集群 ...") @@ -14195,7 +18274,7 @@ index 0000000..8da3e21 + f"[ParamOptimizer] failed to run pressure test, err msg is {pressure_test_result.err_msg}" + ) + -+ baseline = float(pressure_test_result.output.output) ++ baseline = float(pressure_test_result.output) + logging.info( + f"[ParamOptimizer] pressure test finished, baseline is {baseline}" + ) @@ -14209,6 +18288,8 @@ index 0000000..8da3e21 + } + best_result = baseline + worst_result = baseline ++ curr_recommend_params = {} ++ best_recommend_params = {} + is_positive = True + symbol = self.app_interface.get_calculate_type() + logging.info( @@ -14222,7 +18303,13 @@ index 0000000..8da3e21 + # 设置参数生效 + self.apply_params(recommend_params) + if self.need_restart_application: -+ self.restart_application() ++ restart_success = self.restart_application() ++ if not restart_success: ++ historys["上一轮调优结果"] = {"上一轮性能": "应用重启失败,参数不合法", "参数推荐": recommend_params} ++ self.apply_params(self.current_params) ++ restart_success = self.restart_application() ++ logging.warning(f"[{i + 1}/{self.max_iterations}] 应用重启失败,参数不合法,恢复第 {i} 轮配置,恢复成功:{restart_success}") ++ continue + + # 执行benchmark,反馈调优结果 + performance_result = self.benchmark() @@ -14231,14 +18318,25 @@ index 0000000..8da3e21 + script_path = '/tmp/euler-copilot-params.sh' + self.save_restart_params_to_script(recommend_params, script_path, i + 1) + self.recover_cluster() ++ if performance_result is None: ++ historys["上一轮调优结果"] = {"上一轮性能": "benchmark失败,参数不合理", "参数推荐": recommend_params} ++ self.apply_params(self.current_params) ++ restart_success = True ++ if self.need_restart_application: ++ restart_success = self.restart_application() ++ logging.warning(f"[{i + 1}/{self.max_iterations}] benchmark失败,参数不合理,恢复第 {i} 轮配置,恢复成功:{restart_success}") ++ continue ++ self.current_params.update(recommend_params) ++ curr_recommend_params.update(recommend_params) + -+ if performance_result * symbol < baseline: ++ if performance_result * symbol < baseline * symbol: + is_positive = False + else: + is_positive = True + + if performance_result * symbol > best_result * symbol: + best_result = performance_result ++ best_recommend_params = dict(curr_recommend_params) + best_history = {"最佳性能": performance_result, "参数推荐": recommend_params} + historys["历史最佳结果"] = best_history + @@ -14251,26 +18349,34 @@ index 0000000..8da3e21 + + ratio = self.calc_improve_rate(baseline, performance_result, symbol) + -+ # 达到预期效果,则退出循环 -+ if self.reached_goal(baseline, performance_result, symbol): -+ logging.info( -+ f"[{i + 1}/{self.max_iterations}] 性能基线是:{baseline}, 最佳结果:{best_result}, 本轮结果:{performance_result if performance_result is not None else '-'}, 性能提升:{ratio:.2%}" -+ ) -+ break -+ + logging.info( + f"[{i + 1}/{self.max_iterations}] 性能基线是:{baseline}, 最佳结果:{best_result}, 本轮结果:{performance_result if performance_result is not None else '-'}, 性能提升:{ratio:.2%}" + ) + ++ # 达到预期效果,则退出循环 ++ if self.reached_goal(baseline, performance_result, symbol): ++ break ++ + logging.info( + f"调优完毕,{'达到' if self.reached_goal(baseline, best_result, symbol) else '未达到'} 预期目标" + ) ++ ++ # 配置最优参数 ++ logging.info(f"配置最佳性能参数:") ++ self.apply_params(best_recommend_params) ++ if self.need_restart_application: ++ restart_success = self.restart_application() ++ if restart_success: ++ logging.info(f"应用重启完成") ++ else: ++ logging.err(f"应用重启失败") ++ diff --git a/copilot-tune/src/performance_optimizer/param_recommender.py b/copilot-tune/src/performance_optimizer/param_recommender.py new file mode 100644 -index 0000000..bbc4556 +index 0000000..e5d909d --- /dev/null +++ b/copilot-tune/src/performance_optimizer/param_recommender.py -@@ -0,0 +1,177 @@ +@@ -0,0 +1,220 @@ +import logging + +from src.performance_analyzer.performance_analyzer import PerformanceAnalyzer @@ -14282,17 +18388,12 @@ index 0000000..bbc4556 +from src.utils.json_repair import json_repair +from src.utils.llm import get_llm_response +from src.utils.metrics import PerformanceMetric -+from src.utils.shell_execute import SshClient -+from src.utils.thread_pool import thread_pool_manager -+ -+# 配置日志 -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) ++from src.utils.shell_execute import SshClient ++from src.utils.thread_pool import thread_pool_manager + ++from src.utils.prompt_instance import prompt_manager + +class ParamRecommender: -+ + def __init__( + self, + service_name: str, @@ -14300,10 +18401,10 @@ index 0000000..bbc4556 + performance_metric: PerformanceMetric, + static_profile: str, + performance_analysis_report: str, ++ all_params, ++ params_set, + chunk_size=20, + ssh_client=None, -+ tune_system_param: bool = False, -+ tune_app_param: bool = True + ): + # 待调优app名称 + self.service_name = service_name @@ -14314,39 +18415,87 @@ index 0000000..bbc4556 + # 静态指标 + self.static_profile = "\n".join(f"{k}: {v}" for k, v in static_profile.items()) + # 可调参数知识库,用于给大模型描述应用参数背景知识 -+ self.param_knowledge = ParamKnowledge( -+ ssh_client=ssh_client, -+ tune_system_param=tune_system_param, -+ tune_app_param=tune_app_param -+ ) -+ self.all_params = self.param_knowledge.get_params(service_name) + self.ssh_client = ssh_client -+ self.params_set = self.param_knowledge.describe_param_background_knob( -+ service_name, self.all_params -+ ) ++ self.all_params = all_params ++ self.params_set = params_set + self.chunk_size = chunk_size + self.performance_analysis_report = performance_analysis_report + ++ def _get_histort(self, history_result, cur_params_set): ++ target_keys = {s.split(':', 1)[0] for s in cur_params_set} ++ ++ history_entries = [] ++ ++ # 1. 上一轮 ++ if "上一轮调优结果" in history_result: ++ entry = history_result["上一轮调优结果"] ++ if isinstance(entry, dict) and "参数推荐" in entry: ++ history_entries.append(( ++ f"上一轮性能: {entry.get('上一轮性能', 'N/A')}", ++ entry["参数推荐"] ++ )) ++ ++ # 2. 历史最佳 ++ if "历史最佳结果" in history_result: ++ entry = history_result["历史最佳结果"] ++ if isinstance(entry, dict) and "参数推荐" in entry: ++ history_entries.append(( ++ f"历史最佳: {entry.get('最佳性能', 'N/A')}", ++ entry["参数推荐"] ++ )) ++ ++ # 3. 历史最差 ++ if "历史最差结果" in history_result: ++ entry = history_result["历史最差结果"] ++ if isinstance(entry, dict) and "参数推荐" in entry: ++ history_entries.append(( ++ f"历史最差: {entry.get('最差性能', 'N/A')}", ++ entry["参数推荐"] ++ )) ++ ++ # 过滤参数 ++ filtered_history = [ ++ ( ++ improve_text, ++ {k: v for k, v in recommend_params.items() if k in target_keys} ++ ) ++ for improve_text, recommend_params in history_entries ++ ] ++ return filtered_history ++ + def _process_chunk(self, history_result, cur_params_set, is_positive): -+ recommend_prompt = f""" -+ # CONTEXT # -+ 本次性能优化的目标为: -+ 性能指标为{self.performance_metric.name}, 该指标的含义为:{self.performance_metric.value},目标是提升{self.slo_goal:.2%} -+ 性能分析报告: -+ {self.performance_analysis_report} -+ 你可以分析的参数有: -+ {",".join(cur_params_set)} -+ # OBJECTIVE # -+ 你是一个专业的系统运维专家,当前性能指标未达到预期,请你基于以上性能分析报告分析有哪些调优思路。 -+ # Tone # -+ 你应该尽可能秉承严肃、认真、严谨的态度 -+ # AUDIENCE # -+ 你的答案将会是其他系统运维专家的重要参考意见,请认真思考后给出你的答案。 -+ """ -+ optimized_idea = get_llm_response(recommend_prompt) -+ recommended_params = self.recommend( -+ history_result, optimized_idea, cur_params_set, is_positive -+ ) ++ history_result = self._get_histort(history_result, cur_params_set) ++ ++ params_set_str = ",".join(cur_params_set) ++ allowed_set = set([ ++ "self.service_name", ++ "self.performance_metric.name", ++ "self.performance_metric.value", ++ "self.slo_goal", ++ "self.static_profile", ++ "history_result", ++ "self.performance_analysis_report", ++ "params_set_str", ++ ]) ++ prompt_mode = prompt_manager.get_mode(self.service_name) ++ if prompt_mode == "fast": ++ recommend_prompt_format = prompt_manager.get(self.service_name, prompt_mode, 'recommender')['value'] ++ recommend_prompt, extras = prompt_manager.render_by_parse(recommend_prompt_format, allowed_set) ++ if len(extras) != 0: ++ logging.warn(f"param not in custom offered param {extras}") ++ recommended_params = get_llm_response(recommend_prompt) ++ elif prompt_mode == "normal": ++ idea_prompt_format = prompt_manager.get(self.service_name, prompt_mode, 'idea')['value'] ++ idea_prompt, extras = prompt_manager.render_by_parse(idea_prompt_format, allowed_set) ++ optimization_idea = get_llm_response(idea_prompt) ++ allowed_set.add("optimization_idea") ++ recommend_prompt_format = prompt_manager.get(self.service_name, prompt_mode, ++ 'recommender_positive' if is_positive else 'recommender_negative')['value'] ++ recommend_prompt, extras = prompt_manager.render_by_parse(recommend_prompt_format, allowed_set) ++ recommended_params = get_llm_response(recommend_prompt) ++ else: ++ # todo for slow prompt ++ recommended_params = get_llm_response(recommend_prompt) + + recommended_params_set = json_repair(recommended_params) + @@ -14383,7 +18532,7 @@ index 0000000..bbc4556 + params_set_str = "\n".join(cur_params_set) + if is_positive: + prompt = f""" -+ 你是专业的系统运维专家。当前性能指标未达预期,但上一轮调优为正向结果(性能提升或无退化)。 ++ [{self.service_name}类] 你是专业的系统运维专家。当前性能指标未达预期,但上一轮调优为正向结果(性能提升或无退化)。 + 请在“心中完成推理”,只输出最终 JSON;除 JSON 以外不要输出任何文字、代码块或注释。 + + 目标:基于以下信息,在保持上轮有效方向的前提下,总结参数调整经验,进一步微调参数(在安全边界内适度加大力度),仅给出需要变更的参数与推荐新值。 @@ -14404,7 +18553,7 @@ index 0000000..bbc4556 + 1) 仅输出与当前配置相比“需要变化”的参数;不相关或无收益的参数不要输出。 + 2) 优先沿“上轮有效”的方向小步前进:连续型参数按原步长的 100%~150% 微增(通常为 +10%~+30%),离散/枚举取更激进且仍在安全范围的相邻档位;避免一次性过大变更(单参数变更幅度不超过 2 倍或 ±30%,取更严格者)。 + 3) 不要动已证明对性能“无影响”的参数;避免同时调整明显互斥的参数。 -+ 4) 必须满足依赖/互斥/上限下限/类型与单位要求;数值默认单位为“字节”。若数值后带单位,请以字符串表示(如 "512MB")。 ++ 4) 必须满足依赖/互斥/上限下限/类型与单位要求。 + 5) 每个参数的推荐值必须可被系统实际接受并确保应用可启动。 + 6) 若无合适变更,输出空json对象。 + @@ -14415,7 +18564,7 @@ index 0000000..bbc4556 + + else: + prompt = f""" -+ 你是专业的系统运维专家。当前性能指标未达预期,且上一轮调优为负向结果(性能下降/不稳定/报错等)。 ++ [{self.service_name}类] 你是专业的系统运维专家。当前性能指标未达预期,且上一轮调优为负向结果(性能下降/不稳定/报错等)。 + 请在“心中完成推理”,只输出最终 JSON;除 JSON 以外不要输出任何文字、代码块或注释。 + + 目标:基于以下信息,总结历史调优经验中的baseline、最佳调优结果、最差调优结果以及上一轮调优结果以及参数取值,反向微调上轮可能导致退化的参数,并选择更保守且安全的值;仅给出需要变更的参数与推荐新值。 @@ -14436,7 +18585,7 @@ index 0000000..bbc4556 + 1) 仅输出与当前配置相比“需要变化”的参数;不相关或无收益的参数不要输出。 + 2) 对上轮参与变更且疑似致退化的参数:沿“相反方向”小步调整(幅度为上轮步长的 30%~50%,通常为 -10%~-20%);必要时关闭可选的高开销特性。 + 3) 避免一次调整过多参数;不要同时调整互斥参数;优先选择风险更低的修正方案。 -+ 4) 必须满足依赖/互斥/上限下限/类型与单位要求;数值默认单位为“字节”。若数值后带单位,请以字符串表示(如 "1GB")。 ++ 4) 必须满足依赖/互斥/上限下限/类型与单位要求。 + 5) 每个参数的推荐值必须可被系统实际接受并确保应用可启动。 + 6) 若无合适变更,输出空json对象。 + @@ -14445,15 +18594,15 @@ index 0000000..bbc4556 + - 不要输出任何多余文字、说明、示例、代码围栏或注释。 + """ + -+ response = get_llm_response(prompt) ++ response = get_llm_response(prompt, max_tokens=1024) + return response + diff --git a/copilot-tune/src/performance_optimizer/set_knob_cmd.jsonl b/copilot-tune/src/performance_optimizer/set_knob_cmd.jsonl new file mode 100644 -index 0000000..5f5cd50 +index 0000000..12fca78 --- /dev/null +++ b/copilot-tune/src/performance_optimizer/set_knob_cmd.jsonl -@@ -0,0 +1,64 @@ +@@ -0,0 +1,41 @@ +{"kernel.sched_child_runs_first":"sysctl -w kernel.sched_child_runs_first=0"} +{"kernel.sched_latency_ns":"sysctl -w kernel.sched_latency_ns=24000000"} +{"kernel.sched_tunable_scaling":"sysctl -w kernel.sched_tunable_scaling=1"} @@ -14495,29 +18644,6 @@ index 0000000..5f5cd50 +{"net.ipv4.tcp_tw_reuse":"sysctl -w net.ipv4.tcp_tw_reuse=2"} +{"net.ipv4.tcp_max_syn_backlog":"sysctl -w net.ipv4.tcp_max_syn_backlog=256"} +{"net.ipv4.tcp_synack_retries":"sysctl -w net.ipv4.tcp_synack_retries=5"} -+{"mysql.innodb_io_capacity":"sed -i 's/^innodb_io_capacity.*$/innodb_io_capacity=996/g' /etc/my.cnf"} -+{"mysql.innodb_thread_concurrency":"sed -i 's/^innodb_thread_concurrency.*$/innodb_thread_concurrency=238/g' /etc/my.cnf"} -+{"mysql.innodb_lru_scan_depth":"sed -i 's/^innodb_lru_scan_depth.*$/innodb_lru_scan_depth=285/g' /etc/my.cnf"} -+{"mysql.innodb_adaptive_hash_index":"sed -i 's/^innodb_adaptive_hash_index.*$/innodb_adaptive_hash_index=on/g' /etc/my.cnf"} -+{"mysql.innodb_max_dirty_pages_pct":"sed -i 's/^innodb_max_dirty_pages_pct.*$/innodb_max_dirty_pages_pct=90/g' /etc/my.cnf"} -+{"mysql.innodb_buffer_pool_instances":"sed -i 's/^innodb_buffer_pool_instances.*$/innodb_buffer_pool_instances=64/g' /etc/my.cnf"} -+{"mysql.innodb_flush_log_at_trx_commit":"sed -i 's/^innodb_flush_log_at_trx_commit.*$/innodb_flush_log_at_trx_commit=0/g' /etc/my.cnf"} -+{"mysql.innodb_adaptive_max_sleep_delay":"sed -i 's/^innodb_adaptive_max_sleep_delay.*$/innodb_adaptive_max_sleep_delay=150000/g' /etc/my.cnf"} -+{"mysql.innodb_spin_wait_delay":"sed -i 's/^innodb_spin_wait_delay.*$/innodb_spin_wait_delay=7/g' /etc/my.cnf"} -+{"mysql.innodb_log_buffer_size":"sed -i 's/^innodb_log_buffer_size.*$/innodb_log_buffer_size=449536/g' /etc/my.cnf"} -+{"mysql.thread_cache_size":"sed -i 's/^thread_cache_size.*$/thread_cache_size=9/g' /etc/my.cnf"} -+{"mysql.innodb_write_io_threads":"sed -i 's/^innodb_write_io_threads.*$/innodb_write_io_threads=24/g' /etc/my.cnf"} -+{"mysql.innodb_change_buffering":"sed -i 's/^innodb_change_buffering.*$/innodb_change_buffering=all/g' /etc/my.cnf"} -+{"mysql.sync_binlog":"sed -i 's/^sync_binlog.*$/sync_binlog=0/g' /etc/my.cnf"} -+{"mysql.innodb_read_io_threads":"sed -i 's/^innodb_read_io_threads.*$/innodb_read_io_threads=16/g' /etc/my.cnf"} -+{"mysql.innodb_max_dirty_pages_pct_lwm":"sed -i 's/^innodb_max_dirty_pages_pct_lwm.*$/innodb_max_dirty_pages_pct_lwm=10/g' /etc/my.cnf"} -+{"mysql.innodb_log_files_in_group":"sed -i 's/^innodb_log_files_in_group.*$/innodb_log_files_in_group=2/g' /etc/my.cnf"} -+{"mysql.innodb_log_file_size":"sed -i 's/^innodb_log_file_size.*$/innodb_log_file_size=50331648/g' /etc/my.cnf"} -+{"mysql.tmp_table_size":"sed -i 's/^tmp_table_size.*$/tmp_table_size=16777216/g' /etc/my.cnf"} -+{"mysql.innodb_page_cleaners":"sed -i 's/^innodb_page_cleaners.*$/innodb_page_cleaners=1/g' /etc/my.cnf"} -+{"mysql.innodb_adaptive_flushing_lwm":"sed -i 's/^innodb_adaptive_flushing_lwm.*$/innodb_adaptive_flushing_lwm=10/g' /etc/my.cnf"} -+{"mysql.innodb_sync_spin_loops":"sed -i 's/^innodb_sync_spin_loops.*$/innodb_sync_spin_loops=427/g' /etc/my.cnf"} -+{"mysql.innodb_purge_threads":"sed -i 's/^innodb_purge_threads.*$/innodb_purge_threads=32/g' /etc/my.cnf"} \ No newline at end of file diff --git a/copilot-tune/src/performance_optimizer/strategy_optimizer.py b/copilot-tune/src/performance_optimizer/strategy_optimizer.py new file mode 100644 @@ -14751,10 +18877,10 @@ new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/performance_test/pressure_test.py b/copilot-tune/src/performance_test/pressure_test.py new file mode 100644 -index 0000000..d30d898 +index 0000000..8478f1e --- /dev/null +++ b/copilot-tune/src/performance_test/pressure_test.py -@@ -0,0 +1,60 @@ +@@ -0,0 +1,58 @@ +import time +import threading + @@ -14804,9 +18930,7 @@ index 0000000..d30d898 + global _pressure_test_running + try: + _pressure_test_running.set() -+ benchmark_result = self.app_interface.benchmark() -+ _pressure_test_result.status_code = 0 -+ _pressure_test_result.output = benchmark_result ++ _pressure_test_result = self.app_interface.benchmark() + except Exception as e: + _pressure_test_result.status_code = -1 + _pressure_test_result.err_msg = ( @@ -14817,10 +18941,10 @@ index 0000000..d30d898 + _pressure_test_running.clear() diff --git a/copilot-tune/src/start_mcpserver.py b/copilot-tune/src/start_mcpserver.py new file mode 100644 -index 0000000..a12ff57 +index 0000000..a5ee921 --- /dev/null +++ b/copilot-tune/src/start_mcpserver.py -@@ -0,0 +1,228 @@ +@@ -0,0 +1,247 @@ +from mcp.server import FastMCP +import logging +from typing import Dict, Any @@ -14838,14 +18962,12 @@ index 0000000..a12ff57 +) +from src.performance_optimizer.param_recommender import ParamRecommender +from src.performance_optimizer.strategy_optimizer import StrategyOptimizer ++from src.performance_optimizer.param_knowledge import ParamKnowledge +from src.utils.config.app_config import AppInterface +from src.utils.shell_execute import SshClient +from src.start_tune import run_param_optimization, run_strategy_optimization + +# ================= 全局配置与缓存 =================== -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) +cache: Dict[str, Dict[str, Any]] = {} + +# 创建MCP Server @@ -14858,6 +18980,9 @@ index 0000000..a12ff57 +app_name = config["servers"][0]["app"] +max_retries = config["servers"][0]["max_retries"] +delay = config["servers"][0]["delay"] ++slo_goal = config["feature"][0]["slo_goal"] ++tune_system_param = config["feature"][0]["tune_system_param"] ++tune_app_param = config["feature"][0]["tune_app_param"] + + +# ================= Collector 接口 =================== @@ -14974,17 +19099,35 @@ index 0000000..a12ff57 + delay=delay, + ) + ++ param_knowledge = ParamKnowledge( ++ ssh_client=ssh_client, ++ tune_system_param=tune_system_param, ++ tune_app_param=tune_app_param ++ ) ++ all_params = param_knowledge.get_params(app_name) ++ params_set, _ = param_knowledge.describe_param_background_knob( ++ app_name, all_params ++ ) ++ + param_recommender = ParamRecommender( + service_name=app_name, -+ slo_goal=0.1, ++ slo_goal=slo_goal, + performance_metric=AppInterface(ssh_client) + .get(app_name) + .performance_metric, + static_profile=cache[host_ip]["static_profile"], + performance_analysis_report=cache[host_ip]["report"], + ssh_client=ssh_client, ++ all_params=all_params, ++ params_set=params_set, + ) -+ param_opt_result = param_recommender.run(history_result=None) ++ ++ history_result = { ++ "历史最佳结果": {}, ++ "历史最差结果": {}, ++ "上一轮调优结果": {} ++ } ++ param_opt_result = param_recommender.run(history_result) + + # --- 策略优化 --- + strategy_opt = StrategyOptimizer( @@ -15036,7 +19179,7 @@ index 0000000..a12ff57 + server_cfg["app"], report, static_profile_info, ssh_client, + feature_cfg["need_restart_application"], feature_cfg["pressure_test_mode"], + feature_cfg["tune_system_param"], feature_cfg["tune_app_param"], feature_cfg["need_recover_cluster"], -+ feature_cfg["benchmark_timeout"] ++ feature_cfg["benchmark_timeout"], feature_cfg["max_iterations"], feature_cfg["slo_goal"] + ) + if feature_cfg["strategy_optimization"]: + run_strategy_optimization(ssh_client, server_cfg["app"], bottleneck, server_cfg, report) @@ -15051,10 +19194,10 @@ index 0000000..a12ff57 + main() diff --git a/copilot-tune/src/start_tune.py b/copilot-tune/src/start_tune.py new file mode 100644 -index 0000000..623cbf7 +index 0000000..8a67814 --- /dev/null +++ b/copilot-tune/src/start_tune.py -@@ -0,0 +1,167 @@ +@@ -0,0 +1,157 @@ +import logging + +from src.performance_collector.micro_dep_collector import MicroDepCollector, COLLECTMODE @@ -15070,15 +19213,6 @@ index 0000000..623cbf7 +from src.utils.shell_execute import SshClient + + -+def setup_logging(): -+ """配置日志格式与级别""" -+ logging.basicConfig( -+ level=logging.INFO, -+ format="%(asctime)s - %(levelname)s - %(message)s", -+ datefmt="%Y-%m-%d %H:%M:%S", -+ ) -+ -+ +def create_ssh_client(server_cfg): + """根据配置创建 SSH 客户端""" + return SshClient( @@ -15152,7 +19286,7 @@ index 0000000..623cbf7 + + +def run_param_optimization(app, report, static_profile_info, ssh_client, need_restart, pressure_mode, tune_system_param, -+ tune_app_param, need_recover_cluster, benchmark_timeout): ++ tune_app_param, need_recover_cluster, benchmark_timeout, max_iterations, slo_goal): + """执行参数优化""" + + def slo_calc_callback(baseline, benchmark_result, symbol): @@ -15162,12 +19296,12 @@ index 0000000..623cbf7 + + optimizer = ParamOptimizer( + service_name=app, -+ slo_goal=0.1, ++ slo_goal=slo_goal, + analysis_report=report, + static_profile=static_profile_info, + ssh_client=ssh_client, + slo_calc_callback=slo_calc_callback, -+ max_iterations=20, ++ max_iterations=max_iterations, + need_restart_application=need_restart, + pressure_test_mode=pressure_mode, + tune_system_param=tune_system_param, @@ -15190,11 +19324,10 @@ index 0000000..623cbf7 + recommendations = strategy_optimizer.get_recommendations_json( + bottleneck, top_k=1, business_context=server_cfg["business_context"] + ) -+ logging.info("推荐策略:", recommendations) ++ logging.info(f"推荐策略: \n{recommendations}") + + +def main(): -+ setup_logging() + server_cfg = config["servers"][0] + feature_cfg = config["feature"][0] + @@ -15208,13 +19341,13 @@ index 0000000..623cbf7 + feature_cfg["microDep_collector"]) + + report, bottleneck = analyze_performance(metrics_data, server_cfg["app"]) -+ logging.info(f">>> PerformanceAnalyzer运行结果:{report} {bottleneck}") ++ logging.info(f">>> PerformanceAnalyzer运行结果:\n{report}\n分析结论:\n{bottleneck}") + + run_param_optimization( + server_cfg["app"], report, static_profile_info, ssh_client, + feature_cfg["need_restart_application"], feature_cfg["pressure_test_mode"], + feature_cfg["tune_system_param"], feature_cfg["tune_app_param"], feature_cfg["need_recover_cluster"], -+ feature_cfg["benchmark_timeout"] ++ feature_cfg["benchmark_timeout"], feature_cfg["max_iterations"], feature_cfg["slo_goal"] + ) + if feature_cfg["strategy_optimization"]: + run_strategy_optimization(ssh_client, server_cfg["app"], bottleneck, server_cfg, report) @@ -15224,10 +19357,10 @@ index 0000000..623cbf7 + main() diff --git a/copilot-tune/src/start_workflow.py b/copilot-tune/src/start_workflow.py new file mode 100644 -index 0000000..f53f95b +index 0000000..3598253 --- /dev/null +++ b/copilot-tune/src/start_workflow.py -@@ -0,0 +1,193 @@ +@@ -0,0 +1,206 @@ +import logging +from typing import Dict, Any + @@ -15247,7 +19380,7 @@ index 0000000..f53f95b +from src.performance_optimizer.strategy_optimizer import StrategyOptimizer +from src.utils.config.app_config import AppInterface +from src.utils.shell_execute import SshClient -+from src.start_tune import main as start_tune ++from src.start_tune import run_param_optimization, run_strategy_optimization + +# ================= FastAPI 初始化 =================== +app = FastAPI( @@ -15257,9 +19390,6 @@ index 0000000..f53f95b +) + +# ================= 全局配置与缓存 =================== -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) +cache: Dict[str, Dict[str, Any]] = {} + +host_ip = config["servers"][0]["ip"] @@ -15269,6 +19399,7 @@ index 0000000..f53f95b +app_name = config["servers"][0]["app"] +max_retries = config["servers"][0]["max_retries"] +delay = config["servers"][0]["delay"] ++slo_goal = config["feature"][0]["slo_goal"] + + +# ================= Collector 接口 =================== @@ -15368,7 +19499,7 @@ index 0000000..f53f95b + + param_recommender = ParamRecommender( + service_name=app_name, -+ slo_goal=0.1, ++ slo_goal=slo_goal, + performance_metric=AppInterface(ssh_client) + .get(app_name) + .performance_metric, @@ -15404,12 +19535,27 @@ index 0000000..f53f95b +# ================= tune(开始调优)接口 =================== +@app.get("/start_tune") +def tune(): -+ """ -+ 此工具用于开始调优,只有用户明确需要开始调优才调用; -+ 此工具耗时预计1小时,需要提醒用户注意等待执行结束; -+ 结果在日志中查看 ,journalctl -xe -u tune-mcpserver --all -f -+ """ -+ start_tune() ++ feature_cfg = config["feature"][0] ++ report = cache[host_ip]["report"] ++ bottleneck = cache[host_ip]["bottleneck"] ++ server_cfg = config["servers"][0] ++ static_profile_info = cache[host_ip]["static_profile"] ++ ssh_client = SshClient( ++ host_ip=host_ip, ++ host_port=host_port, ++ host_user=host_user, ++ host_password=host_password, ++ max_retries=max_retries, ++ delay=delay, ++ ) ++ run_param_optimization( ++ server_cfg["app"], report, static_profile_info, ssh_client, ++ feature_cfg["need_restart_application"], feature_cfg["pressure_test_mode"], ++ feature_cfg["tune_system_param"], feature_cfg["tune_app_param"], feature_cfg["need_recover_cluster"], ++ feature_cfg["benchmark_timeout"] ++ ) ++ if feature_cfg["strategy_optimization"]: ++ run_strategy_optimization(ssh_client, server_cfg["app"], bottleneck, server_cfg, report) + return "调优执行完成" + + @@ -15423,10 +19569,10 @@ index 0000000..f53f95b + main() diff --git a/copilot-tune/src/tests/manager/task_manager.py b/copilot-tune/src/tests/manager/task_manager.py new file mode 100644 -index 0000000..679680b +index 0000000..d2ae978 --- /dev/null +++ b/copilot-tune/src/tests/manager/task_manager.py -@@ -0,0 +1,47 @@ +@@ -0,0 +1,43 @@ +import logging +import pyfiglet +from tabulate import tabulate @@ -15438,10 +19584,6 @@ index 0000000..679680b +from src.performance_collector.application import pgsql_collector +from src.performance_analyzer.application.pgsql_analyzer import PgsqlAnalyzer + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ +host_ip = config["servers"][0]["ip"] +host_port = config["servers"][0]["port"] +host_user = config["servers"][0]["host_user"] @@ -16069,37 +20211,27 @@ new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/utils/collector/collector_trigger.py b/copilot-tune/src/utils/collector/collector_trigger.py new file mode 100644 -index 0000000..0cd4997 +index 0000000..4c26dc3 --- /dev/null +++ b/copilot-tune/src/utils/collector/collector_trigger.py -@@ -0,0 +1,202 @@ +@@ -0,0 +1,221 @@ +import logging ++import subprocess +import threading +import time +from enum import Enum, auto + +import paramiko + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ +# FIFO 文件路径 +FIFO_PATH = "/tmp/euler-copilot-fifo" +MAX_WAIT_TIMEOUT = 300 + -+ +class TriggerStatus(Enum): + WAITING = auto() + TRIGGERED = auto() + CLOSE = auto() + -+ -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ -+ +class TriggerEventListener: + """ + 单例:在后台线程里通过 SSH 轮询远程文件内容, @@ -16203,8 +20335,10 @@ index 0000000..0cd4997 + self._set_status(TriggerStatus.CLOSE) + break + try: ++ # 尝试读取远程的信号 + val = self._read_remote(ssh).strip() + if val == "1": ++ logging.info("remote trigger signal received, start collecting ...") + self._delete_remote(ssh) + self._set_status(TriggerStatus.TRIGGERED) + break @@ -16213,6 +20347,17 @@ index 0000000..0cd4997 + # 可重连 + ssh = self._reconnect(ssh) + ++ try: ++ # 尝试读取本地的信号 ++ val = self._read_local().strip() ++ if val == "1": ++ logging.info("local trigger signal received, start collecting ...") ++ self._delete_local() ++ self._set_status(TriggerStatus.TRIGGERED) ++ break ++ except Exception as e: ++ logging.error("read error: %s", e) ++ + time.sleep(self.poll_interval) + finally: + if ssh: @@ -16229,31 +20374,47 @@ index 0000000..0cd4997 + timeout=10) + return ssh + -+ def _reconnect(self, old_ssh): ++ def _reconnect(self, old_ssh: paramiko.SSHClient): + try: + old_ssh.close() + except: + pass + return self._connect() + -+ def _read_remote(self, ssh): ++ def _read_remote(self, ssh: paramiko.SSHClient): + cmd = f"cat {self.remote_path}" + _, stdout, _ = ssh.exec_command(cmd, timeout=5) + return stdout.read().decode() + -+ def _delete_remote(self, ssh): -+ """删除 remote_path,失败仅警告""" ++ def _delete_remote(self, ssh: paramiko.SSHClient): ++ """清理 remote_path,失败仅警告""" + cmd = f"rm -f {self.remote_path}" + try: + _, stdout, stderr = ssh.exec_command(cmd, timeout=5) + exit_code = stdout.channel.recv_exit_status() + if exit_code == 0: -+ logging.debug("已删除远程文件 %s", self.remote_path) ++ logging.info("clear remote signal file %s", self.remote_path) + else: -+ logging.debug("删除远程文件失败,exit=%s, err=%s", ++ logging.error("clear remote signal file failed, exit=%s, err=%s", + exit_code, stderr.read().decode()) + except Exception as e: -+ logging.debug("删除远程文件异常: %s", e) ++ logging.error("clear remote signal file exception: %s", e) ++ ++ def _read_local(self) -> str: ++ result = subprocess.run(f"cat {FIFO_PATH}".split(), capture_output=True, text=True) ++ return result.stdout.strip() ++ ++ def _delete_local(self): ++ """清理本地信号文件,失败仅警告""" ++ try: ++ result = subprocess.run(f"rm -f {FIFO_PATH}".split(), capture_output=True, text=True) ++ if result.returncode == 0: ++ logging.info("clear local signal file %s", FIFO_PATH) ++ else: ++ logging.error("clear local signal file failed, exit=%s, err=%s", ++ result.returncode, result.stderr) ++ except Exception as e: ++ logging.error("clear local signal file exception: %s", e) + + +if __name__ == "__main__": @@ -16277,10 +20438,10 @@ index 0000000..0cd4997 + print("继续在本机执行其他命令...") diff --git a/copilot-tune/src/utils/collector/metric_collector.py b/copilot-tune/src/utils/collector/metric_collector.py new file mode 100644 -index 0000000..eb7c661 +index 0000000..946829f --- /dev/null +++ b/copilot-tune/src/utils/collector/metric_collector.py -@@ -0,0 +1,229 @@ +@@ -0,0 +1,224 @@ +import logging +import inspect +import traceback @@ -16310,11 +20471,6 @@ index 0000000..eb7c661 + TRIGGERED = "triggered" + DIRECT = "direct" + -+ -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ +SYNC_DIRECT_TASKS = defaultdict(list) +ASYNC_TASKS = defaultdict(list) +SYNC_TRIGGERED_TASKS = defaultdict(list) @@ -16512,13 +20668,14 @@ index 0000000..eb7c661 + return task_list diff --git a/copilot-tune/src/utils/common.py b/copilot-tune/src/utils/common.py new file mode 100644 -index 0000000..58a07c4 +index 0000000..c469de8 --- /dev/null +++ b/copilot-tune/src/utils/common.py -@@ -0,0 +1,95 @@ +@@ -0,0 +1,142 @@ + -+from typing import Any ++from typing import List, Any, Union +from tabulate import tabulate ++from wcwidth import wcswidth + + +def display_banner(): @@ -16527,7 +20684,7 @@ index 0000000..58a07c4 + banner = pyfiglet.figlet_format("EulerCopilot v1.0", font="slant") + print(banner) + except ImportError: -+ print("EulerCopilot v1.0") ++ print("EulerCopilot v1.0 \n") + + +def truncate_string(s, max_length=30): @@ -16577,6 +20734,53 @@ index 0000000..58a07c4 + result.append(truncated_keys + [truncated_value]) # 将键路径和值组合成一个列表 + return result + ++def cn_tabulate( ++ data: List[List[Any]], ++ headers: Union[List[str], str] = (), ++ tablefmt: str = "grid", ++ **kwargs ++) -> str: ++ if not data: ++ return tabulate([], headers=headers, tablefmt=tablefmt, **kwargs) ++ ++ if headers == "keys" and isinstance(data[0], dict): ++ keys = list(data[0].keys()) ++ data = [[row.get(k, "") for k in keys] for row in data] ++ headers = keys ++ elif isinstance(headers, list) and isinstance(data[0], dict): ++ data = [[row.get(k, "") for k in headers] for row in data] ++ ++ max_cols = max(len(row) for row in data) ++ if headers: ++ max_cols = max(max_cols, len(headers)) ++ ++ col_widths = [] ++ for col in range(max_cols): ++ max_width = 0 ++ if headers and col < len(headers): ++ max_width = wcswidth(str(headers[col])) ++ for row in data: ++ if col < len(row): ++ max_width = max(max_width, wcswidth(str(row[col]))) ++ col_widths.append(max_width) ++ ++ def pad_cell(s, width): ++ s = str(s) ++ pad_len = width - wcswidth(s) ++ return s + " " * pad_len ++ ++ padded_data = [ ++ [pad_cell(row[col], col_widths[col]) if col < len(row) else " " * col_widths[col] ++ for col in range(max_cols)] ++ for row in data ++ ] ++ padded_headers = ( ++ [pad_cell(headers[col], col_widths[col]) if col < len(headers) else " " * col_widths[col] ++ for col in range(max_cols)] ++ if headers else () ++ ) ++ ++ return tabulate(padded_data, headers=padded_headers, tablefmt=tablefmt, **kwargs) + +def display_metrics( + metric_data: dict, @@ -16587,7 +20791,7 @@ index 0000000..58a07c4 + if not isinstance(metric_data, dict): + raise TypeError(f"display metric_data only support dict data now!") + -+ table_str = tabulate( ++ table_str = cn_tabulate( + preview_data(metric_data), + headers=headers, + tablefmt="grid", @@ -16595,7 +20799,6 @@ index 0000000..58a07c4 + display_content = "\n".join([title, table_str]) + print(display_content) + -+ +class ExecuteResult: + def __init__(self, status_code: int = -1, output: Any = None, err_msg: str = ""): + self.status_code = status_code @@ -16616,15 +20819,16 @@ new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/utils/config/app_config.py b/copilot-tune/src/utils/config/app_config.py new file mode 100644 -index 0000000..f1fa6c2 +index 0000000..49ab404 --- /dev/null +++ b/copilot-tune/src/utils/config/app_config.py -@@ -0,0 +1,305 @@ +@@ -0,0 +1,307 @@ +import os +import re +from enum import Enum +from string import Template +from dataclasses import dataclass, asdict, field ++from typing import Dict + +from src.utils.constant import SCRIPTS_PATH +from src.utils.shell_execute import SshClient @@ -16762,6 +20966,7 @@ index 0000000..f1fa6c2 + mode = ExecuteMode.REMOTE if mode_str is None else ExecuteMode(mode_str) + return self.mode_map[mode], remaining_string + else: ++ # 默认使用remote执行模式 + return self.mode_map[ExecuteMode.REMOTE], cmd + + def get_param(self, param_name): @@ -16878,7 +21083,7 @@ index 0000000..f1fa6c2 + # 防止重复初始化 + if not getattr(self, "_initialized", False): + self._config = env_config.get("app_config") -+ self._instances = {} ++ self._instances : Dict[str, AppTemplate] = {} + self.ssh_client = ssh_client # 保存 ssh_client + self._initialize_instances() + self.__class__._initialized = True @@ -16897,14 +21102,14 @@ index 0000000..f1fa6c2 + def __getattr__(self, item): + return self.get(item) + -+ def get(self, item): ++ def get(self, item) -> AppTemplate: + if item in self._instances: + return self._instances[item] + raise AttributeError(f"'AppInterface' object has no attribute '{item}'") + + +if __name__ == "__main__": -+ class SshClient: ++ class SimpleSshClient: + def __init__(self): + pass + @@ -16912,7 +21117,7 @@ index 0000000..f1fa6c2 + print(cmd) + + -+ ssh_client = SshClient() ++ ssh_client = SimpleSshClient() + app_interface = AppInterface(ssh_client) + app = app_interface.mysql + sys = app_interface.system @@ -17038,52 +21243,111 @@ index 0000000..2dc1e7f +SCRIPTS_PATH = "/etc/euler-copilot-tune/scripts" diff --git a/copilot-tune/src/utils/json_repair.py b/copilot-tune/src/utils/json_repair.py new file mode 100644 -index 0000000..19b8575 +index 0000000..e1a0045 --- /dev/null +++ b/copilot-tune/src/utils/json_repair.py -@@ -0,0 +1,15 @@ +@@ -0,0 +1,49 @@ +import json ++import json5 ++import logging +from typing import Dict ++from src.utils.llm import get_llm_response + +# TO implement more gernel repair: todo -+def json_repair( ++def json_pair( + json_str: str +) -> Dict: ++ if not json_str or '{' not in json_str or '}' not in json_str: ++ logging.warning(f"the input is not a json string: {json_str}") ++ return {} ++ + json_start = json_str.find('{') + json_end = json_str.rfind('}') + 1 -+ json_str = json_str[json_start:json_end] -+ try: -+ json_data = json.loads(json_str) -+ except json.decoder.JSONDecodeError as e: -+ raise RuntimeError(f"failed to parse json, raw json_str is {json_str}") -+ return json_data -\ No newline at end of file ++ json_candidate = json_str[json_start:json_end] ++ ++ for parser_name, parser in [("json.load", json.loads), ("json5.loads", json5.loads)]: ++ try: ++ return parser(json_candidate) ++ except Exception as e: ++ logging.warning(f"failed to parse json with {parser_name}\nstring: {json_str}\nreson:{e}") ++ ++ return {} ++ ++def json_repair( ++ json_str: str ++) -> Dict: ++ json_data = json_pair(json_str) ++ if json_data != {}: ++ return json_data ++ prompt = f''' ++请检查并修复以下内容,使其成为合法、纯净的 JSON 对象。 ++ ++要求: ++移除所有非 JSON 内容。 ++修复语法错误,包括括号匹配、引号闭合、逗号使用、字符串和数值格式。 ++删除值为空或缺失的字段。 ++遇到重复键时,保留最先出现的键值对,忽略后续相同键。 ++仅输出一个可被 json.loads 直接解析的 JSON 对象,不要包含任何解释、注释、Markdown 代码块或额外文本。 ++ ++ ++{json_str} ++ ++''' ++ ++ result = get_llm_response(prompt) ++ return json_pair(result) diff --git a/copilot-tune/src/utils/llm.py b/copilot-tune/src/utils/llm.py new file mode 100644 -index 0000000..5691dc7 +index 0000000..e51cdc5 --- /dev/null +++ b/copilot-tune/src/utils/llm.py -@@ -0,0 +1,33 @@ +@@ -0,0 +1,59 @@ +import re +from typing import List +import requests +from src.config import config +from langchain_openai import ChatOpenAI ++import httpx ++from colorama import init, Fore, Style ++import logging + +requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning) +requests.Session.verify = False -+ -+def get_llm_response(prompt: str) -> str: -+ client = ChatOpenAI( ++def get_llm_response(prompt: str, **kwargs) -> str: ++ if 'enable' == config["ssl"]: ++ client = ChatOpenAI( + openai_api_key=config["LLM_KEY"], + openai_api_base=config["LLM_URL"], + model_name=config["LLM_MODEL_NAME"], + tiktoken_model_name="cl100k_base", + max_tokens=config["LLM_MAX_TOKENS"], + streaming=True -+ ) -+ result = client.invoke(input=prompt) -+ return re.sub(r".*?", "", result.content, flags=re.DOTALL) ++ ) ++ elif 'disable' == config["ssl"]: ++ client = ChatOpenAI( ++ openai_api_key=config["LLM_KEY"], ++ openai_api_base=config["LLM_URL"], ++ model_name=config["LLM_MODEL_NAME"], ++ tiktoken_model_name="cl100k_base", ++ max_tokens=config["LLM_MAX_TOKENS"], ++ streaming=True, ++ http_client=httpx.Client(verify=False) ++ ) ++ else: ++ raise ValueError(f"无效的SSL配置: {config['ssl']},必须为 'enable' 或 'disable'") ++ result = client.invoke(input=prompt, **kwargs) ++ logging.debug("%sget_llm_response ask:\n%s\n%s", Fore.YELLOW, prompt, Style.RESET_ALL) ++ ++ match = re.search(r"(.*?)(.*)", result.content, flags=re.DOTALL) ++ if match: ++ thought_process = match.group(1).strip("\n") ++ thought_result = match.group(2).strip("\n") ++ logging.debug("%sget_llm_response think:\n%s\n%s", Fore.GREEN, thought_process, Style.RESET_ALL) ++ logging.debug("%sget_llm_response ans:\n%s\n%s", Fore.GREEN, thought_result, Style.RESET_ALL) ++ return thought_result ++ else: ++ logging.debug("%sget_llm_response ans:\n%s\n%s", Fore.GREEN, result, Style.RESET_ALL) ++ return result.content + + +def get_embedding(text: str) -> List[float]: @@ -17103,10 +21367,10 @@ new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/utils/manager/task_manager.py b/copilot-tune/src/utils/manager/task_manager.py new file mode 100644 -index 0000000..65f3d31 +index 0000000..f538a8c --- /dev/null +++ b/copilot-tune/src/utils/manager/task_manager.py -@@ -0,0 +1,185 @@ +@@ -0,0 +1,180 @@ +import logging + + @@ -17125,11 +21389,6 @@ index 0000000..65f3d31 +MAX_TASK_TIMEOUT = 300 +triggered_event_listener = TriggerEventListener() + -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ -+ +def wait_for_signal(): + # waiting状态会阻塞程序,close状态和triggered状态是立即返回的 + event_status = triggered_event_listener.wait() @@ -17314,15 +21573,387 @@ index 0000000..b99edcc + for metric in PerformanceMetric: + print(metric.value) + print("-" * 50) +diff --git a/copilot-tune/src/utils/prompt_instance.py b/copilot-tune/src/utils/prompt_instance.py +new file mode 100644 +index 0000000..4dd953c +--- /dev/null ++++ b/copilot-tune/src/utils/prompt_instance.py +@@ -0,0 +1,3 @@ ++from src.utils.prompt_manager import StringRepository, StringItem ++from src.utils.config.global_config import DEFAULT_CONFIG_PATH ++prompt_manager = StringRepository(store_path=DEFAULT_CONFIG_PATH + "/strings.yaml", defaults_path=DEFAULT_CONFIG_PATH + "/defaults.yaml") +\ No newline at end of file +diff --git a/copilot-tune/src/utils/prompt_manager.py b/copilot-tune/src/utils/prompt_manager.py +new file mode 100644 +index 0000000..bac9ad0 +--- /dev/null ++++ b/copilot-tune/src/utils/prompt_manager.py +@@ -0,0 +1,356 @@ ++#!/usr/bin/env python3 ++# -*- coding: utf-8 -*- ++ ++""" ++YAML 字符串仓库 + FastAPI 接口(含 reset/type & reset_all)。 ++分类模型:type -> subtype -> name -> {"value": str, "meta": {...}} ++ ++运行依赖: ++ pip install fastapi uvicorn pydantic pyyaml ++ ++启动: ++ python string_repo_yaml.py --store ./strings.yaml --defaults ./defaults.yaml --host 0.0.0.0 --port 8000 ++ ++API 路径前缀:/v1 ++""" ++ ++import argparse ++import copy ++import json ++import os ++import shutil ++import tempfile ++import threading ++from typing import Any, Dict, List, Optional ++import string, inspect ++ ++import yaml ++from fastapi import FastAPI, HTTPException ++from fastapi.middleware.cors import CORSMiddleware ++from pydantic import BaseModel, Field ++import uvicorn ++ ++ ++# ----------------------------- ++# Pydantic 请求/响应模型 ++# ----------------------------- ++class StringItem(BaseModel): ++ type: str = Field(..., description="一级类型,如 mysql/nginx/pgsql") ++ subtype: str = Field(..., description="二级类型,如 fast/slow/normal") ++ name: str = Field(..., description="条目名,如 extractor, analyzer, collector, recommender") ++ value: str = Field(..., description="具体内容(提示词/模板/案例等, 当前只有提示词)") ++ meta: Optional[Dict[str, Any]] = Field(default=None, description="可选元信息,如作者/版本/备注") ++ ++class ResetPayload(BaseModel): ++ type: str = Field(..., description="要恢复的类型,如 mysql/nginx/pgsql") ++ ++# ----------------------------- ++# YAML 工具 ++# ----------------------------- ++def _yaml_load(path: str) -> Dict[str, Any]: ++ if not os.path.exists(path): ++ return {} ++ with open(path, "r", encoding="utf-8") as f: ++ data = yaml.safe_load(f) or {} ++ if not isinstance(data, dict): ++ raise ValueError(f"YAML 顶层应为 mapping:{path}") ++ return data ++ ++def _yaml_dump_atomic(path: str, data: Dict[str, Any]) -> None: ++ d = os.path.dirname(os.path.abspath(path)) or "." ++ fd, tmp = tempfile.mkstemp(prefix=".strings.", suffix=".yaml", dir=d) ++ with os.fdopen(fd, "w", encoding="utf-8") as f: ++ yaml.safe_dump( ++ data, ++ f, ++ allow_unicode=True, ++ sort_keys=True, ++ default_flow_style=False, ++ indent=2, ++ ) ++ shutil.move(tmp, path) # 原子替换 ++ ++# ----------------------------- ++# 仓库类(给后端内部直接用) ++# ----------------------------- ++class StringRepository: ++ """ ++ - defaults: 从 defaults.yaml 读入,视为“出厂配置” ++ - store : 工作库 strings.yaml,初次不存在时会用 defaults 全量初始化 ++ - reset(type) : 将某个 type 的 store 覆盖为 defaults 中该 type ++ - reset_all() : 用 defaults 全量覆盖 store ++ 数据结构:data[type][subtype][name] = {"value": str, "meta": {...}} ++ """ ++ ++ def __init__(self, store_path: str, defaults_path: str, autosave: bool = True): ++ self._store_path = store_path ++ self._defaults_path = defaults_path ++ self._autosave = autosave ++ self._lock = threading.RLock() ++ # 都从默认路径读,涉及到改的时候,就改 string yaml ++ self._defaults: Dict[str, Any] = _yaml_load(self._defaults_path) ++ self._data: Dict[str, Any] = _yaml_load(self._defaults_path) ++ ++ # 首次运行:若 store 为空,则用 defaults 初始化 ++ if (not self._data or len(self._data) == 0) and self._defaults: ++ _yaml_dump_atomic(self._store_path, self._defaults) ++ self._data = _yaml_load(self._store_path) ++ ++ # ----------- 内部工具 ----------- ++ def _save(self): ++ if not self._autosave: ++ return ++ _yaml_dump_atomic(self._store_path, self._data) ++ ++ def _ensure_paths(self, type_: str, subtype: str) -> None: ++ self._data.setdefault(type_, {}).setdefault(subtype, {}) ++ ++ # ----------- 查询枚举 ----------- ++ def list_types(self) -> List[str]: ++ with self._lock: ++ return sorted(self._data.keys()) ++ ++ def list_subtypes(self, type_: str) -> List[str]: ++ with self._lock: ++ if type_ not in self._data: ++ return [] ++ return sorted(self._data[type_].keys()) ++ ++ def list_names(self, type_: str, subtype: str) -> List[str]: ++ with self._lock: ++ if type_ not in self._data or subtype not in self._data[type_]: ++ return [] ++ return sorted(self._data[type_][subtype].keys()) ++ ++ def get(self, type_: str, subtype: str, name: str) -> Optional[Dict[str, Any]]: ++ with self._lock: ++ return ( ++ self._data.get(type_, {}) ++ .get(subtype, {}) ++ .get(name, None) ++ ) ++ ++ def get_mode(self, type_: str) -> str: ++ """ ++ get model, fast slow, normal etc ++ """ ++ with self._lock: ++ if type_ not in self._data: ++ raise ValueError(f"defaults.yaml 中不存在类型:{type_}") ++ mode = self._data.get(type_).get('mode', "slow") ++ if mode not in ["fast", "slow", "normal"]: ++ return "slow" ++ else: ++ return mode ++ ++ # ----------- 增删改 ----------- ++ def create(self, item: StringItem) -> None: ++ with self._lock: ++ self._ensure_paths(item.type, item.subtype) ++ if item.name in self._data[item.type][item.subtype]: ++ raise ValueError("条目已存在(type/subtype/name 重复)") ++ self._data[item.type][item.subtype][item.name] = { ++ "value": item.value, ++ "meta": item.meta, ++ } ++ self._save() ++ ++ def upsert(self, item: StringItem) -> None: ++ with self._lock: ++ self._ensure_paths(item.type, item.subtype) ++ self._data[item.type][item.subtype][item.name] = { ++ "value": item.value, ++ "meta": item.meta, ++ } ++ self._save() ++ ++ def delete(self, type_: str, subtype: str, name: str) -> bool: ++ with self._lock: ++ if ( ++ type_ in self._data ++ and subtype in self._data[type_] ++ and name in self._data[type_][subtype] ++ ): ++ del self._data[type_][subtype][name] ++ # 清理空层级 ++ if not self._data[type_][subtype]: ++ del self._data[type_][subtype] ++ if type_ in self._data and not self._data[type_]: ++ del self._data[type_] ++ self._save() ++ return True ++ return False ++ ++ # ----------- 恢复能力 ----------- ++ def reset_type(self, type_: str) -> None: ++ with self._lock: ++ if type_ not in self._defaults: ++ raise ValueError(f"defaults.yaml 中不存在类型:{type_}") ++ # 用 defaults[type] 覆盖到 store[type] ++ self._data[type_] = copy.deepcopy(self._defaults[type_]) ++ self._save() ++ ++ def reset_all(self) -> None: ++ with self._lock: ++ if not self._defaults: ++ raise ValueError("defaults.yaml 为空,无法执行 reset_all") ++ self._data = copy.deepcopy(self._defaults) ++ self._save() ++ ++ @staticmethod ++ def render_by_parse(tpl: str, allowed: set, default: str = "未知"): ++ """ ++ tpl: 模板字符串,含 {field[:fmt][!conv]} ++ allowed: 允许替换的字段全集(如 {'self.service_name','self.performance_metric.name',...}) ++ default: 不在 allowed 或取值失败时的占位 ++ 返回: (渲染后字符串, 额外占位符列表) ++ """ ++ fmt = string.Formatter() ++ f = inspect.currentframe().f_back ++ # 记录上一个调用栈的所有变量信息 ++ scope = {**f.f_globals, **f.f_locals} ++ ++ def resolve(field: str): ++ parts = field.split('.') ++ cur = scope.get(parts[0], None) ++ for p in parts[1:]: ++ if cur is None: return None ++ cur = (cur.get(p) if isinstance(cur, dict) else getattr(cur, p, None)) ++ return cur ++ ++ out, extras = [], [] ++ for literal, field, format_spec, conversion in fmt.parse(tpl): ++ out.append(literal) ++ if not field: ++ continue ++ # 需要的变量不再提供的可用变量里面,直接改值为 “未知”,同时记录这个额外的 “需求变量” ++ if field not in allowed: ++ out.append(default) ++ extras.append(field) ++ continue ++ val = resolve(field) ++ if val is None: ++ out.append(default) ++ extras.append(field) ++ continue ++ # 格式化/转换 ++ try: ++ val = format(val, format_spec) if format_spec else str(val) ++ except Exception: ++ val = str(val) ++ if conversion == 'r': ++ val = repr(val) ++ elif conversion == 'a': ++ val = ascii(val) ++ out.append(str(val)) ++ # extras 去重保序 ++ seen, uniq = set(), [] ++ for e in extras: ++ if e not in seen: ++ seen.add(e) ++ uniq.append(e) ++ return ''.join(out), uniq ++ ++ ++# ---------------------------------------------------------- ++# FastAPI(对前端/其他服务) ++# ---------------------------------------------------------- ++def build_app(repo: StringRepository) -> FastAPI: ++ app = FastAPI(title="String Repository (YAML)", version="1.0.0") ++ ++ app.add_middleware( ++ CORSMiddleware, ++ allow_origins=["*"], # 生产可收敛到具体前端域名 ++ allow_credentials=True, ++ allow_methods=["*"], ++ allow_headers=["*"], ++ ) ++ ++ prefix = "/v1" ++ ++ @app.get(prefix + "/types", response_model=List[str], summary="列出所有类型") ++ def list_types(): ++ return repo.list_types() ++ ++ @app.get(prefix + "/subtypes/{type}", response_model=List[str], summary="列出某类型的所有子类型") ++ def list_subtypes(type: str): ++ subs = repo.list_subtypes(type) ++ if not subs: ++ raise HTTPException(status_code=404, detail="类型不存在或无子类型") ++ return subs ++ ++ @app.get(prefix + "/names/{type}/{subtype}", response_model=List[str], summary="列出条目名称") ++ def list_names(type: str, subtype: str): ++ names = repo.list_names(type, subtype) ++ if not names: ++ raise HTTPException(status_code=404, detail="类型/子类型不存在或无条目") ++ return names ++ ++ @app.get(prefix + "/strings/{type}/{subtype}/{name}", summary="获取具体条目") ++ def get_string(type: str, subtype: str, name: str): ++ item = repo.get(type, subtype, name) ++ if not item: ++ raise HTTPException(status_code=404, detail="未找到该条目") ++ return {"type": type, "subtype": subtype, "name": name, **item} ++ ++ @app.post(prefix + "/strings", status_code=201, summary="新增(重复会 409)") ++ def create_string(item: StringItem): ++ try: ++ repo.create(item) ++ return {"ok": True} ++ except ValueError as e: ++ raise HTTPException(status_code=409, detail=str(e)) ++ ++ @app.put(prefix + "/strings", summary="新增或更新(幂等 Upsert)") ++ def upsert_string(item: StringItem): ++ repo.upsert(item) ++ return {"ok": True} ++ ++ @app.delete(prefix + "/strings/{type}/{subtype}/{name}", summary="删除一个条目") ++ def delete_string(type: str, subtype: str, name: str): ++ ok = repo.delete(type, subtype, name) ++ if not ok: ++ raise HTTPException(status_code=404, detail="删除失败:条目不存在") ++ return {"ok": True} ++ ++ @app.post(prefix + "/reset/type", summary="恢复指定类型为 defaults.yaml 中的出厂配置") ++ def reset_type(payload: ResetPayload): ++ try: ++ repo.reset_type(payload.type) ++ return {"ok": True, "reset": payload.type} ++ except ValueError as e: ++ raise HTTPException(status_code=404, detail=str(e)) ++ ++ @app.post(prefix + "/reset/all", summary="恢复所有类型为 defaults.yaml 的出厂配置") ++ def reset_all(): ++ try: ++ repo.reset_all() ++ return {"ok": True, "reset": "all"} ++ except ValueError as e: ++ raise HTTPException(status_code=400, detail=str(e)) ++ ++ return app ++ ++ ++# ---------------------------------------------------------- ++# CLI & 启动 ++# ---------------------------------------------------------- ++def parse_args(): ++ p = argparse.ArgumentParser(description="YAML 字符串仓库服务") ++ p.add_argument("--store", type=str, default="./config/strings.yaml", help="工作库 YAML 路径") ++ p.add_argument("--defaults", type=str, default="./config/defaults.yaml", help="出厂默认 YAML 路径") ++ p.add_argument("--host", type=str, default="127.0.0.1") ++ p.add_argument("--port", type=int, default=8000) ++ return p.parse_args() ++ ++ ++def main(): ++ args = parse_args() ++ repo = StringRepository(store_path=args.store, defaults_path=args.defaults, autosave=True) ++ app = build_app(repo) ++ uvicorn.run(app, host=args.host, port=args.port) ++ ++ ++if __name__ == "__main__": ++ print("app start ...") ++ main() diff --git a/copilot-tune/src/utils/rag/__init__.py b/copilot-tune/src/utils/rag/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/copilot-tune/src/utils/rag/knob_rag.py b/copilot-tune/src/utils/rag/knob_rag.py new file mode 100644 -index 0000000..4201a4f +index 0000000..96c3edd --- /dev/null +++ b/copilot-tune/src/utils/rag/knob_rag.py -@@ -0,0 +1,208 @@ +@@ -0,0 +1,205 @@ +import json +import logging +import os @@ -17337,9 +21968,6 @@ index 0000000..4201a4f + +from src.utils.llm import get_llm_response, get_embedding + -+logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -+ -+ +class KnobRag: + def __init__(self, config_path: str, bottle_neck: str, application: str, system_report: str): + self.bottle_neck = bottle_neck @@ -17533,10 +22161,10 @@ index 0000000..4201a4f +# ret=['vm.swappiness', 'vm.min_free_kbytes', 'vm.dirty_expire_centisecs', 'vm.overcommit_ratio', 'vm.dirty_background_ratio', 'vm.dirty_bytes', 'vm.dirty_background_bytes', 'kernel.shmmax', 'kernel.shmall', 'vm.drop_caches'] diff --git a/copilot-tune/src/utils/shell_execute.py b/copilot-tune/src/utils/shell_execute.py new file mode 100644 -index 0000000..0b98763 +index 0000000..19fda54 --- /dev/null +++ b/copilot-tune/src/utils/shell_execute.py -@@ -0,0 +1,179 @@ +@@ -0,0 +1,192 @@ +import inspect +import logging +import shlex @@ -17546,7 +22174,7 @@ index 0000000..0b98763 +from collections import defaultdict +from functools import wraps +from types import ModuleType -+from typing import Callable ++from typing import Callable, Dict, List + +import paramiko + @@ -17555,12 +22183,6 @@ index 0000000..0b98763 +decorated_funcs = defaultdict(list) +cmds_registry = defaultdict(list) + -+# 配置日志 -+logging.basicConfig( -+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s" -+) -+ -+ +def retryable(max_retries: int = 3, delay: int = 1): + def decorator(func): + @wraps(func) @@ -17628,7 +22250,7 @@ index 0000000..0b98763 + return result + + @retryable() -+ def run_local_cmd(self, cmd): ++ def run_local_cmd(self, cmd) -> ExecuteResult: + result = ExecuteResult() + try: + # 使用 shlex.split 将命令字符串分割为参数列表 @@ -17667,12 +22289,12 @@ index 0000000..0b98763 + +def process_decorated_func( + result: ExecuteResult, func: Callable, *args, **kwargs -+): ++) -> ExecuteResult: + try: + processed_result = func(result.output, *args, **kwargs) + result.output = processed_result + except Exception as e: -+ print(traceback.format_exc()) ++ result.status_code = -1 + result.err_msg = str(e) + return result + @@ -17682,46 +22304,65 @@ index 0000000..0b98763 + tag: str = "default_tag", + parallel: bool = False, +): ++ ''' ++ return a func which first runs cmd using ssh client, then run decorated parsing function based on cmd output. ++ ++ Also, record decorated function in global dict, including func info like function itself, tag and parallel values by "func", "tag" and "parallel" keys. ++ ++ Get all registered func infos of certain module by calling get_registered_cmd_funcs(). ++ ''' + def decorator(func): -+ file = inspect.getfile(func) -+ + @wraps(func) -+ def wrapper(ssh_client, *args, **kwargs): ++ def wrapper(ssh_client: SshClient, *args, **kwargs): + result = ssh_client.run_cmd(cmd) + if result.status_code == 0: + return process_decorated_func(result, func) + return result + ++ file = inspect.getfile(func) + decorated_funcs[file].append( + {"func": wrapper, "tag": tag, "parallel": parallel} + ) + return wrapper -+ + return decorator + + +def get_registered_cmd_funcs( + module: ModuleType, parallel: bool = True -+): ++) -> List[Dict]: ++ ''' ++ return func info dicts of certain module, with "func" and "tag" keys, filtered by same parallel attribute; ++ these funcs need SshClient as first argument, in order to run cmd remotely. ++ ''' + if not isinstance(module, ModuleType) or not hasattr(module, "__file__"): + raise RuntimeError( + f"module {module.__name__} has no attr __file__, maybe it is a built-in module" + ) -+ caller_file = module.__file__ -+ -+ registered_funcs = decorated_funcs.get(caller_file, []) + + func_list = [] -+ for func_info in registered_funcs: ++ for func_info in decorated_funcs.get(module.__file__, []): + if func_info["parallel"] == parallel: + func_list.append({"func": func_info["func"], "tag": func_info["tag"]}) + return func_list ++ ++if __name__ == "__main__": ++ ssh_client = SshClient() ++ result = ssh_client.run_local_cmd("ls /root/abc") ++ print(result.status_code) ++ print(result.output) ++ print(result.err_msg) ++ ++ ssh_client = SshClient(host_ip="127.0.0.1", host_password="") ++ result = ssh_client.run_cmd("ls /root/abc") ++ print(result.status_code) ++ print(result.output) ++ print(result.err_msg) diff --git a/copilot-tune/src/utils/thread_pool.py b/copilot-tune/src/utils/thread_pool.py new file mode 100644 -index 0000000..5da042d +index 0000000..ea66805 --- /dev/null +++ b/copilot-tune/src/utils/thread_pool.py -@@ -0,0 +1,236 @@ +@@ -0,0 +1,240 @@ +import uuid +import traceback +import threading @@ -17736,7 +22377,7 @@ index 0000000..5da042d + self, + uuid: str, + func_name: str = "", -+ result: Any = None, ++ result: Any | str | ExecuteResult = None, + status_code: int = 0, + tag: str = "default_tag", + ): @@ -17770,8 +22411,10 @@ index 0000000..5da042d + self.tasks: Dict[str, concurrent.futures.Future] = {} + self.all_results: List[TaskResult] = [] + self.pending: list[tuple[str, Callable, tuple, dict]] = [] -+ self.tag_map: dict = {} ++ self.tag_map: Dict[str, str] = {} ++ '''tag_map saves task_id to tag info''' + self.task_meta: Dict[str, str] = {} ++ '''task_meta saves task_id to func name info''' + + # add a task to be run, every task will be asigned a task id + # user can query whether this task has been done by is_done method @@ -17782,24 +22425,30 @@ index 0000000..5da042d + self.tag_map[task_id] = kwargs.pop("tag", "default_tag") + return task_id + -+ """ -+ batch submit tasks, for example: -+ def hello(): -+ return "hello" -+ -+ def add(x, y): -+ return x + y -+ -+ tasks = [ -+ hello, -+ (add, (1, 2), {}), -+ (add, (3, 4), {"z": 5}) -+ ] -+ """ -+ + def add_batch( + self, tasks: Iterable[Union[Callable, Tuple[Callable, Tuple, Dict]]] + ) -> List[str]: ++ """ ++ batch submit tasks, tasks will be added into pending list. return task uuids. ++ ++ tasks will start running in multi-thread async way after calling run_all_tasks(). ++ ++ wait and get all tasks results by calling get_all_results(). ++ ++ for example: ++ ++ def hello(): ++ return "hello" ++ ++ def add(x, y): ++ return x + y ++ ++ tasks = [ ++ hello, ++ (add, (1, 2), {}), ++ (add, (3, 4), {"z": 5}) ++ ] ++ """ + uuids = [] + for task in tasks: + if callable(task): @@ -17825,6 +22474,7 @@ index 0000000..5da042d + uuids_all.extend(uuids_batch) + return uuids_all + ++ # submit all tasks in pending list and record in tasks map + def run_all_tasks(self) -> None: + for task_id, func, args, kwargs in self.pending: + future = self.executor.submit(func, *args, **kwargs) @@ -17855,9 +22505,7 @@ index 0000000..5da042d + result = future.result() + status_code = 0 + except Exception as e: -+ result = ExecuteResult( -+ status_code=-1, output="", err_msg=traceback.format_exc() -+ ) ++ result = traceback.format_exc() + status_code = -1 + self.all_results.append( + TaskResult( @@ -17865,6 +22513,7 @@ index 0000000..5da042d + ) + ) + ++ # wait until all task done and return results + def get_all_results(self) -> List[TaskResult]: + self.wait_all() + self.tasks.clear() @@ -17930,18 +22579,14 @@ index 0000000..5da042d + result = func(*args, **kwargs) + status_code = 0 + except Exception as e: -+ result = ExecuteResult( -+ status_code=-1, output="", err_msg=traceback.format_exc() -+ ) ++ result = traceback.format_exc() + status_code = -1 + + thread = threading.Thread(target=target) + thread.start() + thread.join(timeout=30) # 设置超时时间为30秒 + if thread.is_alive(): -+ result = ExecuteResult( -+ status_code=-1, output="", err_msg="Task timed out after 30 seconds" -+ ) ++ result = "Task timed out after 30 seconds" + status_code = -1 + return TaskResult(task_id, func_name, result, status_code, tag) + @@ -17959,5 +22604,5 @@ index 0000000..5da042d +thread_pool_manager = ThreadPoolManager(max_workers=8) +serial_task_manager = SerialTaskManager() -- -2.43.0 +2.27.0