从2.7到2.9的噩梦——记一次杰奇CMS全站数据迁移的兼容性事故
!bin/bash
“妈的,又是500,第几次了?”
我盯着屏幕上的报错,手里的烟已经烧到了过滤嘴,客户那边催得紧,说旧服务器下个月就到期,但新环境上的杰奇CMS 2.9始终跑不起来——数据是从老版本的2.7直接导过来的,而这两个版本的数据库结构差异,远比我想象的可怕。
迁移后访问速度断崖式下跌
迁移完的第三天,首页打开要8秒,后台登录直接超时,查了MySQL慢查询日志,发现罪魁祸首是jieqi_article_content这张表——老版本里content字段是LONGTEXT,新版本改成了MEDIUMTEXT,但关键是索引全部丢失。
直接给方案:
-- 重建核心索引,注意区分2.7和2.9的字段名差异 ALTER TABLE jieqi_article_content ADD INDEX idx_article_id (articleid), ADD INDEX idx_chapter_id (chapterid), ADD INDEX idx_siteid_lastupdate (siteid, lastupdate);
如果表行数超过500万,别用ALTER,直接创建新表再导入(下面章节表处理会讲方法)。
章节表过大,单表突破800万行
这是杰奇老用户的通病,2.7到2.8之间官方没做分表,2.9才开始推荐按书ID取模,但我们是迁移的数据,不能指望官方脚本。
最稳妥的改造方案(PHP + 定时任务):
// /api/split_article_table.php 手动触发一次
$book_id = (int)$_GET['bookid'];
$table_index = $book_id % 10; // 拆成10张子表
$new_table = "jieqi_article_content_{$table_index}";
// 建表语句基于原表结构,去掉自增改为复合主键
$sql = "CREATE TABLE IF NOT EXISTS {$new_table} LIKE jieqi_article_content_old";
mysqli_query($conn, $sql);
// 数据搬移(分批LIMIT避免锁表)
$offset = 0;
while (true) {
$sql = "INSERT INTO {$new_table} (articleid, chapterid, content, lastupdate)
SELECT articleid, chapterid, content, lastupdate
FROM jieqi_article_content_old
WHERE articleid = {$book_id}
LIMIT {$offset}, 500";
$result = mysqli_query($conn, $sql);
if (mysqli_affected_rows($conn) < 500) break;
$offset += 500;
usleep(500000); // 半秒间隔,降低IO
}
同时在jieqi_article_chapter表增加一个table_index字段,每次查询时先计算:
$table_idx = $bookid % 10;
$chapter_content = $db->query("SELECT * FROM jieqi_article_content_{$table_idx} WHERE articleid = {$bookid} AND chapterid = {$cid}");
阅读页加载卡顿,滑动翻页掉帧
迁移后最直观的体验问题,排查发现是每章都调用了jieqi_article_chapter里的全文缓存字段——旧版本有个cachedcontent,新版本改成了intro,但老数据里没这个字段值,于是每次都要回表查内容表。
解决方案: 阅读页直接走内容分表查询,不再依赖cachedcontent。
修改modules/article/view.php对应部分:
// 旧代码:$chapterContent = $chapter['cachedcontent'];
// 新逻辑:
$table_idx = $chapter['articleid'] % 10;
$rs = $db->getRow("SELECT content FROM jieqi_article_content_{$table_idx} WHERE chapterid = '".intval($chapter['chapterid'])."'");
$chapterContent = $rs['content'];
unset($rs);
如果阅读页还有卡顿,检查jieqi_system_online表——迁移过来的老数据里有大量session残留,会导致每次请求都查这个几千行的表,清空它:
TRUNCATE TABLE jieqi_system_online;
模板标签调用的兼容性坑
7的自定义标签到了2.9,很多模板变量名变了,比如{$article.content}在老版本是直接读的,2.9必须改成{php}echo getArticleContent($article['articleid']);{/php}。
最快排查方式: 在模板文件顶部临时加调试代码:
{php}var_dump(get_defined_vars());exit;{/php}
你会发现所有$article数组里的键名——老版本叫content,新版本叫description,逐个替换模板中的调用即可。
移动端适配直接崩了
老项目的模板是PC版+手机版两套,迁移后手机版访问首页没问题,但阅读页全是乱码——因为2.7的移动端模板调用了jieqi_pc_view模块,而2.9已经废弃了这个模块,统一走jieqi_mobile_view。
处理办法: 如果你不想重写移动端模板,可以做一个路由劫持:
// /modules/mobile/view.php 文件头部
if (isset($_GET['cid']) && isset($_GET['bid'])) {
// 直接复用PC版阅读页逻辑,但输出时强制走H5布局
define('JIEQI_MOBILE_TEMPLATE', 1);
include_once JIEQI_ROOT_PATH . '/modules/article/view.php';
exit;
}
然后在PC版阅读页的HTML模板里,用jQuery.ajax替换为原生移动端翻页API,避免加载整个PC框架。
缓存配置——这才是核心
迁移后最致命的其实是缓存,2.7默认用的是文件缓存,2.9推荐Redis,但旧数据里没有缓存key的前缀适配,导致每次改书章节都写不进去。
在config.php里这样设置:
// 强制使用Redis,并且key加前缀防止与旧数据冲突 JIEQI_CACHE_TYPE = 'redis'; JIEQI_CACHE_PREFIX = 'jq29_' . md5(JIEQI_DB_NAME) . '_';
然后在/includes/cache.php底部加一段兼容层:
function jieqi_cache_read($key) {
$key = JIEQI_CACHE_PREFIX . $key;
return jieqi_cache_read_legacy($key); // 调用底层
}
数据库定期维护——迁移只是开始
最后给个每天凌晨3点执行的系统任务脚本:
0 3 * * * root /usr/bin/php /var/www/jieqi/cron/optimize.php
最后说一句,杰奇2.7到2.9的迁移,表面上只是数据复制,实际是整个数据逻辑层的重构,上面的坑我踩了整整两周,但走完这套流程后,新站的响应时间从8秒降到了0.7秒,如果你正在迁移,别急着上线——先拿一台生产环境的备份机器做全量测试,特别留意optimize.php
<?php
// 清空一个月前的搜索关键词(杰奇的search_log表增长极快)
$db->query("DELETE FROM jieqi_search_log WHERE searchtime < " . (time() - 30*86400));
// 整理章节表碎片(分表后每张表都要)
for($i=0; $i<10; $i++) {
$db->query("ALTER TABLE jieqi_article_content_{$i} ENGINE=InnoDB");
}
// 每周末重建一次所有统计缓存
if(date('N') == 7) {
$db->query("REPAIR TABLE jieqi_article_ranking, jieqi_article_click");
}
chapterid的类型转换(2.7是int(10),2.9是bigint(20)),否则等到用户报错再改就晚了。



发表评论