thinkphp实现增量同步需用updated_at或id筛选+本地状态标记,避免全量覆盖;upsert需手动sql或findorsave;定时任务须异常捕获、超时设置与连接释放;防重复靠sync_version双保险。

怎么用 ThinkPHP 实现定时拉取外部数据并只同步新增/更新记录
核心是避免全量覆盖,靠时间戳或自增 ID 做增量判断。ThinkPHP 本身不提供“增量同步”内置方法,得自己设计逻辑,关键在 where 条件和状态标记的配合。
- 优先用外部接口返回的
updated_at或id字段做筛选,例如where('updated_at', '>', $last_sync_time) - 本地表必须有对应字段(如
ext_updated_at)且建索引,否则查得慢、锁得久 - 别直接用
created_at判断——有些第三方数据会补录历史记录,created_at不等于入库时间 - 同步前先查出本次待处理的最大
updated_at,同步成功后再更新本地last_sync_time,防止中断导致漏数据
ThinkPHP 的 saveAll() 能不能直接做 upsert(插入或更新)
不能原生支持 MySQL 的 ON DUPLICATE KEY UPDATE,saveAll() 默认全是 INSERT,主键冲突就报错 SQLSTATE[23000]: Integrity constraint violation。
- 想用 upsert,得手动拼 SQL:用
Db::execute()调INSERT ... ON DUPLICATE KEY UPDATE - 或者改用
foreach遍历 +findOrSave(),但性能差,1000 条以上明显卡顿 - 确保表里有唯一索引(比如外部系统给的
ext_id字段),否则 upsert 失效 - 注意
findOrSave()查的是主键,不是业务唯一键,得先where('ext_id', $v['ext_id'])->find()再决定存还是改
用 think:command 写定时任务,为什么线上跑着跑着就停了
常见原因是没设超时、没捕获异常、没处理长连接,Linux 定时任务(crontab)调用时一出错就静默退出。
- 命令类里必须包
try...catch,把所有Db、Http、json_decode可能抛的异常兜住,至少记到日志 - 加
set_time_limit(0)开头,防止大批次同步中途被 PHP 超时终止 - 别在命令里复用长连接(比如
cURL句柄没关、Redis 连接没释放),循环中每次请求都新建连接更稳 - crontab 最好加日志重定向,例如
* * * * * /usr/bin/php /var/www/think schedule:run >> /tmp/sync_cron.log 2>&1
同步过程中怎么防止重复写入或覆盖正确数据
靠“本地状态锁 + 外部版本号”双保险,单靠时间戳或 ID 递增都不绝对可靠。
- 给每条同步记录加字段
sync_version,值来自外部接口的version或etag,更新时WHERE sync_version - 同步前先
SELECT FOR UPDATE锁住要更新的几条记录(用Db::table()->lock(true)->where(...)->select()),避免并发任务同时改同一条 - 不要用
replaceInto()——它会删再插,触发器、自增 ID、关联数据可能被连带影响 - 上线前一定在测试环境用真实数据压测一次,重点看并发 3–5 个同步任务时,有没有
Deadlock found when trying to get lock报错
真正难的不是写完同步逻辑,而是确认哪条数据该进、哪条该跳过、哪条该回滚——外部系统文档不全、字段语义模糊、网络分片重传,这些细节比代码本身更耗时间。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











