legacy-db-actual.md 26.7 KB

老库实测结论(只读账号直连)

本文档记录 2026-10-09 用只读账号对老库做的实测。所有数字均为查询所得, 不是推断。与 <code>README.md</code> §4.2 的"待办清单"一一对应。

复现方式:<code>tools/legacy_db.py</code>(连通性/表清单/DDL)、 <code>tools/compare_dup_tables.py</code>(重名表结构比对)。 凭据在 私密/legacy-ro.env(已 gitignore)。

⚠️ 本文件里会出现源实例地址与账号名——它们是"实测对象"的标识,不写就说不清结论是 对哪台库得出的(该账号是只读的,没有口令也无法利用)。口令不在本文件、也不在仓库 任何文件里,只存在于 私密/legacy-ro.env。别为了"方便复现"把口令补进来。

0. 连库方式:CLI 不可用,必须走 PyMySQL

本机 Homebrew 的 mysql 客户端是 26.7,已移除 mysql_native_password 插件, 而老库账号正是该认证方式,直接连报:

ERROR 2059 (HY000): Authentication plugin 'mysql_native_password' cannot be loaded

PyMySQL 用纯 Python 实现了该插件,故统一走 PyMySQL。 (备选:/opt/homebrew/opt/mysql-client@9.6 的插件目录里仍有该 .so,但需显式指定 plugin-dir。)

只读承诺的执行情况:全程仅发 SELECT / SHOW / 读 information_schema, 未做任何写操作。账号本身也确为只读(见 §2 授权)。

1. 六库同实例 ✅,但库名有严重误导性

先纠正一处我自己的误判:各模块配置文件里写的 druid.url(指向 rm-2zeq6z9f00w52tcq7jo)与代码实际面向的库不符,不能作为迁移源依据。 判定迁移源的可靠方法是:看代码引用的表在哪个库里真实存在。

按此做的模块 × 库命中矩阵(去分表后缀):

模块 引用表数 zt_sys zt_park zt_order zt_order_n zt_bill zt_charge
zteits-sys 32 30 (94%) 1 1 1 – –
zteits-park 127 – 110 (87%) 2 3 1 –
zteits-order 91 – – 9 86 (95%) – –
zteits-pay 86 – – 79 (92%) 8 – –
zteits-bill 55 – – 1 – 54 (98%) –
zteits-charge 4 – – – – – 4 (100%)

每个模块都有唯一且高命中率的归属库 → 六库全在 renniting-prod-net.mysql.zhangbei.rds.aliyuncs.com 这一台实例上, INSERT INTO ... SELECT 同实例搬迁的前提成立。

⚠️ 但有两个必须记住的命名陷阱

  1. zt_order 里装的是 pay 域,不是 order 域。 zteits-pay 引用的 86 张表里 79 张在 zt_order(92%); 而 order 域的表在 zt_order_n。库名与业务域刚好错位。
  2. zt_order 里还混着一份 2018 年的订单归档。 该库的 td_b_order / td_b_pay_order / td_b_order_detail_park 末行 create_date 分别是 2018-04-08 / 2018-04-26 / 2018-03-27—— 冻结了 8 年;而同库的 td_c_cust_person(客户)今天仍在写入。 所以 zt_order = pay 活库 + 2018 订单死档案, 搬迁时不能整库照搬,要按表区分。

建议:新库按业务域命名,不要沿用 zt_order/zt_order_n 这种 与内容错位的旧名(方案 §4.4 定的 parking_cloud 单库 + 前缀正是为此)。

对照:另一台实例是另一个环境

rm-2zeq6z9f00w52tcq7jo 上有 zt_sys/zt_park/zt_order/zt_pay/zt_bill/zt_business ——库名与张北部分重叠但内容不同(例如它有 zt_pay,张北没有)。 本次迁移不涉及该实例(除 zteits-business 的 11 张表,见 §7)。

2. 只读账号的授权范围(已核实,覆盖完整)

