跳到主要内容

设备与安装应用的大数据查询路径

先确定业务对象,再选择存储:base_device ES 用于设备档案检索,app_install_device{yyyyMM} ES 用于设备安装快照列表。安装快照回答装了什么,APP 运行记录回答运行过多久,两者不是同一索引。 本页不把 task 的 APP 运行 TDengine 记录改称为 ES 数据;APP 运行记录的实际路径见 task 设计。

下面“现状”来自当前 Controller、Service、Mapper 与 2026-09-18 测试环境 mapping;“查询建议”是据此给出的使用/后续实现方向,不表示已修改接口或验证性能。本次未执行任何业务查询、聚合、导出或 EXPLAIN。

按业务问题选入口​

业务场景当前实际后端和对象有效条件与物理支撑现有查询边界
设备列表:按渠道、型号、MAC、SDK、地区筛选装饰链优先 ES base_device;关闭设备 ES 或 pageNo×pageSize 超阈值时切 MySQL nebula_ids.base_deviceES channel_id/model_id/region_id 数值、cpu keyword,MAC 是 text+keyword;MySQL 有渠道、型号、激活时间索引及 (mac,cpu) 唯一键两后端条件并非完全同义,见下文;未按时间分设备 ES 索引
单个 MAC 查设备档案Redis 装饰层→ES base_device→MySQL base_deviceES 按 MAC 查询、CPU 降序取 1;数据库 MAC+CPU 组合索引同 MAC 可对应多个 CPU,单 MAC 入口不是精确复合身份查询;ES 未命中/异常可回 DB
按导入 Excel 清单筛设备ES base_device.excel_file_ids;MySQL 设备表加关系表子查询ES keyword 数组;MySQL device_excel_file_rel 的 (excel_file_id,device_id) 唯一键和 device_id 索引ES 数组为关系投影,需要同步维护;不是 nested 文档
某月设备安装列表Controller 直接查 ES app_install_device{month},例 app_install_device202609当前只构造 MAC matchPhrase、CPU 等值、应用数量等值条件单月份入口;无自动跨月合并,无设备列表那套深分页回 MySQL 逻辑
某设备在某月安装的具体包/版本MySQL ik_app_install.app_install_device{month},对 apps 做 JSON_TABLE 展开先用 mac,cpu_id,partition_index 定位设备快照,再用包名、版本、应用名等筛展开行这才是当前实际按包字段查询的入口,不是 ES apps.package_name 查询
导出某月设备安装快照MySQL 同月份表,按 partition_index=1..150 循环每次附加月份、分区号及可选 MAC/CPU;每分区读取列表后写 CSV任务方法实际传递月份/MAC/CPU,不能假定列表所有条件都作用于导出
大量设备档案异步导出MySQL base_device 流式 ResultHandler复用 MySQL 条件,JDBC fetchSize=Integer.MIN_VALUE,每 10000 条组成输出微批次不走 ES 深分页;流式减少应用内存,不保证过滤都命中索引
设备档案同步导出、按渠道获取 MAC 集合ES 装饰器部分方法使用 searchAfterPage;同步导出过阈值可回 MySQL源码使用实体 id 排序;实际 id 对应 ES _id不是所有分页接口都用 search_after;SDK 最终 DSL、排序可执行性和一致性本次未运行核验

设备列表:ES 和 MySQL 不能当成完全相同的查询​

DeviceServiceImpl.getDevicePage 调用装饰链,取得结果后再补最后在线时间和渠道名。最后在线时间来自 Redis,不是本次 ES 查询中的日期筛选字段。DeviceESDecorator.getDevicePage 根据设备 ES 开关及页码阈值路由;配置类默认阈值为 10000,提供的配置文本也写为 10000。运行进程的最终配置值本次未读取。

