优化分析查询
1)为什么要优化(iGaming上下文)
业务速度:p95 SLA中的GGR/NET报告,提供商/游戏,RG/AML和营销。
成本:扫描字节和藏红花较少→低于$/请求。
可靠性:稳定高峰时段,没有BI"冻结"。
规模:数十个品牌/市场,数十亿行,新鲜分钟。
2)负载配置文件和SLO
描述"前90%"查询:窗口(7/28/90d),过滤器("品牌,国家/地区,provider, psp, status"),join's, JSON属性,top K和percentili。
SLO示例:p95 ≤ 1。2 s用于dashboard,扫描字节≤ 256 MV/查询,freshness ≤ 5 min。
3)计划解剖学: 寻找什么
Predicate/Projection pushdown:过滤器和列列表降到源。
Partition pruning&data skipping:切断多余的部分/文件(min-max/bloom/manifest)。
Vectorized scan/late materialization:通过JOIN/PROJECT延迟的列读取。
Join strategy: Broadcast Hash (BHJ), Sort-Merge (SMJ), Nested Loop (NLJ — избегать).
Spill&shuffle:洗牌体积和海峡到磁盘是SLA的主要敌人。
Adaptive query execution:在rantime中更改策略(切换BHJ↔SMJ、动态联盟)。
计划必须显示:读取多少字节,藏红花在哪里,我们缓存什么。
4)分期、排序、群集桉例
批次:通过"日期"+1-2访问度量(例如"品牌,国家")。
分类/聚类:"ORDER BY/CLUSTER BY/Z-order"("provider, game_id, occurred_at')。
回收和堆积:用于数据跳板的常规横杆;目标文件大小128-1024 MB。
5)JOIN模式
Broadcast Hash Join (BHJ):小维度(≤ 数百MB) →广播到事实。
sql
/ hint if engine supports/
SELECT /+ BROADCAST(dim_provider) /...
Sort-Merge Join (SMJ):大型集、兼容的关键排序/群集桉例→最小的收缩。
预加成/非规范化:将稳定属性从"dim_"推导到事实快照(projection/materialized view)-减去关键路径上的JOIN。
Anti/semijoins:将"NOT IN/EXISTS"重写为显式的semi -/anti-joins计划。
消除基本爆炸:检查测量中的重复密钥,使用surrogate-keys。
6) GROUP BY、聚合和预聚合
Rollup/Cube/Grouping Sets:一个阶段而不是多个聚合。
sql
SELECT brand, country, DATE(ts) d, SUM(amount)
FROM gold. payments
WHERE ts >= NOW() - INTERVAL '7 days'
GROUP BY GROUPING SETS ((brand,country,d),(brand,d),(d));
实例化视图(MV)/投影:"payments_7d_by_brand_psp","rounds_1d_by_provider_game"。
Partial → Final aggregation:让引擎部分聚合在窃听器上(本地),最后聚合在协调员上。
Approximate: HLL for "COUNT (DISTINCT user)", TDigest for percentiles-价格便宜两倍,足以满足BI的需求。
7)窗口功能(整齐)
PARTITION BY恰好是高选择性的密钥;ORDER BY-通过柱状排序。
在可能的情况下用预装和半装配替换重型窗口。
sql
-- Instead of window distinct
SELECT brand, COUNT() users
FROM (SELECT DISTINCT brand, user_id FROM gold. sessions WHERE d>=CURRENT_DATE-7) t
GROUP BY brand;
8)过滤器、分页和TOP-K
滤波器的顺序对于CBO并不重要,但是选择性和索引/排序很重要。
LIMIT …WITH TIES/APPROX TOP-K-缩短扫描。
分区:"keyset pagination"而不是"OFFSET/LIMIT"用于大表。
sql
-- keyset
SELECT FROM t WHERE (date, id) > (:last_date,:last_id) ORDER BY date, id LIMIT 1000;
9) JSON/半结构化
将热路实现到扬声器('device.os`, `psp.method`).
如果引擎支持,则在JSON 路径上使用反向索引/GIN。
通过行避免UDF:使用属性突出显示更好地投影。
10)Approx和Sampling
HLL/Theta素描:便宜的"COUNT DISTINCT"。
TDigest/KLL:p95/p99无满分。
Reservoir/stratified采样:交互式研究和预览。
11)记忆,海峡和卡伦西
拼写后卫:join/agg上的内存限制;海峡-减少击球/并行主义,增加按键排序。
Concurrency&QoS:用于"热"dashbords和重型hoc的池;扫描/时间限制;kill-switch到"被遗忘"的请求。
Result cache/query cache:启用可重复的BI模式,在新鲜度令牌上致残。
12)回归测试和"双重运行"
存储前N查询的参考配置文件(计划/扫描字节/时间)。
在发布索引/群集之前-A/B运行:比较p95、scanned bytes、skipped share, shuffle。
创建"fail-fast"阈值:如果p95增长了>X%-回滚。
13)可观察性和SLO
SLI:
p50/p95/p99 latency, scanned bytes/query, skipped bytes %, files touched;
shuffle bytes, spilled bytes, peak memory;
cache hit-rate;accuracy approx聚合体。
Alerts: scanned bytes的增长,skipped share的下降,频繁的NLJ,海峡>门槛.
14) iGaming Case(食谱)
14.1 付款/PSP:"故障高峰"
WHERE: `ts BETWEEN now()-7d AND now()`, `brand,country,psp,status`.
聚会: 日;ORDER/Z-order: `(brand,country,ts)`;bitmap: `psp,status`;bloom: `transaction_id`.
MV: `payments_7d_by_brand_psp(status)`.
底线:p95 → ~ 1 s,扫描字节↓ 5-10 ×,零海峡。
14.2场比赛回合:前K场比赛/小时
ORDER BY / cluster по `(provider, game_id, occurred_at)`;预聚合的投影。
Approx Top-K+TDigest用于p95回合持续时间。
底线:热缓存上的子秒图形。
14.3 RG/AML:活动限制
JSON "reason" →专栏;bitmap `rg_state`, `kyc_level`;semi-join与最新状态。
底线:"30天"报告-秒,没有全扫描。
15)优化支票清单(每日)
1.收集前N查询及其配置文件(计划/字节/shafl)。
2.按日期分组+商定排序/群集案例。
3.检查推送和投影运行(仅限所需列)。
4.JOIN策略:广播小,SMJ排序,没有NLJ。
5.热达什板的预聚合/MV。
6.Approx在允许的地方(distinct/percentiles/top-k)。
7.JSON →列和/或反向索引。
8.复合/复构化;skipped bytes目标≥ 70%。
9.结果缓存和单独的concarrency池。
10.监视:p95,scanned bytes,shuffle,spill,hit-rate。
16)模板(准备使用)
16.1优化策略(YAML)
yaml workload: bi_hot slo:
p95_latency_ms: 1200 scanned_bytes_max_mb: 256 skipped_bytes_share_min: 0. 70 storage:
partition_by: ["date"]
cluster_by: ["brand","country","occurred_at"]
indexes:
bloom: ["transaction_id","user_surrogate_id"]
bitmap: ["psp","status","rg_state"]
aggregation:
mv:
- name: mv_payments_7d_brand_psp window: "7d"
group_by: ["brand","psp","status"]
approx:
count_distinct: "hll"
percentile: "tdigest"
concurrency:
pools: {bi_hot: 50, adhoc: 10}
timeout_s: 120
16.2查询回归测试(伪SQL)
sql
-- baseline: p95<=1200ms, scanned_bytes<=256MB
EXPLAIN ANALYZE
SELECT brand, psp, status, COUNT() cnt, SUM(amount) amt
FROM gold. payments
WHERE ts >= NOW() - INTERVAL '7 days'
AND brand =:brand AND country =:country
GROUP BY brand, psp, status;
16.3 DISTINCT重写
sql
-- Bad: Heavy COUNT (DISTINCT user_id)
SELECT COUNT(DISTINCT user_id) FROM gold. sessions WHERE d>=CURRENT_DATE-7;
-- Better: HLL sketch/preaggregate
SELECT hll_union(user_hll) FROM agg. sessions_7d_user_hll WHERE d>=CURRENT_DATE-7;
16.4 Keyset分离
sql
SELECT
FROM gold. game_rounds
WHERE (occurred_at, round_id) > (:ts,:rid)
AND brand=:brand AND country=:country
ORDER BY occurred_at, round_id
LIMIT 1000;
17)反模式
销售中的"SELECT";没有投影启动。
数百万行上的OFFSET分页。
COUNT DISTINCT没有草图;穿过一个完整的sort。
大型装置上的NLJ;在JSON表达式上加入。
小批次和分散的文件(元数据风暴)。
WHERE中的UDF字符串代替了专栏的实现。
忽略统计学家/ANALYZE-盲目优化器和全扫描。
没有回归测试和回滚阈值。
18)实施路线图
0-30天(MVP)
1.测量前N查询并安装SLO/SLI。
2.按日期+排序/群集案例分组;启用data skipping/bloom。
3.每"热门"支付报告一个MV;HLL/TDigest в BI.
4.分离查询池,启用结果缓存。
30-90天
1.重型窗口普查/JSON →预聚合/列。
2.小维广播合作;大型的SMJ;消除NLJ。
3.按计划进行堆叠和重新排列;密钥自动交换机。
4.可观察性和降解异同,A/B计划,自动回滚。
3-6个月
1.具有转换和SLA 的投影/MV目录。
2.Distinct/percentile/top-k的内核Approx跨所有行车记录板。
3.统一回归测试和预算$/请求模板。
4.JSON和UDF持续卫生:物化与指数。
19) RACI
数据平台(R):分期/聚类/复合、MV/投影、缓存、监控。
Analytics/BI (R):重写SQL、approx聚合、回归测试。
域所有者(C):切割和精度要求。
安全/DPO(A/R):隐私/PII,单元的k匿名。
SRE/Observability(C):SLO/Alerting,Concarrency和Capasity。
财务(C):$/查询预算和经济影响。
20)相关部分
分析存储索引,数据模式及其演变,数据验证,DataOps实践,数据聚类,维数降低,分析和度量API,MLOps:模型操作。
底线
查询优化不是"魔术印记",而是系统:可读的数据标记(批次/集群),预聚合和预置算法,正确的JOIN策略,缓存/串联以及持续监视p95和扫描字节。对于iGaming来说,这意味着在SLA和预算范围内快速稳定的支付、游戏和合规指标。