GRANT SELECT, LOCK TABLES, SHOW VIEW ON `zt_sys`.*     TO 'readonly'@'%'
GRANT SELECT, LOCK TABLES, SHOW VIEW ON `zt_park`.*    TO 'readonly'@'%'
GRANT SELECT, LOCK TABLES, SHOW VIEW ON `zt_order`.*   TO 'readonly'@'%'
GRANT SELECT, LOCK TABLES, SHOW VIEW ON `zt_order_n`.* TO 'readonly'@'%'
GRANT SELECT, LOCK TABLES, SHOW VIEW ON `zt_bill`.*    TO 'readonly'@'%'
GRANT SELECT, LOCK TABLES, SHOW VIEW ON `zt_charge`.*  TO 'readonly'@'%'
GRANT SELECT, LOCK TABLES, SHOW VIEW ON `xxl_job`.*    TO 'readonly'@'%'
GRANT PROCESS, REPLICATION SLAVE, REPLICATION CLIENT ON *.*

确为只读,且正好覆盖迁移所需的六个库——授权范围与 §1 的归属矩阵完全吻合, 说明这个账号就是为本次六库合一准备的。

⚠️ 澄清一个我此前的误报:zt_pay 不在这份授权里,但它不是缺失—— pay 域的表物理上就在 zt_order(§1)。所以"pay 库结构拿不到"这个卡点 不存在,无需额外授权。

3. 六库概览

库 表数 近似行数 数据大小 非 utf8mb4 表
zt_park 387 1,193,632,380 414.4 GB 368
zt_order_n 112 409,273,992 260.3 GB 99
zt_order 108 293,091,812 88.8 GB 106
zt_bill 60 5,656,547 1.35 GB 55
zt_sys 38 1,775,497 0.58 GB 38
zt_charge 4 16 0.1 MB 0
合计 709 ~19 亿 ~747 GB 666

要点:

  • 规模远超预期:747 GB / 19 亿行,且 19 亿里有 16 亿在 zt_park。 迁移窗口、分批策略都要按这个量级重估,不能用"小库直接搬"的假设。
  • zt_charge 基本是空库(4 表 / 16 行)→ 充电业务实际未在这套库上跑, 或跑在 dev_charge。
  • TABLE_ROWS 是 InnoDB 估算值(分表族会被逐张累加),量级可信,精确值需逐表 COUNT(*)。
  • 非 InnoDB 表 0 张(无视图、无 MyISAM),搬运无引擎障碍。

4. 重名表:19 组,7 组可直接合并、12 组必须处理

4.1 ✅ 结构完全一致(列+索引+排序规则全同)—— 7 组

表 库
income_temp zt_order_n、zt_park
month_dchz zt_order_n、zt_park
table_15_free zt_order_n、zt_park
td_b_settlement zt_order、zt_order_n
td_b_settlement_org zt_order、zt_order_n
td_b_settlement_share_record zt_order、zt_order_n
td_p_parking_share_order_temp zt_order、zt_order_n

4.2 ⚠️ 结构不一致 —— 12 组

差异分三类,处理方式完全不同:

表 差异性质 活跃份
parking_lot(4 库) zt_bill == zt_order(列全同,仅索引差);zt_order_n / zt_park 是超集(多 area_type/is_special/parent_pl_no/pl_category/push_state/support_etc 等 8~10 列) zt_park(3040 行,08-23 更新)
sys_org(4 库) zt_sys == zt_park == zt_order_n;zt_order 是旧版(short_name 非空、缺 index_org_id) zt_sys(204 行);zt_order 为 0 行
park_in_out_record zt_park 是超集(多 4 列 + 6 个索引) zt_park(9674 万行 / 32 GB,今天更新);zt_order 仅 115 万行 / 231 MB,长期未更新
t_vip_card_cust_info zt_bill 多 3 列(cust_name/cust_phone/parent_cust_card_no) zt_bill(78816 行,今天更新);zt_order 1892 行,陈旧
tb_b_park_income_day zt_order_n 多 2 个索引 zt_order_n(22.7 万行,今天 02:00);zt_order 1796 行
td_b_settlement_record zt_order_n 多 3 列(case_refund_*),check_status 默认值不同(左空/右 100) zt_order_n(8604 行,今天 02:01);zt_order 90 行
td_b_settlement_serial zt_order_n 多 settlement_month 两侧都近乎空
td_b_wx_config zt_order_n 多 cert_name;org_id 类型 int(30) vs int(10) 两侧各 19/23 行
td_c_coupon_person cust_id 索引不同 —
td_c_cust_person ⚠️ 方向相反:zt_order 反而多 4 列(etc_open_state/etc_protocol_state/freeze_date/freeze_state),deivce_type 默认值不同 zt_order(82691 行,今天 10:59);zt_order_n 26367 行,陈旧
tp_e_employee_charge zt_park 多 7 列(eqp_* 设备字段) zt_park(2217 行,今天 10:55)
tp_p_berths zt_park 多 car_number_color zt_park(30162 行,今天 11:01)