设备 ES 构造器实际处理:状态、订单号、MAC、渠道 ID 集合、Excel 清单 ID、型号文本/ID、SDK、名称、系统版本、build、CPU、IP归属地/设备IP、地区 ID 集合、序列号集合、UI 集合和激活起止时间。多值 UI 用多个 matchPhrase 做 OR。

需要特别区分的行为:

  • ES 的 MAC 列表筛选是 matchPhrase(mac);MySQL 是 MAC 前缀 LIKE '输入%'。二者可能返回不同范围,不是数据同步失败的唯一解释。
  • ES 激活范围来自 beginTime/endTime 字符串,按 yyyy-MM-dd 解析后对 activation_time 做 >= / <=;不能默认为结束日期已自动扩展到当日 23:59:59。MySQL 构造器用 activationTime[],字段入口不同。
  • MySQL 支持的 certification/userId/companyId/remarks/brand/ddr/createTime 等条件没有在设备 ES 构造器实现;realDdr/realFlash/productionOrder/masterModel/pcbModel 虽已在 ES BO 和 mapping 出现,也没有在当前 ES 构造器加入筛选。
  • ES 分页会在用户排序之后追加 update_time DESC、实体 id ASC。PageUtils 根据 Java 注解 判断是否加 .keyword,不是实时读取 mapping;因此注解与实库不同的字段仍有排序风险。
  • 设备列表方法没有统一捕获 ES 异常再回 MySQL。异常回退存在于单 MAC 查询、批量 MAC→ID 等特定方法;批量 ID 查询若 ES 返回空集不会再回 DB。

查询建议:需要核对具体设备时优先明确 MAC 与 CPU;分析设备所属群体时先使用渠道/型号/地区这些明确标识,减少仅靠宽泛文本搜索。需要在 ES 和 MySQL 间核对结果时,应对齐上述条件与时间口径,不能把同一份请求参数默认看作等价查询。

安装列表:月份先选索引,设备详情再查 JSON​

当前 ES 安装列表用 AppInstallDeviceEs.INDEX_NAME + reqVO.month 选择一个索引,不按时间数组自动推导月份。VO 虽声明 lastUndateTime[]/createTime[]/updateTime[],当前 ES buildQueryWrapper 没有使用它们,apps 全文条件也处于注释状态。传入这些字段并不能证明列表已按它们过滤;现有接口没有按包名搜索全部设备的有效 ES 条件。

MySQL 安装详情使用 app_install_device${reqVO.month} 的显式表名,避免动态表名拦截器处理 JSON_TABLE。partition_index 由服务端请求对象按 abs(hash(mac+cpu)%150)+1 计算,再与 MAC/CPU 一起限定快照行;展开出的 name/packageName/className/versionCode/versionName/lastUndateTime/isSysteApp 是 JSON 数组元素字段,不是月表列。

现有详情 Mapper 支持应用名/包名包含、版本名/版本号和系统应用标识等条件。className 非空分支当前 SQL 写作 j.classNamelike,需要先由维护者确认修正,不能把该分支当成已验证可用。这里仅记录查询能力边界,未修改业务代码。

安装列表只取用户排序列表的第一个字段,并仅对 mac 特判为 mac.keyword;其他字段直接交给分页排序对象。它没有针对各月 mapping 差异做适配,也没有在该方法检查 app.install.es.enable 或 app.install.pageMaxIndex。配置中存在这些选项,不代表每个入口都使用。

查询建议:先选明确月份,再按设备缩小快照范围,查看包/版本时使用详情入口。跨月需要逐月处理并保留月份维度;如果以后实现跨月 ES 查询,应先按 mapping 同构组划分索引,再定义去重、排序和总数口径,不能直接拿单月接口结果宣称全周期安装设备数。

依据实际 mapping 选择字段​

