feat: 根目录文档、脚本、gitignore

This commit is contained in:
2026-10-08 16:15:33 +08:00
commit e98660ce4e
284 changed files with 26838 additions and 0 deletions
+175
View File
@@ -0,0 +1,175 @@
# -*- coding: utf-8 -*-
"""200 zentao_dev 只读导出 → 161 zentao_dev_2026 导入
源库只执行 SELECT/SHOW,绝不写入;流式游标 + 分批插入。
"""
import io, sys, time, re, pymysql
from pymysql.cursors import SSCursor
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
SRC = dict(host='192.168.3.200', user='root', password='PX4fTAAsJ#T!1',
database='zentao_dev', charset='utf8mb4', connect_timeout=15,
read_timeout=900, write_timeout=900)
DST = dict(host='192.168.1.161', user='devgps', password='dev@2021GPS',
charset='utf8mb4', autocommit=False, connect_timeout=15,
read_timeout=900, write_timeout=900)
TARGET_DB = 'zentao_dev_2026'
BATCH = 500
# 日志大表只建结构不拷数据(用户拍板 2026-07-29)
SKIP_DATA = {'zt_action', 'zt_actionrecent'}
def log(msg):
print(f'[{time.strftime("%H:%M:%S")}] {msg}', flush=True)
src = pymysql.connect(**SRC)
dst = pymysql.connect(**DST)
scur = src.cursor()
dcur = dst.cursor()
# 目标库会话加速+防约束干扰+放大单包上限
dcur.execute('SET SESSION foreign_key_checks=0')
dcur.execute('SET SESSION unique_checks=0')
try:
dcur.execute('SET SESSION max_allowed_packet=1073741824')
except Exception:
pass
dcur.execute(f'CREATE DATABASE IF NOT EXISTS `{TARGET_DB}` DEFAULT CHARACTER SET utf8mb4')
dcur.execute(f'USE `{TARGET_DB}`')
dst.commit()
log(f'目标库 {TARGET_DB} 就绪')
# 源库表清单(仅 BASE TABLE)
scur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema='zentao_dev' AND table_type='BASE TABLE' ORDER BY table_name")
tables = [r[0] for r in scur.fetchall()]
log(f'共 {len(tables)} 张表')
# 1) 建表结构(只建目标库缺失的表;已存在的不动——配合断点续跑,已拷数据不丢)
dcur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema=%s", (TARGET_DB,))
existing = {r[0] for r in dcur.fetchall()}
created = 0
for t in tables:
if t in existing:
continue
scur.execute(f'SHOW CREATE TABLE `{t}`')
create_sql = scur.fetchone()[1]
create_sql = re.sub(r' DEFINER=`[^`]+`@`[^`]+`', '', create_sql)
dcur.execute(create_sql)
created += 1
dst.commit()
log(f'表结构就绪(新建 {created} 张,复用 {len(tables)-created} 张)')
def row_size(row):
s = 0
for v in row:
if v is None:
continue
if isinstance(v, (bytes, bytearray)):
s += len(v)
elif isinstance(v, str):
s += len(v.encode('utf-8', 'ignore'))
else:
s += 16
return s
MAX_PACKET = 12 * 1024 * 1024 # 单包限 12MB(161 max_allowed_packet=64MB,留足余量)
def copy_table(t, attempt):
"""单表复制:断线重连 + 按字节分包;重试前清空目标表保证幂等"""
global src, dst, scur, dcur
if attempt > 0:
time.sleep(3)
for conn in (src, dst):
try:
conn.close()
except Exception:
pass
src = pymysql.connect(**SRC)
dst = pymysql.connect(**DST)
scur = src.cursor()
dcur = dst.cursor()
dcur.execute('SET SESSION foreign_key_checks=0')
dcur.execute('SET SESSION unique_checks=0')
try:
dcur.execute('SET SESSION max_allowed_packet=1073741824')
except Exception:
pass
dcur.execute(f'USE `{TARGET_DB}`')
dst.commit()
dcur.execute(f'DELETE FROM `{t}`')
dst.commit()
read_cur = src.cursor(SSCursor)
read_cur.execute(f'SELECT * FROM `{t}`')
cols = [d[0] for d in read_cur.description]
col_sql = ','.join(f'`{c}`' for c in cols)
ph = ','.join(['%s'] * len(cols))
insert_sql = f'INSERT INTO `{t}` ({col_sql}) VALUES ({ph})'
n = 0
batch = []
batch_bytes = 0
for row in read_cur:
batch.append(row)
batch_bytes += row_size(row)
if len(batch) >= BATCH or batch_bytes >= MAX_PACKET:
dcur.executemany(insert_sql, batch)
dst.commit()
n += len(batch)
batch = []
batch_bytes = 0
if batch:
dcur.executemany(insert_sql, batch)
dst.commit()
n += len(batch)
read_cur.close()
return n
# 2) 逐表复制数据(源只读流式;SKIP_DATA 只留空表结构;已拷完的表自动跳过=断点续跑)
total_rows = 0
for t in tables:
if t in SKIP_DATA:
log(f'{t}: 跳过数据(仅结构,0 行)')
continue
scur.execute(f'SELECT COUNT(*) FROM `{t}`')
src_cnt = scur.fetchone()[0]
dcur.execute(f'SELECT COUNT(*) FROM `{t}`')
if dcur.fetchone()[0] == src_cnt:
log(f'{t}: 已拷过({src_cnt} 行),续跑跳过')
total_rows += src_cnt
continue
t0 = time.time()
last_err = None
for attempt in range(3):
try:
n = copy_table(t, attempt)
break
except pymysql.err.OperationalError as e:
last_err = e
log(f'{t}: 第 {attempt+1} 次失败 {e},重连重试')
else:
raise last_err
total_rows += n
log(f'{t}: {n} 行 ({time.time()-t0:.0f}s)')
log(f'数据复制完成,总 {total_rows} 行')
# 3) 逐表行数对拍(SKIP_DATA 预期 0 行,单独标注)
mismatch = []
for t in tables:
scur.execute(f'SELECT COUNT(*) FROM `{t}`')
s = scur.fetchone()[0]
dcur.execute(f'SELECT COUNT(*) FROM `{t}`')
d = dcur.fetchone()[0]
if t in SKIP_DATA:
log(f'{t}: 源 {s} 行 → 目标 {d} 行(按计划跳过)')
elif s != d:
mismatch.append((t, s, d))
if mismatch:
log(f'!!! 行数不一致 {len(mismatch)} 张: {mismatch}')
else:
log(f'对拍通过:{len(tables)} 张表行数全部一致')
# 4) 抽查中文往返
dcur.execute('SELECT id, title FROM zt_story WHERE id=6566')
log(f'抽查 zt_story 6566: {dcur.fetchone()}')
dcur.execute('SELECT COUNT(*) FROM zt_perf_config')
log(f'抽查 zt_perf_config 行数: {dcur.fetchone()[0]}')
src.close(); dst.close()
log('全部完成')