由这 12 组得出的关键判断

重名表其实是两种性质,必须分开处理:

  1. 跨库冗余副本(sys_org、parking_lot、park_in_out_record、 t_vip_card_cust_info、tp_p_berths、tp_e_employee_charge) —— 为了在本库做 JOIN 而复制过来的只读副本,长期不同步、结构已漂移。 典型如 sys_org:zt_sys 有 204 行,zt_park/zt_order_n 各只有 20 行, zt_order 更是 0 行 —— 副本不仅滞后,还是残缺的。

  2. 新老两代同表(zt_order vs zt_order_n 的结算/收入系列) —— 且活跃方向不一致:tb_b_park_income_day、td_b_settlement_record 活的是 zt_order_n,而 td_c_cust_person 活的是 zt_order。 没有任何一条统一规则可判,必须逐表定性。

⚠️ parking_lot / sys_org 这类"字段集是超集/子集"的差异, 合库时不能取并集(会破坏另一侧代码),也不能随意取一侧(会丢列)。

归属可由 §1 的矩阵推断:parking_lot 的活份在 zt_park(3040 行、有更新), sys_org 的活份在 zt_sys(204 行,其余库只有 20 行甚至 0 行)。 也就是说 zt_bill/zt_order/zt_order_n 里的那几份都是跨库只读副本—— 合库时保留主份、丢弃副本,并把原来的跨库 JOIN 改成走 parking-module-api (正好落回红线第 7 条"禁止跨模块直接访问数据库")。 但副本上的独有列不能直接丢——那说明有代码在写副本,需先查清调用方。

5. 字符集:666/709 张不是 utf8mb4,另有 30 张 utf8_bin 排序规则冲突

排序规则 表数 风险
utf8_general_ci 635 3 字节 utf8,存不下 emoji/生僻字(4 字节)
utf8mb4_general_ci 39 目标形态
utf8_bin 30 ⚠️ 见下
utf8mb4_unicode_ci 4 与 general_ci 混用需统一
latin1_swedish_ci 1 ⚠️ zt_order_n.td_b_pay_blncac_wx,含中文必乱码

⚠️ utf8_bin 造成的真实 JOIN 障碍

parking_lot 在 zt_bill / zt_order / zt_order_n / zt_park 四个库里都是 utf8_bin, 而周边表的同名列是 utf8_general_ci:

表 列 排序规则
parking_lot pl_no utf8_bin
park_in_out_record pl_no utf8_general_ci
tp_p_berths pl_no utf8_general_ci

MySQL 5.7 下两者相 = 比较会报 Illegal mix of collations。 即:合库统一排序规则后,一批老系统里"本来就跑不通"的 JOIN 会开始能跑 —— 这与 Dubbo 那 8 处"合并顺手修好"属于同一类行为变化,回归时要专门过。

另外 zt_park 里有一批带 del20181116 前缀的历史备份表(如 del20181116td_g_guide_park_berths),而代码里仍在按老名字 td_g_guide_park_berths 查(见 §7)——表被改名删了,代码没跟。

