7.5 KiB
7.5 KiB
MySQL Query Design
Goal
让三方直接查 MySQL,同时保持查询简单、稳定、足够快。
当前数据规模:
device_record: 6219lookup key: 23463
这个量级不需要分库分表,也不需要 ES。MySQL 8 + 合适索引就足够。
Overall Design
建议把 MySQL 拆成两层角色:
- 离线构建层
- 继续以
brands/*.md为原始数据。 - 通过
tools/device_mapper.py生成标准化记录。 - 通过
tools/export_mysql_seed.py导出 MySQL seed SQL。
- 继续以
- 查询服务层
- 三方只读查询 MySQL。
- 只开放读账号,只允许
SELECT。 - 最好挂在只读实例或只读副本上,不直接连主库。
Tables
mm_device_record
兼容视图,一条设备一行,来自 mm_device_catalog 聚合。
主要字段:
record_iddevice_namebrandmanufacturer_brandparent_brandmarket_branddevice_typesource_filesectionsource_ranksource_weightaliases_json
用途:
- 回源查看聚合后的设备信息
- 兼容历史排查 SQL
- 不再作为独立物理表维护
mm_device_catalog
统一设备查询主表,一条设备 alias 一行。
主要字段:
modelalias_normrecord_iddevice_namebrandmanufacturer_brandparent_brandmarket_branddevice_typesource_ranksource_weightcodecode_aliasver_name
这是当前唯一的设备物理表。
原因:
- 只需要等值查
alias_norm - 同时兼容
mm_device_lookup和models - 最适合高频只读场景
mm_device_lookup
兼容视图,来自 mm_device_catalog。
mm_brand_lookup
品牌归一化表,用于把三方传来的品牌值归到:
manufacturer_brandparent_brandmarket_brand
比如:
荣耀->HONORredmi->market_brand=Redmixiaomi->manufacturer_brand=Xiaomi
models / python_services_test.models
为了兼容旧查询链路,保留一层旧结构适配:
mobilemodels.models(兼容视图)python_services_test.models(兼容视图)
字段映射说明:
model- 当前行可直接查询的原始设备标识
- 一条设备的多个 alias 会展开成多行
dtype- 对应当前的
device_type
- 对应当前的
brand- 优先使用
market_brand
- 优先使用
brand_title- 使用
manufacturer_brand
- 使用
code- 当前设备识别出的主型号编码
code_alias- 其他型号编码,使用
|拼接
- 其他型号编码,使用
model_name- 对应当前的
device_name
- 对应当前的
ver_name- 当前设备的人类可读别名,使用
|拼接
- 当前设备的人类可读别名,使用
这层主要用于兼容历史 SQL 和第三方直接查表,不建议作为后续新能力的主数据模型。
Query Contract
三方不要直接查原始字符串,统一查归一化后的 alias_norm。
归一化规则与当前项目一致:
- 全部转小写
- 只保留
[0-9a-z\u4e00-\u9fff] - 去掉空格、横线、下划线和其他标点
例如:
SM-G9980->smg9980iPhone14,2->iphone142NOH-AL00->nohal00
Recommended SQL
推荐分三层:
- 新接入
- 直接查
mobilemodels.mm_device_catalog
- 直接查
- 兼容现有查询
- 继续查
mobilemodels.mm_device_lookup
- 继续查
- 兼容历史旧结构
- 继续查
python_services_test.models
- 继续查
1. 主推查法:按设备标识查主表
SELECT
model,
record_id,
alias_norm,
device_name,
brand,
manufacturer_brand,
parent_brand,
market_brand,
device_type,
source_file,
section,
source_rank,
source_weight,
code,
code_alias,
ver_name
FROM mobilemodels.mm_device_catalog
WHERE alias_norm = ?
ORDER BY source_rank ASC, record_id ASC
LIMIT 20;
说明:
alias_norm是主查询键model是当前命中的原始设备标识code / code_alias / ver_name用于兼容历史字段和辅助展示
1.1 兼容查法:沿用 mm_device_lookup
如果接入方已经依赖 mm_device_lookup,可以不改 SQL:
SELECT
alias_norm,
record_id,
device_name,
brand,
manufacturer_brand,
parent_brand,
market_brand,
device_type,
source_file,
section,
source_rank,
source_weight
FROM mobilemodels.mm_device_lookup
WHERE alias_norm = ?
ORDER BY source_rank ASC, record_id ASC
LIMIT 20;
1.2 兼容旧结构查法:沿用 python_services_test.models
如果接入方仍然沿用旧表结构,可以继续查:
SELECT
model,
dtype,
brand,
brand_title,
code,
code_alias,
model_name,
ver_name
FROM python_services_test.models
WHERE model = ?
LIMIT 20;
2. 主推查法:带品牌约束查
先把品牌查成归一化结果,再过滤:
SELECT
l.model,
l.record_id,
l.alias_norm,
l.device_name,
l.brand,
l.manufacturer_brand,
l.parent_brand,
l.market_brand,
l.device_type,
l.source_file,
l.section,
l.source_rank,
l.source_weight,
l.code,
l.code_alias,
l.ver_name
FROM mobilemodels.mm_device_catalog AS l
LEFT JOIN mobilemodels.mm_brand_lookup AS b
ON b.alias_norm = ?
WHERE l.alias_norm = ?
AND (
b.alias_norm IS NULL
OR (b.market_brand IS NOT NULL AND l.market_brand = b.market_brand)
OR (b.manufacturer_brand IS NOT NULL AND l.manufacturer_brand = b.manufacturer_brand)
OR (b.parent_brand IS NOT NULL AND l.parent_brand = b.parent_brand)
)
ORDER BY l.source_rank ASC, l.record_id ASC
LIMIT 20;
3. 兼容查法:按设备记录聚合查看
如果历史排查逻辑需要“一台设备一行”,可以继续查:
SELECT
record_id,
device_name,
brand,
manufacturer_brand,
parent_brand,
market_brand,
device_type,
source_file,
section,
source_rank,
source_weight,
aliases_json
FROM mobilemodels.mm_device_record
WHERE record_id = ?
LIMIT 1;
Migration Advice
建议迁移顺序:
- 新增接入直接使用
mobilemodels.mm_device_catalog - 旧查询先保持
mm_device_lookup/python_services_test.models不变 - 待业务侧完成字段适配后,再逐步切到主表
这样可以做到:
- 数据底层只维护一张设备实体表
- 现有查询链路不需要同时改造
- 新能力统一围绕主表扩展
Performance Strategy
要点只有四个:
- 只允许等值查询
alias_norm- 禁止
%xxx%这类模糊查
- 禁止
- 查询主表用
mm_device_catalog- 兼容链路走
mm_device_lookup/models视图
- 兼容链路走
- 给三方只读账号
- 只开放
SELECT
- 只开放
- 最好放只读实例
- 不让三方流量影响同步和管理操作
在当前数据规模下,alias_norm 命中是很轻的查询。
Update Flow
建议每日全量刷新一次,不做增量。
原因:
- 数据量小
- 全量替换更稳定
- 不容易出现脏数据和漏删
建议流程:
- 拉取上游原始数据
- 生成最新
device_index.json - 导出 MySQL seed SQL
- 在 MySQL 中执行:
DELETE FROM mm_device_catalogDELETE FROM mm_brand_lookup- 批量插入新数据
- 完成后切换读流量
Security
如果一定让三方直连 MySQL,至少要做这些限制:
- 单独只读账号
- IP 白名单
- 只授权
mobilemodels和兼容需要的python_services_testschema - 不开放 DDL / DML
- 连接数限制
- 查询超时限制
更稳妥的方式仍然是:
- 三方查你们自己的只读网关
- 网关内部查 MySQL
但如果现阶段必须直连库,上面的三张表已经足够支撑。
Files
- Schema:
sql/mobilemodels_mysql_schema.sql - Seed exporter:
tools/export_mysql_seed.py
生成 seed:
python3 tools/export_mysql_seed.py
默认输出:
dist/mobilemodels_mysql_seed.sql