82 lines
2.7 KiB
Markdown
82 lines
2.7 KiB
Markdown
|
|
# service_mask 索引性能对比报告
|
|||
|
|
|
|||
|
|
## 结论摘要(如何判定“提升”)
|
|||
|
|
- 在 `service_mask` 位运算过滤场景中,新增 `idx_service_mask_first_bit` 后,查询计划应从 `Seq Scan` 转为 `Index Scan/Bitmap Index Scan`(并触发分区裁剪时,仅扫描命中的分区)。
|
|||
|
|
- 若过滤选择性较高(例如仅小比例满足 “&1=1”),通常能显著降低 IO 与响应时间。
|
|||
|
|
|
|||
|
|
## 索引脚本
|
|||
|
|
- 结构与索引创建包含在:
|
|||
|
|
- `scripts/db/010_heartbeat_schema.sql`
|
|||
|
|
- `scripts/db/020_partitioning_auto_daily.sql`
|
|||
|
|
- 关键语句:
|
|||
|
|
- `CREATE INDEX idx_service_mask_first_bit ON heartbeat.heartbeat_events ((service_mask & 1));`
|
|||
|
|
|
|||
|
|
## 测试方法(可复现)
|
|||
|
|
### 1) 数据准备
|
|||
|
|
建议在测试库中写入至少 100 万行,保证计划稳定:
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
SELECT heartbeat.ensure_partitions(current_date, current_date + 1);
|
|||
|
|
|
|||
|
|
INSERT INTO heartbeat.heartbeat_events (
|
|||
|
|
ts_ms, hotel_id, room_id, device_id, ip,
|
|||
|
|
power_state, guest_type, cardless_state, service_mask,
|
|||
|
|
pms_state, carbon_state, device_count, comm_seq, extra
|
|||
|
|
)
|
|||
|
|
SELECT
|
|||
|
|
(extract(epoch from now()) * 1000)::bigint + (g % 86400000),
|
|||
|
|
(g % 1000)::int2,
|
|||
|
|
('R' || (g % 500))::varchar(50),
|
|||
|
|
('D' || (g % 20000))::varchar(64),
|
|||
|
|
'127.0.0.1:1',
|
|||
|
|
1, 0, 0,
|
|||
|
|
(g % 1024)::bigint,
|
|||
|
|
0, 0, 1, (g % 100000)::int4,
|
|||
|
|
jsonb_build_object('src','bench')
|
|||
|
|
FROM generate_series(1, 1000000) g;
|
|||
|
|
|
|||
|
|
ANALYZE heartbeat.heartbeat_events;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2) 对比维度
|
|||
|
|
- 响应时间:`EXPLAIN (ANALYZE, BUFFERS)` 的 `Execution Time`
|
|||
|
|
- QPS:用同一 SQL 连续执行 N 次(例如 1000 次)并统计平均耗时(由压测端计算)
|
|||
|
|
- 查询形态:
|
|||
|
|
- 形态 A(位运算表达式,命中表达式索引):
|
|||
|
|
- `WHERE (service_mask & 1) = 1`
|
|||
|
|
|
|||
|
|
## 修改前/后对比(需要在目标环境产出)
|
|||
|
|
说明:不同硬件/数据分布/缓存命中会导致数值差异。以下对比表格请在你的环境执行本报告“测试方法”并填入实际输出。
|
|||
|
|
|
|||
|
|
### A. 修改前(无 idx_service_mask_first_bit)
|
|||
|
|
- 查询计划(期望:Seq Scan 或 Bitmap Heap Scan + BRIN):
|
|||
|
|
|
|||
|
|
```sql
|
|||
|
|
EXPLAIN (ANALYZE, BUFFERS)
|
|||
|
|
SELECT count(*)
|
|||
|
|
FROM heartbeat.heartbeat_events
|
|||
|
|
WHERE (service_mask & 1) = 1;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 结果记录:
|
|||
|
|
- QPS(1000 次):____
|
|||
|
|
- P50 响应时间(ms):____
|
|||
|
|
- P95 响应时间(ms):____
|
|||
|
|
|
|||
|
|
### B. 修改后(有 idx_service_mask_first_bit)
|
|||
|
|
```sql
|
|||
|
|
EXPLAIN (ANALYZE, BUFFERS)
|
|||
|
|
SELECT count(*)
|
|||
|
|
FROM heartbeat.heartbeat_events
|
|||
|
|
WHERE (service_mask & 1) = 1;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
- 结果记录:
|
|||
|
|
- 位运算表达式命中索引(期望:Index/Bitmap Index Scan):
|
|||
|
|
- QPS(1000 次):____
|
|||
|
|
- P50(ms):____
|
|||
|
|
- P95(ms):____
|
|||
|
|
|
|||
|
|
## 结论与建议
|
|||
|
|
- 业务使用 `WHERE (service_mask & 1) = 1` 可直接命中 `idx_service_mask_first_bit`。
|