Java应用定期扫描MariaDB表A新行并迁移至表B的实现问询
当然可以!你的思路完全可行,而且刚好能复用你现有的写入逻辑,作为Java/MySQL新手,这个方案上手难度也不高,下面一步步给你讲清楚怎么做:
完全可以通过created_at字段来筛选指定时间间隔内的新增行——因为新增数据的created_at就是它的创建时间,咱们只需要每次定时任务执行时,记录上次任务结束的时间,然后查询created_at大于等于这个时间点的所有行,就能精准拿到这段时间内的新增数据,接着把这些数据存入List后直接调用你已有的写入表B的方法就行。
如果之后需要同步更新过的数据,也可以用updated_at字段,逻辑是一样的。
1. 记录上次任务执行的时间
要避免重复迁移数据,必须记住上次任务查到了哪个时间点。推荐用数据库存这个值(比本地文件更可靠,程序重启也不会丢失):
- 新建一张简单的配置表,比如:
CREATE TABLE task_config ( task_name VARCHAR(50) PRIMARY KEY, last_executed_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ); -- 初始化数据,第一次任务执行前先插入这条 INSERT INTO task_config (task_name, last_executed_time) VALUES ('data_migration', '2020-01-01 00:00:00');
- 每次任务开始前,从这张表读
last_executed_time,任务执行完成后,把这个值更新为当前任务开始的时间(避免任务执行过程中新增的数据被重复抓取)。
2. 编写增量查询SQL
用created_at作为筛选条件,SQL示例:
SELECT * FROM table_a WHERE created_at >= ? AND created_at < ?;
- 第一个
?填上次任务的执行时间,第二个?填本次任务开始的时间(比如new Timestamp(System.currentTimeMillis()))。 - 用
<而不是<=是为了避免刚好在任务执行瞬间新增的数据被重复抓取。
3. 复用现有写入方法
把查询到的结果转换成你现有方法需要的实体类List,直接调用写入方法即可。比如你的方法是void writeToTableB(List<TableA> data),那拿到List后直接传参就行,完全不用改原有逻辑。
4. 实现定时任务
Java里有两种简单的定时方案,新手可以选第一种:
方案1:基础的ScheduledExecutorService
不需要框架,JDK自带,代码示例:
import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class MigrationTask { public static void main(String[] args) { int intervalMinutes = 5; // 你要的x分钟间隔 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); // 初始延迟0分钟,每隔intervalMinutes执行一次 scheduler.scheduleAtFixedRate(() -> { runMigration(); // 这里放你的迁移逻辑:读上次时间→查增量→写入表B→更新上次时间 }, 0, intervalMinutes, TimeUnit.MINUTES); } private static void runMigration() { // 这里写前面步骤的具体代码:查询、写入、更新时间 } }
方案2:Spring项目用@Scheduled
如果你的应用是Spring/Spring Boot项目,直接用注解更方便:
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class MigrationTask { // 每隔5分钟执行一次,cron表达式可以自定义更复杂的时间规则 @Scheduled(fixedRate = 5 * 60 * 1000) public void runMigration() { // 迁移逻辑 } }
时区一致性:一定要保证MariaDB和Java应用的时区一致!比如都设为
Asia/Shanghai:- JDBC URL里加参数:
jdbc:mariadb://localhost:3306/your_db?serverTimezone=Asia/Shanghai - MariaDB配置文件里设置:
default-time-zone = '+08:00'
时区不一致会导致时间筛选完全错误,漏数据或重复数据。
- JDBC URL里加参数:
事务处理:查询和写入要加事务,如果写入表B失败,一定要回滚,并且不要更新上次执行时间,不然下次会漏掉数据。JDBC里可以用
conn.setAutoCommit(false),然后在try-catch里手动commit或rollback。数据量控制:如果x分钟内新增数据特别多,一次查全量可能内存溢出,这时候要分页查询,比如加
LIMIT offset, 1000分批处理,或者用游标(ResultSet的TYPE_FORWARD_ONLY+CONCUR_READ_ONLY)来逐行读取。异常处理:定时任务里一定要加try-catch,不然一次任务抛出异常会导致整个定时任务停止。捕获异常后要打印详细日志,方便排查问题。
先手动测试:不要直接开定时,先手动调用
runMigration()方法,测试增量查询是否正确、写入是否成功,确认没问题再开定时。
内容的提问来源于stack exchange,提问作者Roshan Upreti