对象/字段实测类型与子字段可用方式及限制
base_device.mactext,mac_analyzer,另有 mac.keyword当前列表用分词短语查询;后续需要完整 MAC 精确筛选/分组时应明确使用已存在的 keyword 子字段
base_device.cpu/order_number/serial_number/brand/ddrkeyword适合精确标识筛选;只有业务构造器实现的条件才会实际生效
base_device.channel_id/model_id/region_id/status数值类型用 ID/状态精确筛选;不要改用分词后的显示名称代替业务主键
base_device.activation_time/create_time/update_timedate,格式 yyyy-MM-dd HH:mm:ss物理上可做日期范围/排序;当前接口具体支持哪些时间条件仍以上述构造器为准
base_device.device_model/sdk_version_name/ui/build/name 等text + .keyword,keyword ignore_above=256text 用于搜索;按完整值分组/排序应选对应 keyword。超过该长度的值不会进入 keyword 子字段,不能用它统计所有长文本
base_device.real_ddr/real_flash/production_order/master_model/pcb_model实际 text + .keyword,源码标注 KEYWORD后续直接 ES 查询应使用实测子字段;当前注解驱动排序不能保证自动选对
base_device.excel_file_idskeyword,可保存多值是清单 ID 数组,不是 nested 对象,没有 excel_file_ids.id 路径
安装索引 appstext + apps.keyword,keyword ignore_above=256存的是整份列表 JSON 字符串,不是 nested 应用数组;不能对 apps.packageName 做 nested 查询,也不能用整串 keyword 代替包级去重统计
202503–202512 安装索引 cpu_id 与日期列CPU 为 keyword;下划线时间列为 date可以按已存在字段类型设计精确筛选及日期处理,但现有列表未使用时间过滤
app_install_device202601下划线 CPU/date 字段保留,另并存驼峰 text 字段优先按下划线物理契约辨认字段;不能假定两套字段都有同样完整的数据
202602–202609 安装索引 cpu_id 与日期列text + .keywordCPU 完整值应选 cpu_id.keyword;日期的 keyword 只提供字符串语义,不应据此宣称具有日期范围/时区/日期聚合能力

近期月索引的日期不是 date,直接排序父 text 字段也没有本次证据证明可用。需要可靠时间分析时,应使用有真实日期类型的存储/同构月份,或单独规划 mapping 修复及历史重建;不能为了查询临时把日期 keyword 的字典序当成已核验时间线。mapping 修复、索引模板与历史数据迁移均未在本次实施。

实际别名 ee_default_alias 同时覆盖 base_device、多个安装月索引、app_install_device_s0 及 document_s1 等 14 个索引,并且没有覆盖最新全部月份。它不是“全部设备安装历史”的专用别名,查询应明确目标索引。当前相关 Service 没有显式传入按 MAC/月份的 ES _routing;MySQL 的 partition_index 也不是 ES routing 字段。

实测分表、分区与 ES 分片​

2026-09-18 21:32–21:33(北京时间)补采的物理定义确认如下。按月分表先选择业务月份;MySQL 分区在所选表内组织数据;字段索引支持具体过滤或排序;ES 分片负责索引的数据分布。四者不能互相替代,也不能仅凭定义推断一次查询的实际执行计划。

实际对象物理参数对当前业务查询的含义
ik_app_install.app_install_device202606 至 202609,共 4 张月表每表 150 个 LIST(partition_index) 分区月份决定表;详情接口计算分区值,导出逐分区遍历。分区键不是日期,跨月仍需选择多张表
nebula_ids.app_install_device150 个 LIST(partition_index) 分区是单体遗留表,不能因分区数相同就当作当前月份表或全月份汇总
nebula_ids.app_install_package100 个 LIST(partition_index) 分区遗留包结构与设备快照分区数不同,不能套用设备快照的 %150 规则
ik_yudao_device.device_log_file32 个 KEY(mac,cpuid) 分区按设备标识组织日志文件元数据,并非按日志日期分区;MAC/CPU 的条件与字段索引需分别核对
base_device、app_install_device202507每索引 1 个主分片、0 个副本主分片数不是 MySQL 分区数;本次只核验配置,未核验运行时分片分配或查询延迟
其余安装月索引(202503–202609,除 202507)、app_install_device、app_install_device_s0每索引 1 个主分片、1 个副本月份对应不同 ES 索引,不是在一个索引内创建 MySQL 式月份分区

