在业务系统运行过程中,统计表通常用于存储汇总后的业务数据,为报表生成提供直接的数据支撑。如果每次更新统计表都采用全量同步的方式,会大量占用数据库IO和CPU资源,尤其是当业务表数据量达到百万甚至千万级别时,全量更新的耗时和性能损耗会非常明显。增量更新只同步业务表中发生变化的数据,能够大幅降低更新过程的资源消耗,是统计表更新的首选方案。

常见增量更新判断方式
1. 基于更新时间戳判断
业务表中通常会设置update_time字段,记录每条数据最后一次更新的时间。统计表同步时,只需要查询业务表中update_time晚于上一次同步时间的记录,就是需要增量同步的数据。这种方式实现简单,适合大多数有更新时间字段的业务表。
假设我们有业务表order_info存储订单明细,统计表order_daily_stat存储每日订单汇总数据,表结构如下:
-- 业务表结构
CREATE TABLE order_info (
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
order_amount DECIMAL(10,2),
order_status TINYINT,
update_time DATETIME
);
-- 统计表结构
CREATE TABLE order_daily_stat (
stat_date DATE PRIMARY KEY,
total_order_count INT,
total_order_amount DECIMAL(10,2),
last_sync_time DATETIME
);
增量更新SQL示例如下:
-- 查询上次同步时间
SET @last_sync_time = (SELECT COALESCE(last_sync_time, '1970-01-01 00:00:00') FROM order_daily_stat LIMIT 1);
-- 插入或更新当日统计
INSERT INTO order_daily_stat (stat_date, total_order_count, total_order_amount, last_sync_time)
SELECT
DATE(update_time) AS stat_date,
COUNT(*) AS total_order_count,
SUM(order_amount) AS total_order_amount,
MAX(update_time) AS last_sync_time
FROM order_info
WHERE update_time > @last_sync_time
GROUP BY DATE(update_time)
ON DUPLICATE KEY UPDATE
total_order_count = VALUES(total_order_count),
total_order_amount = VALUES(total_order_amount),
last_sync_time = VALUES(last_sync_time);
2. 基于自增ID判断
如果业务表有自增主键id,且没有更新历史数据的场景,可以通过记录上一次同步的最大ID,只同步ID大于该值的新增数据。这种方式性能比时间戳判断更高,因为自增ID的查询可以利用主键索引,速度更快。
对应的增量更新SQL示例:
-- 查询上次同步的最大ID
SET @last_max_id = (SELECT COALESCE(MAX(order_id), 0) FROM order_daily_stat_rel);
-- 同步新增数据到关联表
INSERT INTO order_daily_stat_rel (order_id, stat_date, order_amount)
SELECT
order_id,
DATE(update_time) AS stat_date,
order_amount
FROM order_info
WHERE order_id > @last_max_id;
-- 更新统计表(按日汇总)
INSERT INTO order_daily_stat (stat_date, total_order_count, total_order_amount)
SELECT
stat_date,
COUNT(*) AS total_order_count,
SUM(order_amount) AS total_order_amount
FROM order_daily_stat_rel
WHERE order_id > @last_max_id
GROUP BY stat_date
ON DUPLICATE KEY UPDATE
total_order_count = total_order_count + VALUES(total_order_count),
total_order_amount = total_order_amount + VALUES(total_order_amount);
3. 基于变更日志表判断
如果业务表存在频繁更新、删除操作,前两种方式可能无法覆盖所有变更场景,此时可以维护一张变更日志表,记录业务表的所有增删改操作,增量更新时直接读取变更日志表的数据进行处理。这种方式能够完整捕获所有数据变更,但是需要额外的日志表维护成本。
增量更新注意事项
- 数据一致性:增量更新过程中如果业务表有新数据写入,可能会出现数据遗漏,建议在更新时加行级锁或者使用事务,确保同步时间段内的数据不会发生变化。
- 性能优化:增量查询的条件字段需要建立合适的索引,比如
update_time、id字段,避免全表扫描。 - 异常处理:需要记录每次同步的起始和结束时间、同步的数据量,出现同步失败时可以根据记录进行重试,避免数据重复或者遗漏。
- 历史数据处理:如果业务表有历史数据补录的场景,需要额外处理补录数据的同步,避免漏更。
批量更新优化技巧
当需要同步的增量数据量较大时,可以采用批量提交的方式,避免单条操作的开销。比如每1000条数据提交一次事务,或者将增量数据先存入临时表,再通过临时表批量更新统计表,减少统计表的写入次数。
临时表批量更新示例:
-- 创建临时表存储增量数据
CREATE TEMPORARY TABLE tmp_order_increment (
stat_date DATE,
order_count INT,
order_amount DECIMAL(10,2)
);
-- 插入增量数据到临时表
INSERT INTO tmp_order_increment (stat_date, order_count, order_amount)
SELECT
DATE(update_time) AS stat_date,
COUNT(*) AS order_count,
SUM(order_amount) AS order_amount
FROM order_info
WHERE update_time > (SELECT COALESCE(last_sync_time, '1970-01-01 00:00:00') FROM order_daily_stat LIMIT 1)
GROUP BY DATE(update_time);
-- 批量更新统计表
INSERT INTO order_daily_stat (stat_date, total_order_count, total_order_amount, last_sync_time)
SELECT
stat_date,
order_count,
order_amount,
NOW()
FROM tmp_order_increment
ON DUPLICATE KEY UPDATE
total_order_count = total_order_count + VALUES(total_order_count),
total_order_amount = total_order_amount + VALUES(total_order_amount),
last_sync_time = VALUES(last_sync_time);
-- 删除临时表
DROP TEMPORARY TABLE tmp_order_increment;