CodeIgniter 删除大数据量应分批进行,使用主键或时间字段游标分页,每次 LIMIT 删除固定数量,通过 affected_rows() 判断循环终止,推荐“上次最后 id + 1”方式推进以避免并发问题。

CodeIgniter 删除大数据量数据时,直接用 $this->db->delete() 会一次性加载全部匹配记录或触发全表扫描,容易超内存、锁表时间长、甚至导致数据库连接中断。正确做法是分批删除,控制每次操作的数据量,降低数据库压力。
分批删除的核心逻辑
不是靠 PHP 循环查出所有 ID 再逐条删(太慢且占内存),而是用主键(如 id)或时间字段做游标分页,每次只删固定数量(比如 1000 条),通过 WHERE 条件限定范围,循环推进直到删完。
- 必须有可排序、唯一、递增的字段(推荐自增
id或created_at) - 每次 DELETE 带上 LIMIT,避免误删或阻塞
- 用
affected_rows()判断是否还有数据可删,作为循环退出条件 - 适当 sleep(如 usleep(10000))缓解数据库瞬时压力,非必需但建议在高并发场景使用
CodeIgniter 3 实现示例(基于 id 分批)
假设要删除 logs 表中 status = 0 的旧数据:
$batch_size = 1000;
$min_id = 0;
do {
$this->db->where('id >', $min_id);
$this->db->where('status', 0);
$this->db->limit($batch_size);
$this->db->delete('logs');
$affected = $this->db->affected_rows();
if ($affected === 0) break;
// 更新 min_id 为本次删除的最大 id
$row = $this->db->query("SELECT MAX(id) as max_id FROM logs WHERE id > {$min_id} AND status = 0 LIMIT 1")->row();
$min_id = $row ? $row->max_id : $min_id + $batch_size;
// 可选:短暂休眠
usleep(50000);
} while ($affected == $batch_size);
更稳妥的写法(避免 MAX 查询依赖)
上面用 MAX(id) 有一定风险(并发写入时可能跳过数据)。更推荐用“上次最后 id + 1”方式推进:
$batch_size = 1000;
$last_id = 0;
do {
$this->db->select('id');
$this->db->from('logs');
$this->db->where('id >', $last_id);
$this->db->where('status', 0);
$this->db->order_by('id ASC');
$this->db->limit($batch_size);
$query = $this->db->get();
if ($query->num_rows() === 0) break;
$ids = $query->result_array();
$id_list = array_column($ids, 'id');
$last_id = end($id_list);
$this->db->where_in('id', $id_list);
$this->db->delete('logs');
usleep(30000);
} while (count($id_list) === $batch_size);
补充建议
- 生产环境执行前,先在测试库验证 SQL 和性能
- 考虑在凌晨低峰期执行,并监控数据库 CPU/IO/连接数
- 删除后及时执行
OPTIMIZE TABLE(MyISAM 必须,InnoDB 可选)释放空间 - 如果表有外键关联,确认级联策略或手动清理关联数据,避免报错中断
- 日志记录每次删除数量和耗时,便于后续复盘和调优


