6. 分表族:实测确认,共 228 张物理表(2 个族)

🔁 2026-10-10 修正:本节原写「230 张、3 个族」,把 parking_running_statistic2023/2025 也算成了族。那是错的—— 判据见下。真正的分表族只有 2 个、228 张;那两张是人工快照, 已移入 §8 的嫌疑清单(合计约 133 GB,见 §6.1)——2026-10-10 已拍板不搬。

什么叫分表族:表名由代码在运行期拼出来("tp_p_eqp_log" + yyyyMM)。 因此判据是代码里存不存在 "前缀" 这个字面量——静态表在 SQL 里永远只以裸名字 出现(FROM parking_running_statistic),Java 里不会有它的引号字面量。 这条判据已固化进 tools/audit_junk_tables.py 的 sharded_in_code(), 并做双向自检(登记的都真在拼 / 在拼的都登记了,任一不满足即 exit 2)。

库 表名前缀 后缀 张数 范围 代码拼装处
zt_park tp_p_eqp_inout_record yyyyMM 115 201709 ~ 202703 EqpInOutParkStatisticServiceImpl
zt_park tp_p_eqp_log yyyyMM 113 201711 ~ 202703 TpPEqpLogDaoImpl / EqpLogDaoImpl / EqpHeartBeatJob

未来到 2027 年 3 月(说明有预建表任务)。 合库时必须连着分表族一起搬,且不能把 ${...} 写死; ParkingLotUseStatisticDaoImpl:169 那个"用当前时间拼表名"的存量缺陷也在此列。

6.1 ⚠️ 被误判为族的 parking_running_statistic*:整个家族约 183 GB

判据(§6)改正后,这个前缀下的物理表没有一张是分表族成员,全是静态表; 带后缀的那几张贴是人工滚动快照:

表 估算行数 大小 建表时间 代码引用 处置
zt_park.parking_running_statistic 1.05 亿 20.5 GB 2026-04-17 12:05 ✅ 有(2 个 XML)——全家族唯一被代码读的那张 随迁
zt_park.parking_running_statistic2023 3.86 亿 83.0 GB 2024-05-22 无 不搬(§10.1)
zt_park.parking_running_statistic2025 2.41 亿 50.5 GB 2026-04-17 12:03 无 不搬(§10.1)
zt_park.parking_running_statistic_his 1.25 亿 25.3 GB 2022-07-27 无 暂定搬(未确认)
zt_park.parking_running_statistic_chifeng_2019021 1947 万 3.6 GB 2022-07-27 无 暂定搬(未确认)
zt_park.parking_running_statistic_chifeng 1.36 万 2.5 MB 2022-07-27 无 暂定搬(未确认)

怎么认出它是滚动快照的:无后缀那张的建表时间与 …2025 只差 2 分钟 (12:03 / 12:05),是典型的「把老表 CREATE TABLE … SELECT 出来存档、再重建一张空表接着写」。 于是带日期的贴是滚动前的历史数据,而代码只读无后缀那张——这也解释了为什么年度 不连号(只有 2023、2025,没有 2024):它不是分区方案,是人工存档。

全家族合计约 183 GB(占六库 747 GB 的近四分之一),其中 133.5 GB 已拍板不搬。

⚠️ _his(25.3 GB)、_chifeng_2019021(3.6 GB)、_chifeng(2.5 MB)与已拍板不搬的 那两张是同一形态,但它们连嫌疑判据都没命中(名字里没有日期后缀), 也没拿到处置指示——所以仍按"先全搬"。要一并省掉的话说一句即可, 再省约 29 GB(改写一处 DO_NOT_MIGRATE 就行,见 §10.1)。

7. 代码引用、但六库中都不存在的表(40 张)

这份清单比"表清单"更有用——它直接指出哪些代码在查不存在的表:

模块 张数 表 定性
zteits-business 11 td_c_business_*、order_business_coupon* 在另一实例的 zt_business(§1 对照)→ 属方案 §4.6 的随迁例外范畴
zteits-pay 5 car_search_history、cust_person_credit、td_b_bills_days、td_b_park_card_cust、td_b_park_card_rel ⚠️ pay 的归属库是 zt_order(§1),这 5 张在六库里都真的没有 → 要么是死代码,要么在另一实例。需逐张确认
zteits-park 17 hub_*(6)、nbtemp、generate_test_table、inpark_log、outpark_log、eqp_in_time、eqp_out_time、park_pay_log、parking_lot_berths、parking_lot_credentials、charger_assess_result_log、td_g_guide_park_berths 混合:部分是从未建表的死代码(generate_test_table/nbtemp);td_g_guide_park_berths 是表已改名——同库存在 del20181116td_g_guide_park_berths,代码没跟着改;hub_* 疑似未上线对接
zteits-order 4 inspection_log、order_arrearage_edit_log、order_park_bjxc_success、park_in_time 待定
zteits-sys 2 tb_work_order、tb_work_order_process 工单表,疑似属其他系统

对 P2 的意义:zt_business 那 11 张与方案 §4.6 已识别的"business 随迁"对上了, 属已规划项;其余 29 张必须在搬迁前逐张定性, 否则会把"本来就坏的代码"原样搬进新系统、继续 silently 失败。

已确认的一例:td_g_guide_park_berths —— 表在 2018-11-16 被改名成 del20181116td_g_guide_park_berths,代码仍在查老名字。 这类"改名未同步"很可能还有别的,值得用同一手法(比对物理表名单)继续挖。

8. 废弃/备份表:逐张确认完毕(2026-10-10)

逐张确认已做完,产物是 <code>../docs/废弃表逐张确认.md</code> (由 <code>tools/audit_junk_tables.py</code> 生成,可随时重跑)。 本节只记口径与结论,逐张明细看那份报告。

8.1 确认结果:61 张,按判据分布

判据 张数
_bak 备份 16
名含 temp(独立词) 12
_<8位日期> 后缀 11
_copy 副本 8
_<4位数字> 后缀 6
裸<数字>后缀(无下划线,见 §6.1) 3
名含 test(独立词) 2
del 前缀带日期 2
_<6位年月> 后缀 1

按库:zt_park 27、zt_order_n 13、zt_sys 8、zt_order 8、zt_bill 5 (zt_pay / zt_charge 无命中)。

⚠️ 本节原列的「del<日期> 2 张、_<日期> 12 张、_copyN 8 张、_bak 14 张、 temp 词 13 张、test 词 1 张」是按旧口径数的粗数(未排除分表族、未做词边界), 以 8.1 表为准。

8.2 确认方法(为什么不能只看名字)

三条证据缺一不可,全部由脚本产出:

  1. 精确 COUNT(*)(不是 TABLE_ROWS 估算)——估算对大表可差数十个百分点, 而"这张表到底是不是空的"恰恰是关键判断;
  2. 代码引用——在 parking-module-* 里搜表名。分表位置(FROM/JOIN/… 之后 或 Java 字面量,硬证据)与疑似(其余命中)两级,两级都贴命中行原文;
  3. 建表时间——_bak 不等于陈旧:sys_op_advice_bak 是 2026-02-25 建的 (109 万行 / 200 MB,与无后缀的 sys_op_advice 只差 4 秒——同一类滚动操作)。 近期建的备份表很可能是运维在用的。(这条问对了:2026-10-10 业务方确认那是备份, 不用管 → §10.1。)

8.3 结论分布与处置

结论 张数 处置
🔴 有表位置代码引用 6 必须随迁——是活表,只是名字像残留
🟠 疑似引用(非表位置) 1 zt_park.temp——已人眼核对,6 处命中全是 SQL 别名/foreach item,确认无引用;它 7 行,故处置同下下行
🟡 有数据但无引用 46 先全搬(§10 决策;老库仍在,可逆)
🟡 无引用但行数未知 1 parking_running_statistic_chifeng_2019021(3.6 GB,超限未数)。同族的 _his(25.3 GB)连判据都没命中——这两张都没拿到处置指示,仍按"先全搬"(§6.1 末注)
⬛ 已拍板不搬(空表 4 + 备份 3) 7 不搬(新库建表脚本里也不要有)。4 张空表不搬零代价;3 张有数据的备份见 §10.1