本次采集的 23 个业务 ES 索引均为 1 个主分片;其中 task 的 apk_push_history 也是 0 副本,其业务归属仍在 task。返回结果未包含 index.sort.*,不能把请求中的排序写成已配置的物理索引排序;也没有返回 index.max_result_window,源码的 10000 页码阈值不能冒充实测服务端窗口参数。

除 app_install_device_s0 未返回该分析器配置外,其余采集业务索引的 mac_analyzer 均为 pattern 类型、分隔模式为冒号 :。这解释了 MAC 父字段的分词语义;完整 MAC 的精确匹配、分组或排序仍应按实际 mapping 使用对应 keyword 字段。副本数、主分片数和分析器配置均不证明某个过滤条件已经命中最优路径。分区裁剪、索引选择与性能收益需要后续获准的执行计划或实际查询验证,本次没有执行。

已有优化与仍需验证的内容​

源码已实现:设备列表超阈值切 MySQL;设备异步导出流式拉取;安装导出按月、按分区循环;单设备安装详情先定位快照再展开 JSON;部分设备 ES 导出/渠道查询使用 search_after;Excel 集合的 MySQL 查询用子查询避免先加载海量 ID。

不能据此宣称已经解决:所有 ES 异常都自动回源、安装列表跨月检索、日期数组已生效、包名在 ES 中可作为 nested 字段查询、跨月 mapping 一致、所有导出过滤与列表完全一致、每个查询都能命中最优索引。特别是现有 search_after 分支仍使用实体 id(映射 _id)作排序,当前 mapping 未提供独立 id 数值列;需在后续允许运行验证时确认 SDK 输出与服务端支持,不能直接推广为其他接口的现成模板。

完整物理信息见 设备 ES mapping、近期安装 ES mapping、过渡月份 mapping、设备 MySQL 字典 和 安装 MySQL 字典。本节物理布局依据补采快照 mysql-layout-ik_app_install.json、mysql-layout-nebula_ids.json、mysql-layout-ik_yudao_device.json 与 es-query-settings.json;它们与源码分区算法、接口页码阈值分别记录,不混作同一种证据。

新增源码证据​

路径前缀沿用 device 首页。

行为源码位置
设备列表路由、同步导出、排序D/service/device/DeviceESDecorator.java:140,164,200
设备 ES 条件、单 MAC 回源D/service/device/DeviceESDecorator.java:297,349
设备 MySQL 条件与 Excel 子查询D/dal/mysql/device/DeviceMapper.java:29;D/service/device/DeviceDBDataService.java:59
异步流式设备导出D/service/device/DeviceServiceImpl.java:766,799,822;D/dal/mysql/device/DeviceMapper.java:92
安装列表直接 ES 与 JSON 详情入口D/controller/admin/appinstalldevice/AppInstallDeviceController.java:62,131
安装单月 ES、排序及有效过滤D/service/appinstalldevice/AppInstallDeviceEsServiceServiceImpl.java:92,113
安装月份/分区导出及 JSON 展开D/service/appinstalldevice/AppInstallDeviceServiceImpl.java:189,221,266;R/mapper/appinstalldevice/AppInstallDeviceMapper.xml:12,24,32
分区键请求计算D/controller/admin/appinstalldevice/vo/AppInstallDeviceDetailPageReqVO.java:59;D/utils/AppInstallUtil.java:124
注解驱动 keyword 排序F/yudao-spring-boot-starter-elasticsearch/src/main/java/cn/iocoder/yudao/framework/es/util/PageUtils.java:23,38
用户文档
AI 助手
Agent 列表
请选择一个 Agent 开始对话
AI 问答