
Nginx 访问日志蕴含着丰富的流量信息,通过 SQL 语句进行深度分析可以挖掘出业务洞察。本文介绍如何利用腾讯云 CLS 的 SQL 分析能力,对 Nginx 日志进行多维度查询,实现流量监控、性能优化和安全防护。
在日常运维工作中,Nginx 访问日志往往只被用于事后排查问题。实际上,这些日志记录了每一次 HTTP 请求的完整信息,包括客户端 IP、请求时间、URL 路径、状态码、响应大小和耗时等关键字段。通过对这些数据进行系统性分析,运维和运营团队可以获得关于用户行为、系统性能和潜在风险的深度洞察。
传统的日志分析方式依赖命令行工具如 awk、grep 等进行文本处理,这种方式在面对海量日志时效率较低,且难以进行多维度的关联分析。将 Nginx 日志接入腾讯云 CLS(Cloud Log Service)后,可以通过 SQL 语句直接对日志进行统计分析。CLS 作为一体化可观测 SaaS 服务,支持日志(Log)和指标(Metric)数据的采集、存储、检索分析、加工投递、可视化仪表盘和告警,并已上线 AI 助手(支持自然语言生成检索分析语句)和 MCP Server(让大模型直接查日志)等智能化能力。平台兼容 SQL 92 标准,内置 200+ SQL 函数,具备亿级日志秒级返回的检索性能,大幅降低分析门槛,提升工作效率。
在 CLS 控制台创建日志集和日志主题后,进入"日志采集"页面添加采集配置。Nginx 默认的 combined 日志格式如下:
log_format combined '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time';在 CLS 采集配置中选择"正则模式",系统会自动匹配 Nginx 标准日志格式,将各字段解析为结构化键值对。解析后的字段名与原始 Nginx 变量一一对应:
Nginx 变量 | CLS 字段名 | 说明 |
|---|---|---|
|
| 客户端 IP |
|
| 请求时间 |
|
| 完整请求行 |
|
| HTTP 方法(GET/POST) |
|
| 请求路径 |
|
| HTTP 状态码 |
|
| 响应体大小 |
|
| 来源页面 |
|
| 浏览器标识 |
|
| 请求处理时间(秒) |
此外,CLS 还会自动附加 __TIMESTAMP__(日志写入时间戳)、__SOURCE__(日志来源机器IP)、__FILENAME__(日志文件路径)等内置字段,方便后续按采集维度进行筛选。
采集策略建议选择"增量采集",避免历史文件重复上报。编码模式通常选择 UTF-8。如果需要同时采集多个 Nginx 日志文件(如 access.log 和 error.log),可以在同一采集配置中添加多条文件路径规则。
采集配置完成后,进入日志主题的"索引配置"页面开启索引。CLS 默认会自动识别 JSON 格式的字段并建立键值索引,对于正则模式采集的 Nginx 日志,建议手动配置以下关键字段的键值索引:
记得勾选"开启统计"选项——只有开启了统计的字段才能在 SQL 聚合查询中使用。对于 status、request_time 等需要进行 count_if、avg、approx_percentile 计算的字段,这一步必不可少。
如果不确定哪些字段需要索引,可以先使用 CLS 的 AI 助手——在检索分析页面输入自然语言描述(如"统计每个接口的 P99 响应时间"),AI 助手会自动生成对应的 SQL 语句并提示需要哪些索引字段。
在 CLS 检索分析页面输入以下 SQL 语句,即可按分钟粒度统计请求量趋势:
* | select time_series(__TIMESTAMP__, '1m', '%H:%i:%s', '0') as time_window, count(*) as pv group by time_window order by time_window limit 1440其中 time_series 函数会按指定间隔(此处为 1 分钟)对时间轴进行分组,即使某些分钟没有请求也会以零值填充,确保趋势图连续完整。__TIMESTAMP__ 是 CLS 内置的日志时间字段,limit 1440 表示返回最近一天的分钟级数据。
统计独立访客数(UV)只需将 count(*) 替换为 approx_distinct(remote_addr)——approx_distinct 是 CLS 提供的近似去重函数,在海量数据下仍能快速返回结果:
* | select time_series(__TIMESTAMP__, '5m', '%H:%i:%s', '0') as time_window, approx_distinct(remote_addr) as uv group by time_window order by time_window limit 288如果需要对比今天和昨天的流量变化,可以使用 CLS 的 compare 函数:
* | SELECT time, compare[1] AS today, compare[2] AS yesterday FROM ( SELECT time_series(__TIMESTAMP__, '10m', '%H:%i:%s', '0') AS time, count(*) AS pv, compare(pv, 86400) AS compare FROM log GROUP BY time LIMIT 1000 ) ORDER BY timecompare(pv, 86400) 会将当前值与 86400 秒(即 24 小时)前的值进行对比,方便在仪表盘上绘制双曲线观察同比变化。
统计请求量最高的 URL 路径,使用以下 SQL:
* | select count(*) as request_count, url_extract_path(url) as url_path group by url_path order by request_count desc limit 20这里 url_extract_path(url) 是 CLS 提供的 URL 解析函数,它会剥离查询参数(如 ?id=123&from=home),只保留路径部分。这样可以避免同一个接口因参数不同而被拆分成多条记录,让统计结果更加准确。
分析流量来源渠道时,可以对 http_referer 字段进行域名提取和聚合:
* | select regexp_extract(http_referer, 'https?://([^/]+)', 1) as referer_domain, count(*) as pv where http_referer != '-' group by referer_domain order by pv desc limit 10regexp_extract 是 CLS 支持的 200+ SQL 函数之一,用于从字符串中提取匹配的子串。这条语句可以从完整的 Referer URL 中提取出域名部分,快速识别出搜索引擎、社交媒体等流量来源的占比。
CLS 提供了简洁的条件聚合语法来统计错误率。以下语句可直接计算 HTTP 错误请求的占比:
* | select round(count_if(status >= 400) * 1.0 / count(*), 4) as error_rate, count_if(status >= 400) as error_count, count(*) as total_countcount_if(status >= 400) 是 CLS SQL 中的条件计数函数,只统计满足条件的日志条数。乘以 1.0 是为了将整数转换为浮点数,确保除法运算保留小数精度。round(..., 4) 则保留四位小数。
要进一步定位哪些 URL 的错误最多,可以按 URL 分组:
status >= 400 | select count(*) as error_count, url_extract_path(url) as url_path group by url_path order by error_count desc limit 10注意管道符 | 前面的 status >= 400 是检索条件——这是 CLS 特有的"检索+SQL"组合模式,先过滤出所有 4xx 和 5xx 错误的日志,再对其执行 SQL 聚合分析。这种两段式语法既利用了倒排索引的快速筛选能力,又发挥了 SQL 的统计分析优势。
如果要监控特定类型的错误(如 502 Bad Gateway),可以直接精确匹配:
status:502 | select time_series(__TIMESTAMP__, '5m', '%H:%i:%s', '0') as time_window, count(*) as count group by time_window order by time_window limit 288CLS 的 approx_percentile 函数可以高效计算响应时间的百分位数。以下语句同时输出平均响应时间、P50、P95 和 P99:
* | select avg(request_time) as avg_rt, approx_percentile(request_time, 0.50) as p50, approx_percentile(request_time, 0.95) as p95, approx_percentile(request_time, 0.99) as p99approx_percentile(request_time, 0.99) 表示将所有请求按响应时间排序后,取第 99 百分位的值——即 99% 的请求都在这个时间内完成。相比平均值,P99 更能暴露长尾延迟问题。
按时间趋势观察响应时间变化,可以结合 time_series 函数:
* | select time_series(__TIMESTAMP__, '5m', '%H:%i:%s', '0') as time_window, avg(request_time) as avg_rt, approx_percentile(request_time, 0.99) as p99 group by time_window order by time_window limit 288按接口定位性能瓶颈,使用 group by 配合 url_extract_path:
* | select url_extract_path(url) as api, count(*) as requests, avg(request_time) as avg_rt, approx_percentile(request_time, 0.95) as p95, approx_percentile(request_time, 0.99) as p99 group by api having requests > 100 order by p99 desc limit 20having requests > 100 用于排除请求量过少的接口,避免偶然性数据干扰判断。order by p99 desc 则将 P99 最高的接口排在最前面——这些就是最需要优化的性能瓶颈。
CLS 内置了 IP 地理位置函数,可以直接从 remote_addr 字段解析出访问者的省份信息:
* | select ip_to_province(remote_addr) as province, count(*) as pv where ip_to_country(remote_addr) like 'China%' group by province order by pv desc limit 20ip_to_country 和 ip_to_province 是 CLS 提供的 IP 解析函数,能够将 IP 地址映射到国家和地区。配合 CLS 仪表盘的地图图表类型,可以将查询结果直接可视化为热力地图,直观展示全国各省份的访问分布。这对于评估 CDN 节点覆盖效果非常有价值——如果某个省份访问量很大但 P99 延迟偏高,就说明该区域的 CDN 覆盖可能需要加强。
识别潜在的 DDoS 攻击或恶意爬虫,可以通过统计单 IP 的请求频率来实现:
* | select remote_addr, count(*) as requests, approx_distinct(url) as unique_urls where __TIMESTAMP__ > now() - interval '5' minute group by remote_addr having requests > 500 order by requests desc limit 10这条语句统计最近 5 分钟内请求次数超过 500 次的 IP 地址。now() - interval '5' minute 是 CLS SQL 中的时间表达式,用于限定时间窗口。approx_distinct(url) 则计算每个 IP 访问了多少个不同的 URL——如果一个 IP 在短时间内发起大量请求但只访问少数几个 URL,很可能是定向攻击;如果访问了大量不同 URL,则可能是扫描行为。
检测常见的 Web 攻击模式(如 SQL 注入、路径遍历),可以使用 regexp_match 函数对 URL 进行模式匹配:
* | select count(*) as attack_count, remote_addr where regexp_match(url, '(union.*select|\\.\\./|%2e%2e|<script|eval\\(|exec\\()') group by remote_addr order by attack_count desc limit 10regexp_match 会在 URL 中搜索 SQL 注入关键字(union select)、路径遍历(../)、XSS 脚本标签(<script>)等常见攻击特征。将这类查询保存为定时 SQL 分析并设置告警阈值,就能实现自动化的安全威胁预警。
CLS 的定时 SQL 功能可以将上述查询自动化。以"每分钟提取 PV、错误数和 P99 响应时间"为例,在检索分析页面验证好查询语句后,点击"另存为定时 SQL 分析",设置调度周期为 1 分钟,查询时间窗口为 @m-1m,@m(即最近 1 分钟),系统就会每分钟自动执行并将结果写入指定的指标主题:
* | select count(*) as pv, count_if(status >= 400) as error_count, approx_percentile(request_time, 0.99) as p99_rt, url_extract_path(url) as api group by url_extract_path(url) order by pv desc limit 10提取到指标主题后的数据可以用 PromQL 查询,无缝对接 Grafana 等监控系统。相比直接查询原始日志,指标查询的性能提升可达数十倍,彻底解决了大时间范围仪表盘超时的问题。
定时 SQL 的结果还可以触发 CLS 告警。例如当 P99 响应时间超过 1 秒时自动发送通知:
* | select approx_percentile(request_time, 0.99) as p99 where request_time > 1.0 group by url_extract_path(url) having count(*) > 10将此查询设置为告警条件,当任意接口在 5 分钟内出现超过 10 次慢请求时,系统会通过电话、短信、邮件、企业微信、钉钉、飞书等渠道通知值班人员,并在告警内容中附带受影响的 URL 列表,帮助快速定位问题。
日志分析的最终目的是驱动改进。通过持续的流量监控,可以发现系统容量规划的偏差并及时调整资源配置。性能瓶颈的定位结果为开发和运维团队提供了明确的优化方向。安全威胁的早期识别则有助于在损失扩大之前采取应对措施。
将分析结果固化为仪表盘和告警规则,可以让日志数据的价值持续发挥作用。团队成员无需每次都从头编写查询语句,打开仪表盘就能看到最新的运行状况。这种常态化的监控机制,是保障系统稳定运行的重要基础。
想要为 Nginx 日志搭建 CLS 分析体系,新用户开通即可领取 10U × 3 个月 免费资源包用于体验,首单特惠最低至 0.8 折起;新老用户购买资源包常规档位最低可享 6.3 折优惠。如需了解更多详情或领取优惠,可访问 腾讯云 CLS 产品页 及 特惠活动页。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。