小计 6 + 1 + 46 + 1 + 7 = 61 √ (⚠️ 原表写的是 6 + 2 + 1 + 48 + 4:其中「🟠 近期创建 2 张」已由业务方答复, 转成 ⬛;🟢 4 张空表也归入 ⬛ 以便一处看全。分类口径变了,总数不变。)

🔴 的 6 张(都不许按名字处置,明细见报告): zt_order.td_b_temp_parklot、zt_order.td_p_parking_share_order_temp、 zt_order_n.td_p_parking_share_order_temp、zt_park.duty_roster_base_temp、 zt_park.tp_p_eqp_inout_record_temp、zt_sys.sys_export_temp_file。

⚠️ 这 6 张的存在恰恰说明「名字像临时表」与「是不是临时表」无关: td_b_temp_parklot 是停车场临时信息表(insert into …),duty_roster_base_temp 是排班导入的中间表(TRUNCATE + 搬进 duty_roster_base),都在日活路径上。

⚠️ 反方向的风险更大:也不能仅凭名字搬。把 sys_org_copy 这类历史副本 原样搬进新库,它就永久留在新库里——00 建库脚本与后来的建表语句都不知道它存在, 等于往新库注入一批无来源、无归属、以后没人敢删的表。这正是 §8.3 放行的 只有两类的原因:4 张空表(不搬零代价,判据是数据自证)与 3 张拿到明确拍板的备份(§10.1,有数据,判据是业务判断而非数据)。 第二类的风险方向相反——不搬会少数据,所以它必须有外部拍板留痕, 条目的理由串里写清"谁、何时、凭什么"(见 tools/audit_junk_tables.py 文件头)。

另注:month_dchz、table_15_free 这类"看着像临时表"的不是残留, 是被代码正常引用的业务表(且在两库结构一致)——名字判据一开始就没命中它们, 被误列进来过,这里明确排除。

9. 闭环情况与剩余事项

9.1 已闭环

原疑问 结论
迁移源是哪台实例? ✅ 张北 renniting-prod-net(模块×库命中矩阵 87~100%,见 §1)
zt_pay 结构拿不到? ✅ 不存在此问题——pay 域的表就在 zt_order(§1)
六库是否同实例? ✅ 是,同实例可直接 INSERT INTO ... SELECT
zt_order / zt_order_n 谁是谁? ✅ zt_order_n = order 域活库;zt_order = pay 域活库 + 2018 订单归档(§1)
字符集是否都是 utf8mb4? ✅ 否,666/709 不是,含 30 张 utf8_bin + 1 张 latin1(§5)
分表族到底有多少张? ✅ 实测 228 张物理表 / 2 个族,到 2027-03(§6)。原写的 230 张 / 3 个族含误判,2026-10-10 修正
§8 那些废弃/备份表怎么处置? ✅ 61 张逐张确认完毕(2026-10-10,§8)→ 分层处置见 §8.3:7 张不搬(空表 4 + 备份 3),其余 54 张随迁。其中 3 张备份(约 133 GB)不再进入迁移体量(§10.1)

9.2 仍需你定夺

# 事项 说明
1 zteits-business 的 11 张表要不要随迁 它们在另一台实例的 zt_business,本次只读账号覆盖不到。按方案 §4.6,business 仅在"被核心模块运行时调用"时才随迁——需要拿它是随迁还是砍掉
2 zt_order 里那份 2018 订单归档怎么处理 是一并搬进新库(保留历史查询能力),还是只搬 pay 域活表、归档另行放置?这直接影响迁移体量(该库 88 GB,大部分是归档)
3 关键大表的精确行数 本次用 TABLE_ROWS 估算。定迁移窗口前,需对 zt_park 的几张大表取精确 COUNT(*)
4 §8 的废弃/备份表是否丢弃 ✅ 2026-10-10 已闭环:61 张逐张确认完毕,口径与处置见 §8;逐张明细见 docs/废弃表逐张确认.md(可重跑)

