开发新闻RSS抓取API时,捕获重复条目异常提升性能是否可行?
问题分析与优化方案
性能假设验证
你的两处性能判断均符合实际场景:
- 逐条查询数据库判断条目是否存在,会让单次写入操作多一次网络IO请求,整体处理效率至少下降50%,数据量较大时延迟会非常明显
- 全量查询10万+条guid存入内存构建Map,不仅会占用大量JVM堆内存,全量查询本身对数据库的IO压力也很高,确实不适合长期运行
最优解决方案
推荐你分层实现去重,兼顾性能和资源占用:
- 数据库层兜底去重(核心方案)
你已经为article表的url字段设置了唯一索引,直接使用MySQL原生的INSERT IGNORE语法即可,重复数据插入时数据库会自动忽略该条写入,不会抛出Duplicate Entry异常,也不需要提前做查询判断,性能损耗极低。
如果业务需要更新已存在的旧文章内容(比如内容有修订),可以替换为ON DUPLICATE KEY UPDATE语法,重复时自动更新指定字段即可。 - 批量插入优化
你当前的循环单条写入效率很低,调整为批量插入模式,配合INSERT IGNORE语法一次提交10~100条数据,可大幅降低数据库的网络IO开销。 - 轻量缓存前置过滤
RSS内容具备强时效性,新抓取的Feed中几乎不会出现2小时之前发布的旧内容,你可以用Caffeine等本地缓存组件,只缓存最近2小时入库的文章guid,每次抓取到新条目后先查本地缓存,缓存中不存在的再发送到数据库执行写入,进一步减少无效的数据库请求。该方案缓存的条目最多只有数千条,完全不会占用过多内存。 - 日志级别调整
原本捕获异常的逻辑可作为兜底容错方案,但是重复数据的日志不需要设置为error级别,调整为debug或info即可,避免日志文件被无效信息占满。
代码示例
自定义写入方法(Spring Data JPA 实现)
在你的ArticleRepository接口中新增原生SQL写入方法:
@Modifying @Transactional @Query(value = "INSERT IGNORE INTO article(title, url, content, guid, rss_feed_id, pub_time) " + "VALUES (:#{#article.title}, :#{#article.url}, :#{#article.content}, :#{#article.guid}, :#{#article.rssFeedId}, :#{#article.pubTime})", nativeQuery = true) int insertIfNotExists(Article article);
批量插入实现(JdbcTemplate 实现)
public void batchSaveArticles(List<Article> articles) { String sql = "INSERT IGNORE INTO article(title, url, content, guid, rss_feed_id, pub_time) VALUES (?,?,?,?,?,?)"; jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { Article article = articles.get(i); ps.setString(1, article.getTitle()); ps.setString(2, article.getUrl()); ps.setString(3, article.getContent()); ps.setString(4, article.getGuid()); ps.setLong(5, article.getRssFeedId()); ps.setTimestamp(6, Timestamp.valueOf(article.getPubTime())); } @Override public int getBatchSize() { return articles.size(); } }); }
注意事项
如果不同RSS源的guid可能存在重复,建议将唯一索引调整为(guid, rss_feed_id)联合唯一键,避免不同源的相同guid被误判为重复内容。
内容的提问来源于stack exchange,提问作者Lukonjun
相关产品推荐
相关产品推荐

