按业务选择查询路径与优化边界
查询设计先确定业务问题、时间口径和实际入口,再看索引。APP 运行明细在 Task 走 TDengine;设备安装列表在 Device 走 ES 月索引。 安装快照说明“装了什么”,运行记录说明“某天运行多久、打开几次”,不能因为都带设备与包名就共用查询方案。
本页将当前代码条件、实测结构和后续建议分别说明。依据为 2026-09-18 源码与元数据快照;没有读取业务数据、执行 EXPLAIN、ES 搜索或性能测试。对象规模、分区数量及索引存在性都不能证明某条查询的实际耗时或执行计划。
按业务场景选入口
| 业务问题 | 当前实际存储和对象 | 当前代码采用的范围 | 先检查什么 |
|---|---|---|---|
| 按渠道、型号或标识找设备 | Device ES base_device,特定条件下切 MySQL nebula_ids.base_device | ES 与 MySQL 各自构造条件,部分回源和深分页分支不同 | MAC/CPU复合身份、字段类型、两个后端的过滤是否同义;见 Device 查询指南 |
| 看某设备某月安装了哪些应用 | ES app_install_device{yyyyMM} 列表;MySQL 月表展开 JSON 查看安装详情 | month 直接选一个索引或表;安装列表 ES 当前主要筛 MAC/CPU/应用数 | 时间数组并未自动变成列表 ES 过滤;apps 是 JSON 字符串而非 nested 应用对象 |
| 看应用每天运行多久 | TD nebula_ids 或 app_runtime_archive 的 flow_app_runtime | 先按当前月/上月与更早月份分源,再使用 record_time 范围、day IN 及应用/设备条件 | 跨冷热边界请求会被拒绝;日记录时间归零,不用入库时间替代;见 Task 查询指南 |
| 看运行排行榜 | MySQL nebula_ids.stats_app_runtime_top 固定表 | 指定日期和周期类型,按需跨版本汇总 | 唯一索引顺序为 (date,package_name,type,version_code);跨版本设备数相加不等于跨版本去重 |
| 看日/月/年活跃趋势 | TD app_activity_day/month/year.app_activity_detail | 周期选择库;包名及周期 TAG 限定范围,汇总 SQL 另有 ts BETWEEN | 月/年时间归一到周期起点,避免月中开始漏首周期;日/月/年编码与运行排行榜不同 |
| 追查原始活跃心跳 | MySQL ik_thirdparty.app_activity_log_{包名}_{yyyyMMdd} | 按服务端接收日选表,设备哈希决定表内 LIST 分区 | 是日表而非月表;分区内没有已采集到的 MAC/CPU/时间二级索引 |
| 查卸载反馈 | MySQL ik_apk.flow_blacklisted_device | 策略、MAC、CPU与 create_time 窗口 | 实测唯一键以策略开头;界面显示设备时间不代表 WHERE 按设备时间过滤;见 Blacklist 查询指南 |
| 查按日杀进程反馈 | TD blacklisted.app_kill_record | 策略 TAG、设备普通列、业务日期 ts 或接收时间 create_time | 区分“何时执行”和“何时收到”;TAG 过滤不等于已验证命中额外索引 |
这张表用于选择已核对的查询链,不代表所有历史表均参与当前业务。尤其 nebula_ids 是单体遗留库;Task 已有独立库,仍有遗留依赖,不能把同库连接去重解释成业务已完全共库或完全拆分。
分表、分区、索引与分片分别解决什么
按日期分表/分索引决定访问哪些业务对象,例如安装月索引和活跃日表。MySQL 分区决定一张表内部的数据分布;本项目大部分分区键是设备哈希值而非时间。字段索引决定如何定位或排序候选记录。ES 主分片分布一个索引的数据,副本保存副本;ES 分片不是时间分区,MySQL 分区也不是二级索引。
MySQL 优化器可以在条件足以确定分区范围时裁剪不可能匹配的分区;等值、IN 及部分范围条件是否适用取决于分区表达式。应用显式写 PARTITION(...) 是另一种指定范围的方式。无论哪种方式,都不能自动补出分区内缺少的设备或时间索引。MySQL 8.0 分区裁剪说明
联合索引也要保留实际列序。例如 (date,package_name,type,version_code) 不能写成 (date,type,package_name,version_code),只给日期和类型不能直接视为两列连续前缀定位。是否使用索引及是否还要过滤、排序,须由执行计划确认。MySQL 8.0 多列索引说明
实测 MySQL 分区概览
2026-09-18 21:32:38—21:33:23(北京时间)对原12个 MySQL 库补采 information_schema.PARTITIONS 中有分区名的定义,复算为 37张声明式分区表、27,130项分区定义。这是结构项数量,不是业务记录数量;文档按对象族汇总,不展开逐项清单。
| 库与对象族 | 表数 | 分区方式 | 每表分区数 | 分区定义项合计 |
|---|---|---|---|---|
ik_thirdparty.app_activity_log_* | 25 | LIST(partition_index) | 1024 | 25,600 |
ik_app_install.app_install_device202606…202609 | 4 | LIST(partition_index) | 150 | 600 |
ik_rule.rule_mac_item、rule_mac_resource_item | 2 | LIST(partition_index) | 300 | 600 |
ik_yudao_device.device_log_file | 1 | KEY(mac,cpuid) | 32 | 32 |
ik_yudao_report.apk_push_history、apk_push_history_new、apk_push_history11111 | 3 | KEY(mac,cpuid) | 16 | 48 |
nebula_ids.app_install_device | 1 | LIST(partition_index) | 150 | 150 |
nebula_ids.app_install_package | 1 | LIST(partition_index) | 100 | 100 |
| 合计 | 37 | — | — | 27,130 |
ik_apk/ik_yudao_bpm/ik_yudao_infra/ik_yudao_launcher/ik_yudao_system/ik_yudao_task 本次未返回声明式分区定义。这不排除业务层动态选表,也不代表这些库没有字段索引。Report 三张同族表、遗留安装表是否参与具体入口,仍以各服务源码归属为准,不能仅按命名给所有对象安排在线查询职责。
将“已使用条件”“真实物理结构”“建议”分开
| 对象 | 代码已使用 | 实测物理支撑 | 后续建议,尚未实施 |
|---|---|---|---|
| 活跃 MySQL 日表 | 按应用/接收日选表,显式分区;分区内按 MAC/CPU 筛选、按接收时间排序 | 25张表均仅 PRIMARY(id,partition_index) | 频繁设备追查时评估 MAC/CPU/时间候选索引,先确认分区内数据量、计划和写入成本 |
| 运行排行榜 | 日期等值,包名/应用名包含匹配,周期类型;可按包名聚合 | PRIMARY(id)、非唯一 date(date)、唯一 key(date,package_name,type,version_code) | 已知包名的精确查询与应用名称查找分开;再评估日期+类型读取是否值得新索引 |
| 卸载反馈 | 策略/设备等值与接收时间窗口 | 唯一 (blacklisted_id,mac,cpu,event_id,device_time),没有 create_time 索引 | 先决定按发生时间还是接收时间分析;不要把第五列设备时间当作接收窗口索引 |
| TD运行明细 | 时间范围+日期TAG,包名或设备条件;热/归档二选一 | 两库实测同名超级表、package_name/day TAG、时间列与复合键 | 汇总SQL缺少日期TAG时评估补齐;先统一版本、MAC与MAC+CPU统计口径 |
| 安装 ES 列表 | 指定月索引,MAC短语匹配、CPU及应用数条件 | 不同月份 mapping 不完全一致,部分日期/CPU由 keyword/date 变成 text+keyword | 先按同构月份与真实字段查询;mapping修复和历史重建需独立设计,不直接把日期字符串当日期类型 |
这些候选项不是变更清单。完成查询计划核验后,才有依据决定是否增加索引、调整字段类型或改变数据组织;还需保留唯一键原有的去重语义。
ES:查对字段,也要查对月份
text 用于分词搜索,默认不用于排序和聚合;结构化标识、完整值分组和排序通常应使用实际存在的 keyword 或对应子字段。父字段与子字段不是同一种匹配语义,不能把 matchPhrase(mac) 与完整 MAC 等值查询混为一谈。ES 7.17 text 说明、keyword 说明
本次安装 apps 是整份 JSON 字符串,不存在可直接查询的 apps.packageName nested 路径;其 keyword 子字段还受实际 ignore_above 限制,不能拿整串 keyword 统计应用包。近期月份部分时间列实际是 text,即使有 keyword 子字段,也不能据此声称具有日期类型的范围与时区语义。详细字段差异见 Device 查询指南。
补采 _settings 的23个业务索引包含 base_device、apk_push_history、19个月安装索引(202503—202609)、app_install_device 和 app_install_device_s0:
| 设置 | 本次实际结果 | 不能由此推出的结论 |
|---|---|---|
| 主分片 | 23个索引均为1 | 不是按月份分成1个时间分区,也不证明数据量或当前节点分配 |
| 副本 | base_device/apk_push_history/app_install_device202507 为0,其余20个索引为1 | 只核验设置,未核验副本实际分配或运行状态 |
mac_analyzer | 除 app_install_device_s0 未返回此配置,其余22个均为 pattern、分隔模式 : | 分词配置不证明所有入口都使用了相同字段或精确匹配 |
index.sort.* | 本次返回的所选设置中无此项 | 请求排序不是已配置的物理索引排序 |
index.max_result_window | 不在补采器保留字段范围 | 不能把源码页码阈值写成实测服务端窗口,也不能拿文档默认值替代采集值 |
index.number_of_shards 指主分片数,number_of_replicas 控制每个主分片的副本数;它们与按月创建多个索引是不同层级的设计。ES 7.17 索引设置说明
优化顺序与验证边界
先确认结果语义:设备身份是 MAC 还是 MAC+CPU,计数是记录数还是去重设备数,日期是设备发生日还是服务端接收日。接着核对请求真正生成了哪些 WHERE/ES 条件,再限定目标库、月份表/索引、TAG或分区。最后才考虑字段索引和分页方案。
分页返回一页,不代表聚合前只读一页。异步导出只代表执行方式改变,也不保证流式读取;例如 Task 运行导出按天分段,但单日仍整体 selectList。Device 某些路径已有 search_after 或流式导出,不能推断所有入口都具备相同能力。稳定排序、完整过滤及跨月去重必须一起定义。
下一轮若获准运行验证,可按真实入口收集生成语句/DSL、代表性参数、执行计划与耗时,分别评估候选范围、索引、排序、聚合和回源行为。当前没有 EXPLAIN 或性能测量证据,因此不写“已命中最优索引”“只扫一个物理分区”或“性能提高若干倍”。补采工具、时间与未采集范围见 采集说明。