第 2 条影响最大:zt_order(88 GB)与 zt_order_n(260 GB)合计 349 GB, 若归档可剥离,迁移体量能显著下降。

10. 已确认的决策(2026-10-09)

# 事项 决策 影响
1 zteits-business 随迁。其 11 张表在另一实例的 zt_business,需在该实例开一个只读账号(只需 SELECT ON zt_business.*),填入 私密/legacy-ro.env 的 LEGACY_DB_BUSINESS_* 未提供前,六库比对不受影响;提供后补做 business 的结构比对
2 zt_order 里的 2018 年订单归档 一并搬进新库(零数据丢失优先;老库仍作只读回滚路径) 迁移体量包含这部分,约为数十 GB

决策 2 意味着新库会携带一批"新代码永远不会查"的表。 若后续确认体积不可接受,可从老库单独导出这批表另存,而不影响新库结构—— 因此这个决策是可逆的,现在按"先全搬"执行。

10.1 废弃/备份表处置(2026-10-10)

§9.2 第 4 项闭环后的决定,口径见 §8.3:

# 事项 决策 影响
3 §8 的 61 张废弃/备份表 按 §8.3 分层处置:🔴 6 张随迁(是活表);🟠 1 张已核为无引用;🟡 47 张先全搬;⬛ 7 张不搬(空表 4 + 备份 3) 与决策 2 的"零数据丢失优先"一致;⬛ 那 7 张不进 00 建库脚本
4 parking_running_statistic2023 / …2025(约 133 GB) ✅ 2026-10-10 业务方拍板:备份表,无需理会 → 不搬进新库 迁移体量最值得省的一块(§6.1);133 GB 不进 parking_cloud。数据留在老库(未销毁),需要时从老库单独导出
5 sys_op_advice_bak(2026-02-25 建,109 万行 / 200 MB) ✅ 同上,不搬 只有 200 MB,搬不搬都无所谓;按拍板走,与新库"少一批没人认领的表"更一致
6 parking_running_statistic_his(25.3 GB)/ _chifeng_2019021(3.6 GB)/ _chifeng(2.5 MB) 仍按"先全搬"——与决策 4 是同一形态(§6.1),但没拿到指示 要一并省掉约 29 GB 的话,把这三张加进 DO_NOT_MIGRATE 的 备份 层即可

落地在哪(这决定才是真的,报告只是生成物):

落地处 内容
tools/audit_junk_tables.py 的 DO_NOT_MIGRATE 唯一事实来源。值 = (层级, 理由),备份 层的理由串必须写清"谁、何时、凭什么"
同上,main() 的自检 逐条复核前提仍成立(两层共有:仍存在、仍无表位置引用;空表 层另有"仍是 0 行")。不成立就 exit 2
sql/migration/exclude_tables.txt 30 行(23 副本 + 7 不搬)→ mysqldump --ignore-table
sql/migration/99_verify.sql §1 NOT IN (...)——不排掉它会永远报红(那 7 张是有意不在目标库里的)
sql/migration/98_count_reconcile.sql 排除集同上;顺带把 zt_park 段最贵的两次 COUNT(*)(6.27 亿行)也省掉了

⚠️ 决策 3 里 🔴 那 6 张不能按"名字像临时表"处理:它们是日活路径上的真表 (如 td_b_temp_parklot、duty_roster_base_temp),误删会直接坏业务。 这正是 §8 一开始就警告的"不能仅凭名字删"。

⚠️ 决策 4/5 是唯一一批"知道有数据、仍然不搬"的表。它们的守卫比空表弱: 只能保证"代码里还是没人读它"这个前提,证明不了"这是你需要的备份"—— 那是业务判断,任何查询都答不了。所以改动它们时先回到这条决策,别只看脚本过不过。