9.8 KiB
业务数据分层存储与迁移清单
结论
caiwu_business_mirror 继续作为唯一的中央同步入口,但不再要求所有数据都以 MariaDB 行存表长期保存:
- MariaDB 保存同步状态、权限/配置、小型维表、当前态业务表和页面直接使用的汇总表;
- ClickHouse 保存订单、退款、推广费用、原始账单等大事实表,承担筛选、聚合和历史明细查询;
- 七牛 S3 兼容对象存储保存按稳定哈希分区生成的不可变 Parquet,作为重建和灾难恢复层;
- 页面只能经过固定 Repository 读取服务层,不能直接访问上游,也不能自行选择数据库。
列式迁移采用 MariaDB 旧表 -> ClickHouse 新代次 -> 行数/唯一键对账 -> Parquet 分区归档 -> 页面切换 -> 观察 -> 旧表清理。任何一步失败都保留 MariaDB 原表和当前页面链路。
2026-09-07 生产只读盘点
本次通过生产 information_schema 与当前代码引用交叉核对。生产部署 SHA 为 f0150f1c4971c807239e216398df32ec1751d121;服务器 500 GiB 系统盘当时约有 385 GiB 可用。caiwu_business_mirror 有 65 张物理表,InnoDB 估算约 5172 万行、51.4 GiB(数据 32.2 GiB、索引 19.2 GiB)。InnoDB TABLE_ROWS 是估算值,迁移验收必须使用精确 COUNT(*) 与唯一键计数。
占用最大的表如下:
| 表 | 估算行数 | 总占用 | 当前真实用途 | 处理 |
|---|---|---|---|---|
ops_jst_sku_map |
2492 万 | 28.1 GiB | 当前页面没有查询,只曾被推广费用页误当作就绪条件 | 立即停止后续同步;现有表保留,完成 Parquet 归档后再申请删除 |
store_report_bill_rows |
1385 万 | 7.8 GiB | 只用于生成 store_report_daily_rows,页面不应扫描原始行 |
页面改读日汇总;原始层迁入 ClickHouse 并归档后,仅保留必要增量窗口 |
ops_item_sales_cost_ratio |
289 万 | 3.6 GiB | 推广费用页面 | 迁入 ClickHouse;页面查询验收后切换 |
ops_jst_original_bill_info |
184 万估算 / 已处理 208 万 | 2.3 GiB | 新登记的通用原始账单,当前无页面直读,首次同步已暂停 | 不再向 MariaDB 续传;先用现有 208 万行验证列式迁移,不重新读取上游 |
ops_jst_orders |
112 万 | 1.7 GiB | 售后费用 | 与订单明细、退款表作为同一激活组迁入 ClickHouse |
ops_jst_order_item |
158 万 | 1.7 GiB | 售后费用 | 同上 |
ops_jst_return_refund_info |
113 万 | 1.6 GiB | 售后费用、非京喜退款 | 同上 |
ops_jst_return_refund_item |
135 万 | 1.3 GiB | 售后费用、非京喜退款 | 同上 |
ops_jst_sku |
50 万 | 778 MiB | 商品资料及订单行补充 | 作为高频维表留在 MariaDB |
ops_work_order_form_value |
41 万 | 436 MiB | 协同工单当前态 | 留在 MariaDB,继续观察增速 |
js_fys_ticketing_install_view |
24 万 | 400 MiB | 安装明细 | 留在 MariaDB;达到阈值后再单独迁移 |
前三个可清理的 MariaDB 原始层合计约 38.2 GiB,但只能在归档对象存在、对象大小/逻辑校验通过、页面或同步器不再读取且获得生产删除授权后清理。当前迁移和部署不会自动删除这些表。
页面与数据层映射
代码当前登记 51 个固定上游数据集,另有 PostgreSQL 连接器动态登记的店铺账号维表。完整逐数据集策略由 config/business_data.php 强制登记;新增固定数据集若未登记,business-data:plan 返回失败。
| 页面/业务域 | 数据集 | 目标层 | 说明 |
|---|---|---|---|
| 公司、店铺、商标 | fys_companyInfo、fys_shopInfo、fys_TrademarkInfo |
MariaDB 维表 | 行数小、点查与关联频繁 |
| 店铺经营报表 | store_report_lifecycle_rows + store_report_daily_rows |
MariaDB 维表/汇总 | 页面已改为共享日汇总,不依赖 runtime/bills.sqlite,不扫描原始账单 |
| 普通商品资料 | jst_sku + 采购价镜像 |
MariaDB 维表/服务表 | 约 55 万商品仍适合行存筛选;保留现有完整导出校准 |
| 推广费用 | ads_itemid_sales_cost_ratio_v2_di |
ClickHouse 事实表 | jst_skumap 不再作为页面就绪条件 |
| 售后费用、非京喜退款 | 订单、订单明细、退款主表、退款明细 | ClickHouse 事实表 + MariaDB SKU/仓库/店铺维表 | 四张事实表必须成组对账和切换,不能逐张切换造成跨库半成品 |
| 原始账单 | jst_originalBillInfo |
ClickHouse + Parquet | 当前无页面直读;先迁移已保留部分验证压缩率和恢复能力 |
| 营销账户账单 | 淘宝、拼多多、阿里巴巴、京东账单 | MariaDB 服务表 | 当前单表约 2 万至 21 万行,先保留;按增速和查询成本复评 |
| 供应商应付、板材、封边条、库存 | 对应 finance_*、pm_*、结算表 |
MariaDB 服务表/汇总 | 当前规模可控;不做无收益的跨库改写 |
| 安装明细 | 安装视图与明细 | MariaDB 服务表 | 当前约 408 MiB,页面需要稳定分页与详情点查 |
| 协同工单、小额打款 | 工单主/子/表单/映射 | MariaDB 当前态 | 更新频繁且需要当前态精确读取 |
| 快递资料 | 快递、网点、账户、扩展 | MariaDB 维表 | 小型主数据 |
| 动态店铺账号数据集 | database_4 动态登记表 |
MariaDB 外部维表 | 未经单独评审不得自动进入列式层 |
已实现的安全边界
business-data:plan只读输出固定数据集、页面用途、目标层、同步策略、当前表体量与迁移阶段;固定数据集漏配或多配即失败。business-data:backfill-columnar <dataset>只从现有 MariaDB 镜像按_mirror_key断点读取,不连接上游;读取范围固定在任务启动时的_mirror_synced_at截止点。- ClickHouse 表使用
ReplacingMergeTree,以(_mirror_generation, _mirror_key)为排序/去重键,并按业务键哈希固定分为 32 个分区;同一代次内的业务键更新会被正确折叠,不同代次会同时保留。建表检查发现旧表缺少代次键时会拒绝继续回填,必须先以影子表完成可回滚修复。 - 回填结束同时核对截止点精确行数、ClickHouse
FINAL行数和唯一键数;不一致只记录失败,不切换页面。 --archive在对账后把 32 个哈希分区分别输出为 Parquet,经现有私有七牛 S3 磁盘流式上传;每个对象记录路径、行数、字节数和逻辑校验值。- 本阶段没有“激活”或“删除”命令。页面切换与 MariaDB 清理必须在各 Repository 的 ClickHouse 查询完成真实页面验收后另行提交,避免有人只执行一条命令就绕过保护门。
- ClickHouse 凭据只生成在服务器权限为
600的runtime/clickhouse.env;Git、PR、日志和命令参数中不出现密码。
执行顺序
以下是上线后的运维顺序,不是普通开发机操作。生产迁移、回填、归档和删除分别需要对应授权。
# 1. 只读核对配置与现状
docker exec -u www-data caiwu_app php artisan business-data:plan
# 2. 用已经暂停的原始账单部分数据验证 ClickHouse,不访问上游
docker exec -u www-data caiwu_app php artisan business-data:backfill-columnar \
operations_replica.rpt.jst_originalBillInfo --batch=5000 --max-batches=20
# 3. 对账完成后分区归档;仍不会删除 MariaDB 原表
docker exec -u www-data caiwu_app php artisan business-data:backfill-columnar \
operations_replica.rpt.jst_originalBillInfo --batch=5000 --max-batches=0 --archive
随后按以下顺序推进:
- 记录 ClickHouse 实际压缩率、回填吞吐、CPU/内存峰值和对象存储归档大小;
- 为推广费用 Repository 增加列式读取,在相同筛选下对比首页、第二页、末页、金额汇总与无数据;
- 将订单/退款四张事实表作为一个激活组迁移并完成同口径页面验收;
- 将店铺报表原始层迁入 ClickHouse,确认日汇总可从归档或列式原始层重建;
- 至少观察一个完整增量周期,再提出精确旧表清理清单、备份位置和回滚方案;
- 获得删除授权后才逐表清理 MariaDB,清理后再次执行页面与归档恢复抽查。
容量与告警建议
- MariaDB 镜像总量达到 80 GiB 或单表达到 5 GiB 时告警,不再自动扩大索引;
- ClickHouse 数据卷达到 70% 时暂停新回填,优先确认归档与旧代次清理;
- 对象存储按
数据集/代次/哈希分区保留不可变对象,清单写入business_data_archives; - 同步状态的
running只代表任务游标,不代表页面已经切换;页面切换必须另有active_generation和验收记录; - 上游断线时保留 MariaDB/ClickHouse 上一代数据,不由 HTTP 请求回退直连上游。
本地协议验证
2026-09-07 使用与部署配置相同的 ClickHouse 26.8.2.7-alpine 完成两级验证:应用 HTTP 客户端健康检查成功;同一 _mirror_key 写入两个版本后,FINAL 只返回版本号较大的 1 行;金额为最新值;SELECT ... FORMAT Parquet 返回标准 PAR1 文件头。随后使用 2 行隔离 SQLite 镜像走通完整命令,结果为 exit=0 / phase=verified / source=2 / target=2 / archives=32 / files=32 / parquet_magic=50415231。验证结束已删除 ClickHouse 临时表和临时归档,并停止本地容器;未连接生产数据。
ClickHouse 官方说明中,MergeTree 数据以不可变数据片段写入并在后台合并;ReplacingMergeTree 的查询在需要即时去重时必须使用 FINAL。S3 表函数与 Parquet 格式支持从列式表导出和后续重建,本方案据此把服务存储与不可变归档分开: