“Warning: mysql_fetch_array() expects parameter 1 to be resource...” 凌晨两点,盯着监控屏上刺眼的红色告警,我手里的咖啡差点洒在键盘上,这是客户一个跑了五年的行业门户,帝国CMS搭建,文章数据超过三百万条,静态页生成到一半突然全部报错,登录后台,发现栏目页、列表页全部白屏,只有首页还能勉强打开——典型的数据库连接中断或数据表损坏。
帝国CMS数据表恢复全实录,从应急清理到百万级性能调优的一次完整战报
我快速打开phpMyAdmin,检查phome_ecms_news主表,发现sql_ 前缀的索引文件大小异常,myisam表的.MYD 和.MYI 文件大小对不上,帝国CMS默认使用MyISAM引擎,这类表在非正常关机或磁盘满时极易损坏,第一步不是急着修复数据,而是先备份物理文件:cp -rp /www/wwwroot/site/e/data /www/backup_data_20231026,同时用mysqldump -u root -p --single-transaction --quick --lock-tables=false 备份整个数据库到远程存储,任何修复操作前,备份是唯一后悔药。
挂马后的清理与恢复流程
检查e/class/connect.php和e/config/config.php文件修改时间,发现比系统发布时间晚了两天,用grep -r "eval\|base64_decode" /www/wwwroot/site/e/扫描,找到三处加密后门,清理步骤:1)用干净备份覆盖被篡改核心文件;2)删除e/data/下非白名单的临时PHP文件;3)修改后台目录e/admin为e/myadmin2023,同时修改config.php中的$ecms_config['esave']['adminpath'];4)全站文件权限收紧,目录755、文件644,e/data/ 单独设为只读,恢复数据表时,如果myisamchk报错,直接用REPAIR TABLE phome_ecms_news;,但先记得SET GLOBAL innodb_force_recovery = 1;(虽然帝国默认MyISAM,但建议尽早转InnoDB)。
百万级数据查询优化实战
恢复后测试,SELECT * FROM phome_ecms_news WHERE classid=12 ORDER BY id DESC LIMIT 10 耗时3.8秒,不可接受,分析执行计划发现classid和id没有复合索引,执行:ALTER TABLE phome_ecms_news ADD INDEX idx_classid_id (classid, id); 再跑一次,0.02秒,帝国CMS核心查询都在e/class/db_sql.php里,建议直接改模型生成规则:在后台“系统设置-数据表优化”里,将常用字段newstime、classid、isgood加入联合索引,同时把title字段改为FULLTEXT索引(如果支持),对于全文搜索,别用LIKE '%关键词%',改走MATCH...AGAINST,或者上Sphinx。
静态页生成卡死的处理方案
问题表象是批量生成时进度条停在第2380个,查看e/class/functions.php里GetHtml()函数,发现它每次生成都检查所有未生成列表,且未释放MySQL连接,优化:1)分批生成——后台“系统设置-生成选项”里,将“每次生成页数”调至200;2)修改/e/class/htmlfun.php,加入usleep(50000); 防止CPU过载;3)最关键:开启生成缓存,将已生成文件的MD5记录到phome_ecms_news_index表,重复请求直接返回缓存路径,砍掉80%重复IO,实测三百万文章全量生成从11小时降到3小时。
分表与索引深度建议
当单表数据超过200万,务必分表,帝国CMS支持按时间分表:phome_ecms_news_2023、phome_ecms_news_2024,在后台“数据表管理”里创建新表,并用系统自带的“数据迁移”脚本定时归档旧数据,索引优化遵循“最左前缀”原则:把WHERE里最常用的等值条件放最左,范围查询放最后,比如(classid, newstime, id)优于(id, classid),将myisamchk改为定期OPTIMIZE TABLE,但高峰期别跑,会造成表锁。
整站搬家完整流程与坑点
先把e/data/、e/class/、e/extend/、e/template/整体打包,但别打包e/temp/和e/cache/,数据库导出前,关闭后台所有生成任务,用mysqldump带--routines --triggers,搬到新服务器后,重点改三点:1)e/config/config.php里$ecms_config['db']['dbhost']、dbname;2)e/class/connect.php里的数据库端口;3)e/template/里的绝对路径全部改为相对路径,否则后台模板预览全裂,恢复时注意PHP版本兼容,帝国CMS 7.5在PHP 7.4下会报each()弃用,需快速替换为foreach,最后记得清空e/data/cache/下的缓存文件,重新生成栏目和首页。
缓存策略最终配置
本机Redis装好并配置php_redis.dll后,在帝国CMS后台“系统设置-缓存管理”启用“Redis缓存”,将e/class/db_sql.php里的查询函数包一层:第一次查数据库,第二次查Redis,TTL设300秒,对于列表页,直接输出静态HTML,动态标签用js调用接口,让nginx直接返回缓存,实测首页从每次动态200ms降到静态20ms,数据库连接数从150降到30。
凌晨五点半,数据表全部恢复,索引重建完成,新后台路径已生效,静态页再次全量生成,全程零丢失,帝国CMS虽然老,但只要掌握这些硬核操作,照样能扛住大型站点,最后留一句:任何操作前,先备份,再备份,三备份,不是开玩笑。



发表评论