禅与计算机 禅与计算机
首页
  • Java基础

    • 一文搞懂Java核心技术
    • Java面向对象知识点大总结,建议收藏
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
    • Java异常:从原理到实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
    • 探索JVM的隐秘角落:元空间详解
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 元认知

    • 摩擦感:AI时代的写作自省
    • 从断墨寻径浅谈程序员的元学习能力
    • AI时代专注力培养
    • 如何阅读一本书:技术书籍的读书笔记方法论
  • 开发工具

    • IDEA配置详解与高效使用指南
    • Windows环境下JDK安装与环境变量配置
    • Windows 10 下的 Maven 安装配置教程
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
    • 感觉你因为AI退化了?用Redis SCAN源码打脸AI时代的孔乙己
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • Elasticsearch核心原理与架构设计
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
    • 基于 Claude Code 复刻 Redis 慢查询指令实践
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
    • 对账核销场景设计与重构实践
    • 千万级交易流水慢查询综合治理实践
    • 记一次StarRocks源码级排错和既有架构优化实践
    • 基于快照合并修复Seata AT回滚补偿与Flink批攒导致StarRocks数据不一致最佳实践
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
  • AI工具链

    • Claude Code 实战指南:从安装配置到企业级开发流程
    • 一次 Claude Code 启动失败的 AI 辅助排查复盘
    • 基于提示词工程与KITE框架的Redis签到功能开发实践
    • Claude Code 记忆管理:CLAUDE.md 最佳实践
    • Claude Code 规则管理:Rules 拆分编排与迭代实践(文末送书)
    • VSCode与Claude Code后端开发环境搭建与AI编程工作流实践
    • AI 写的企业级组件不敢用?我替你验过了
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

sharkchili

计算机禅修者
首页
  • Java基础

    • 一文搞懂Java核心技术
    • Java面向对象知识点大总结,建议收藏
    • 聊聊Java中的异常
    • 聊聊Java中的常用类String
    • 万字长文带你细聊Java注解本质
    • 来聊聊Java的反射机制
    • 深入解析Java泛型的魅力与机制
    • Java集合框架深度解析与面试指南
    • Java常用集合类HashMap深度解析
    • LinkedHashMap源码到面试题的全解析
    • 深入解析CopyOnWriteArrayList的工作机制
    • Java基础IO总结
    • Java三大IO模型小结
    • Java BIO NIO AIO详解
    • Java进阶NIO之IO多路复用详解
    • Java8流式编程入门
    • 一文速通lambda与函数式编程
    • Java8函数式方法引用最佳实践
    • Java异常:从原理到实践
  • Java并发编程

    • Java并发编程基础小结
    • 深入理解Java中的final关键字
    • 浅谈Java并发安全发布技术
    • 浅谈Java并发编程中断的哲学
    • Java线程池知识点小结
    • 浅谈Java线程池中拒绝策略与流控的艺术
    • synchronized关键字使用指南
    • 深入源码解析synchronized关键字
    • 详解JUC包下的锁
    • 详解并发编程中的CAS原子类
    • LongAdder源码分析
    • AQS源码解析
    • 深入剖析Java并发编程中的死锁问题
    • Java并发容器总结
    • 详解Java并发编程volatile关键字
    • 并发编程ThreadLocal必知必会
    • CompletableFuture基础实践小结
    • CompletableFuture异步多任务最佳实践
    • 硬核详解FutureTask设计与实现
    • 线程池大小设置的底层逻辑与场景化方案
    • 来聊一个有趣的限流器RateLimiter
  • JVM相关

    • 从零开始掌握 JVM
    • JVM核心知识点小结
    • JVM指令集概览:基础与应用
    • JVM类加载器深度解析
    • JVM方法区深度解析
    • Java内存模型JMM详解
    • Java对象大小的精确计算方法
    • 逃逸分析在Java中的应用与优化
    • 从零开始理解JVM的JIT编译机制
    • G1垃圾回收器:原理详解与调优指南
    • JVM故障排查实战指南
    • JVM内存问题排错最佳实践
    • JVM内存溢出排查指南
    • 简明的Arthas使用教程
    • 简明的Arthas配置及基础运维教程
    • 基于Arthas Idea的JVM故障排查与指令生成
    • 基于arthas量化监控诊断java应用方法论与实践
    • 深入剖析arthas技术原理
    • 探索JVM的隐秘角落:元空间详解
  • 深入理解Spring框架

    • Spring 核心知识点全面解析
    • Spring核心功能IOC详解
    • Spring AOP 深度剖析与实践
    • Spring 三级缓存机制深度解析
    • 深入 Spring 源码,剖析设计模式的落地实践
    • 探索 Spring 事务的奥秘
    • 深入解析Spring Bean的生命周期管理
    • 解读 Spring Boot 核心知识点
    • Spring Boot 启动优化实战:1分钟到13秒的排查与优化之路
    • Spring Boot自动装配原理及实践
    • 一文快速上手Sharding-JDBC
    • sharding-jdbc如何实现分页查询
    • 基于DynamicDataSource整合分库分表框架Shardingsphere
  • 计算机组成原理

    • 计算机硬件知识小结
    • CPU核心知识点小结
    • 浅谈CPU流水线的艺术
    • 从Java程序员视角聊聊CPU缓存
    • CPU任务调度和伪共享问题小结
    • CPU MESI缓存一致性协议
    • CPU内存管理机制
    • 内存深度解析
    • 磁盘存储原理
    • 详解计算机启动步骤
    • CPU南北桥架构与发展史
    • CPU中断机制与硬件交互详解
  • 操作系统

    • 如何实现一个高性能服务器
    • Linux文件结构与文件权限
    • Linux常见压缩指令小结
    • Linux核心系统调用详解
    • Linux进程管理
    • Linux线程管理
    • 进程与线程深度解析
    • Linux进程间通信机制
    • 零拷贝技术原理与实践
    • CPU缓存一致性问题深度解析
    • IO任务与CPU调度艺术
  • 计算机网络

    • 网卡通信原理详解
    • 网卡数据包处理指南
    • 基于抓包详解TCP协议
  • 编码最佳实践

    • 浅谈现代软件工程TDD最佳实践
    • 浅谈TDD模式下并发程序设计与实现
    • 面向AI编程新范式Trae后端开发环境搭建与实践
    • 基于Vibe Coding的Redis分页查询实现
    • 告别AI无效对话:资深工程师的提示词设计最佳实践
  • 实用技巧与配置

    • Mac常用快捷键与效率插件指南
    • Keynote技术科普短视频制作全攻略
  • 元认知

    • 摩擦感:AI时代的写作自省
    • 从断墨寻径浅谈程序员的元学习能力
    • AI时代专注力培养
    • 如何阅读一本书:技术书籍的读书笔记方法论
  • 开发工具

    • IDEA配置详解与高效使用指南
    • Windows环境下JDK安装与环境变量配置
    • Windows 10 下的 Maven 安装配置教程
  • Nodejs
  • 博客搭建
  • Redis

    • Redis核心知识小结
    • 解锁Redis发布订阅模式
    • 掌握Redis事务
    • Redis主从复制技术
    • Redis的哨兵模式详解
    • 深度剖析Redisson分布式锁
    • 详解redis单线程设计思路
    • 来聊聊Redis所实现的Reactor模型
    • Redis RDB持久化源码深度解析
    • 来聊聊redis的AOF写入
    • 来聊聊Redis持久化AOF管道通信的设计
    • 来聊聊redis集群数据迁移
    • Redis SDS动态字符串深度解析
    • 高效索引的秘密:redis跳表设计与实现
    • 聊聊redis中的字典设计与实现
    • 感觉你因为AI退化了?用Redis SCAN源码打脸AI时代的孔乙己
  • MySQL

    • MySQL基础知识点小结
    • 解读MySQL 索引基础
    • MySQL 索引进阶指南
    • 解读MySQL Explain关键字
    • 探秘 MySQL 锁:原理与实践
    • 详解MySQL重做日志redolog
    • 详解undoLog在MySQL MVCC中的运用
    • MySQL二进制日志binlog核心知识点
    • MySQL高效插入数据的最佳实践
    • MySQL分页查询优化指南
    • MySQL流式查询的奥秘与应用解析
    • 来聊聊分库分表
    • 来聊聊大厂常用的分布式ID生成方案
  • ElasticSearch

    • Elasticsearch核心原理与架构设计
    • ES 基础使用指南
    • ElasticSearch如何写入一篇文档
    • 深入剖析Elasticsearch文档读取原理
    • 聊聊ElasticSearch性能调优
    • Spring借助Easy-Es操作ES
  • Netty

    • 一文快速了解高性能网络通信框架Netty
    • Netty网络传输简记
    • 来聊聊Netty的ByteBuf
    • 来聊聊Netty消息发送的那些事
    • 解密Netty高性能之谜:NioEventLoop线程池阻塞分析
    • 详解Netty中的责任链Pipeline如何管理ChannelHandler
    • Netty Reactor模型常见知识点小结
    • Netty如何驾驭TCP流式传输?粘包拆包问题全解
    • Netty解码器源码解析
  • 消息队列

    • 一文快速入门消息队列
    • 消息队列RocketMQ入门指南
    • 基于RocketMQ实现分布式事务
    • RocketMQ容器化最佳实践
    • RocketMQ常见问题与深度解析
    • Kafka快速安装与使用指南
  • Nginx

    • Linux下的nginx安装
    • Nginx基础入门总结
    • Nginx核心指令小结
    • Nginx进程结构与核心模块初探
    • Nginx应用进阶HTTP核心模块配置
    • Nginx缓存及HTTPS配置小记
    • nginx高可用实践简记
    • Nginx性能优化
  • 微服务基础

    • 微服务基础知识小结
    • 分布式事务核心概念小结
    • OpenFeign核心知识小结
    • 微服务组件Gateway核心使用小结
    • 分布式事务Seata实践
    • 用 Docker Compose 完成 Seata 的整合部署
  • Nacos

    • Nacos服务注册原理全解析
    • Nacos服务订阅流程全解析
    • Nacos服务变更推送流程全解析
    • 深入解析SpringCloud负载均衡器Loadbalancer
    • Nacos源码环境搭建与调试指南
  • Seata

    • 深度剖析Seata源码
  • Docker部署

    • 一文快速掌握docker的理念和基本使用
    • 使用docker编排容器
    • 基于docker-compose部署微服务基本环境
    • 基于docker容器化部署微服务
    • Gateway全局异常处理及请求响应监控
    • Docker图形化界面工具Portainer最佳实践
  • Go基础

    • 一文带你速通Go语言基础语法
    • 一文快速掌握Go语言切片
    • 来聊聊go语言的hashMap
    • 一文速通go语言类型系统
    • 浅谈Go语言中的面向对象
    • go语言是如何实现协程的
    • 聊聊go语言中的GMP模型
    • 极简的go语言channel入门
    • 聊聊go语言基于epoll的网络并发实现
    • 写给Java开发的Go语言协程实践
  • mini-redis实战

    • 来聊聊我用go手写redis这件事
    • mini-redis如何解析处理客户端请求
    • 实现mini-redis字符串操作
    • 硬核复刻redis底层双向链表核心实现
    • 动手复刻redis之go语言下的字典的设计与落地
    • Go 语言下的 Redis 跳表设计与实现
    • Go 语言版 Redis 有序集合指令复刻探索
    • 基于 Claude Code 复刻 Redis 慢查询指令实践
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

    • Java实现文件分片上传
    • 基于时间缓存优化浏览器轮询阻塞问题
    • 基于EasyExcel实现高效导出
    • 10亿数据高效插入MySQL最佳方案
    • 从开源框架中学习那些实用的位运算技巧
    • 对账核销场景设计与重构实践
    • 千万级交易流水慢查询综合治理实践
    • 记一次StarRocks源码级排错和既有架构优化实践
    • 基于快照合并修复Seata AT回滚补偿与Flink批攒导致StarRocks数据不一致最佳实践
  • CI/CD

    • 基于NETAPP实现内网穿透
    • 基于Gitee实现Jenkins自动化部署SpringBoot项目
    • Jenkins离线安装部署教程简记
    • 基于Nexus搭建Maven私服基础入门
    • 基于内网的Jenkins整合gitlab综合方案简记
  • 监控方法论

    • SpringBoot集成Prometheus与Grafana监控
    • Java监控度量Micrometer全解析
    • 从 micrometer计量器角度快速上手promQL
    • 硬核安利一个监控告警开源项目Nightingale
  • Spring AI

    • Spring AI Alibaba深度实战:一文掌握智能体开发全流程
    • Spring AI Alibaba实战:JVM监控诊断Arthas Agent的工程化构建与最佳实践
  • 大模型评测

    • M2.7 真能打!我用两个真实场景测了测,结果有点意外
    • Qoder JetBrains插件评测:祖传代码重构与接口优化实战
  • AI工具链

    • Claude Code 实战指南:从安装配置到企业级开发流程
    • 一次 Claude Code 启动失败的 AI 辅助排查复盘
    • 基于提示词工程与KITE框架的Redis签到功能开发实践
    • Claude Code 记忆管理:CLAUDE.md 最佳实践
    • Claude Code 规则管理:Rules 拆分编排与迭代实践(文末送书)
    • VSCode与Claude Code后端开发环境搭建与AI编程工作流实践
    • AI 写的企业级组件不敢用?我替你验过了
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 项目编排

    • Spring脚手架创建简记
    • Spring脚手架集成分页插件
    • Spring脚手架集成校验框架
    • maven父子模块两种搭建方式简记
    • SpringBoot+Vue3前后端快速整合入门
    • 来聊聊Java项目分层规范
  • 场景设计

  • CI-CD

  • 架构设计

  • 监控方法论

  • 软件工程实践
  • 场景设计
sharkchili
2026-05-20
目录

基于快照合并修复Seata AT回滚补偿与Flink批攒导致StarRocks数据不一致最佳实践

# 写在文章开头

针对既有场景设计和架构这个话题,一直是笔者近几年的工作课题,也一直想写,但是没有时间。还好,最近因为个人一些原因刚刚好需要针对这些场景进行一下复盘。而这篇文章正是基于上一篇starrocks架构优化前所遇到一个存量问题,即一个分布式架构下各个软件中间件局限性所导致的数据一致性问题。

同样的,这篇文章笔者不会过分深入代码细节,更着重去强调笔者的分析和解决思路,这也一直是AI时代背景下,笔者所一直强调的核心能力。

SharkChili · 计算机路上的禅修者

开源贡献

  • mini-redis:教学级 Redis 精简实现 · https://github.com/shark-ctrl/mini-redis
  • Nightingale:深度源码研究

关注公众号,回复 【加群】 加入技术社群

# 业务背景说明

因为需要进行一些聚合报表分析,某个新业务上线之后需要通过flink将数据同步至starrocks,出于对核心业务的可靠性的保证,团队通过"比数"监控平台监听异构同步库的数据量一致性来分析flink处理的一致性。该业务稳定运行一段时间,某段时间线上监控频繁出现数据不一致,结合业务侧的反馈报表平台存在大量脏数据:

在基于前置可靠性通用排查后发现一条数据链路中kafka以及Flink数据都存在且都可靠消费后,我们就着重于在业务链路进行进一步的排查分析。

# 问题排查与修复方案

# 问题定位与根因分析

通过数据库离线比对我们定位到了几条问题数据,再通过kafka消息回放的方式我们发现这几条数据都是在毫秒级以内完成insert和delete操作。结合Flink job上的数据流算子,最终我们发现了问题。

出于设计理念上的考量,starrocks给自身定位就是需要基于同步流作为聚合报表分析的数据库。在当时的业务场景和表类型(非主键表)下,stream load仅支持批量数据upsert操作,删除需要通过jdbc完成。所以,这使得Flink算子在进行批攒和同步操作时,是通过两个动作实现。改造前的原始同步逻辑如下:


@Override
    public void snapshotState(FunctionSnapshotContext context) throws Exception {
        doExecuteData();
    }

public void doExecuteData() throws SQLException, InterruptedException {
					//jdbc删除
            executeBatchDelete();
            //stream load保存
            executeBatch();
    }
1
2
3
4
5
6
7
8
9
10
11
12

于是就导致本次问题的出现:在MySQL端正常插入后删除的语句,在单位时间内被Flink批攒后回放时,因为executeBatchDelete()先于executeBatch()执行,导致删除操作先于插入操作生效,最终starrocks多出一条源端已删除的数据:

结合相关数据流负责人的同事沟通后,笔者终于了解了问题的全貌。原来,这位同事最近上新了一个功能,为保证全局数据的一致性,这条相关数据被加入seata at模式和其他数据流统一管控治理。因为某些业务上的异常导致全局事务回滚,Seata在二阶段根据undo log执行反向补偿,使得这条数据在瞬时出现insert和补偿delete操作,最终被Flink批攒导致误插入。

之所以之前没有出现该问题,原因也很简单,存量业务的删除操作大部分都不是瞬时发生的,通常是先插入数据,经过一段时间后再因业务调整进行删除,亦或者通过标识的方式进行软删除。而本次业务因为seata的管控治理,并没有在业务上进行维稳控制,最终导致该问题的暴露。

# 快照合并方案的设计

明确事故问题和根因之后,我们就针对既有数据流进行改造,本质上该问题就是不应该插入的数据在事件回放时被插入了,所以笔者本着业务维稳的方式进行拓展修复,对应思路为:

  1. 在批攒期间增加一个缓存,key为数据id,value是一个列表,记录这条数据的事件类型和position位点,位点值越大则说明事务提交时间越靠后
  2. 执行同步前,遍历检查每个key对应的事件是否大于1,若大于1则说明存在瞬时存在多个SQL操作
  3. 判断delete事件position是否大于insert事件,若大于则将这条数据从待同步数据集中移除

# 方案落地与验证

所以最终的做法还是要保证当前数据流不改动的情况下,在窗口内插入后秒删的数据集揪出来并剔除,于是就在同步窗口前的删除操作增加一个快照合并的逻辑,即每次进行同步检查删除操作中的数据集是否存在于更新或者插入数据集中,过滤出存在的,并比对position,将删除的position大于插入的从中剔除:

@Override
public void snapshotState(FunctionSnapshotContext context) throws Exception {
    /**
     * 根据配置决定是否进行快照合并,大体思路为:
     * 1. 遍历删除的快照集
     * 2. 查看插入操作中是否存在,如果有则判断删除的偏移量是否大于插入
     * 3. 如果情况2返回true,则将快照清单中的删除、插入、更新(有插入大概率有更新,所以通用性加入处理)清理掉
     * 4. 后续的同步只要判断是否存在于清单中,如果存在则直接同步写入到starrocks中
     */
    deleteOffsetMap.entrySet().stream()
            .filter(e -> (insertOffsetMap.containsKey(e.getKey()) &&
                    e.getValue() > insertOffsetMap.get(e.getKey())) ||
                    (updateOffsetMap.containsKey(e.getKey()) &&
                            e.getValue() > updateOffsetMap.get(e.getKey())))
            .map(Map.Entry::getKey)
            .collect(Collectors.toSet())
            .forEach(k -> {
                deleteOffsetMap.remove(k);
                updateOffsetMap.remove(k);
                insertOffsetMap.remove(k);
            });
    doExecuteData();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

完成代码改造之后,并在测试环境完成验收之后,考虑到该操作需要增加内存级别的批攒,期间我们针对堆内存gc情况和延迟都进行详尽的覆盖测试。因为这几种场景的数据表每分钟的数据量都不大,且我们同步的阈值都是基于监控指标采集到的阈值进行动态变更的(不会超过5M 不超过Region的大小,约1万条),所以不会导致堆内存飙升。

然后就是延迟方面,结合生产排查和观测情况来看一切良好,因为只是加了一层内存层面的过滤删除操作,且数据集都是采用设计良好的哈希集来存放,无论是定位还是删除效率都很高,耗时都是可以忽略不计的。甚至更进一步地说,我们减少了非必要的jdbc和streamload开销,同步效率甚至比以往更加出色。

明确业务一致性和压测验收都通过后,在从库跑了一个月后(主库期间采用离线订正方式先顶着),主库完成升级并通过业务一致性验收,正式解决问题。

# 常见问题

# Flink Checkpoint 机制

定期为流处理任务中的所有算子状态创建全局一致的快照,并将这些快照持久化存储到外部系统(如 HDFS、S3 等)。其核心机制基于 Chandy-Lamport 分布式快照算法,通过在数据流中注入 Barrier(屏障)将数据流切分为 Barrier 前后的记录,算子收到所有上游 Barrier 后触发状态快照,当所有 Sink 算子完成快照后即完成一次 Checkpoint。

Source → Operator1 → Operator2 → Sink
   ↓        ↓           ↓         ↓
  状态 1   状态 2      状态 3    状态 4
   └────────┴───────────┴─────────┘
              ↓
         Checkpoint 快照
              ↓
         持久化存储 (HDFS/S3)
	
1
2
3
4
5
6
7
8
9

# At-Least-Once 与 Exactly-Once 语义

At-least-once(至少一次)

时间线:
T1: 收到订单 A,计数从 0 → 1 ✓
T2: 准备更新计数时,任务故障 ✗
T3: 任务恢复,从 checkpoint 恢复状态
T4: 重新消费订单 A,计数从 0 → 1 ✓
T5: 继续处理订单 B,计数从 1 → 2 ✓

最终结果:计数 = 2(但实际只有 2 个订单,看似正确)

但如果:
T1: 收到订单 A,计数 0 → 1 ✓
T2: 更新成功后故障 ✗
T3: 恢复后重新消费订单 A
T4: 计数 1 → 2(重复累加!)

最终结果:计数 = 3(错误!实际只有 2 个订单)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Exactly-once(精确一次)

使用 checkpoint + 两阶段提交(2PC):

T1: Checkpoint 开始,注入 Barrier
T2: 收到订单 A,预处理(未提交)
T3: 所有算子对齐 Barrier,准备提交
T4: 两阶段提交确认,正式写入 MySQL
T5: Checkpoint 完成

故障恢复:
- 从 checkpoint 恢复,重放数据
- 通过 Checkpoint 协调 + 事务机制确保数据只被提交一次
- 跳过已处理的订单 A

最终结果:计数 = 2(正确!)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# Flink GC 调优实践

 S0 S1 E O M CCS YGC YGCT FGC FGCT CGC CGCT GCT 
 0.00 100.00 71.28 35.74 85.20 75.08 243030 23660.875 0 0.000 79150 1719.807 25380.682 

1
2
3

从GC日志来看,FGC(Full GC次数)为0,说明没有发生Full GC;老年代使用率(O)为35.74%,元空间使用率(M)为85.20%,均在合理范围内。Young GC次数较多(243030次),但总耗时约23660秒,单次Young GC平均耗时约0.097ms,属于正常水平。不过S1(Survivor 1)使用率达到100%,说明Survivor区偏小,存活对象可能直接晋升老年代,存在一定的优化空间。

针对上述情况,我们适当增大了年轻代的空间比例(调整-XX:NewRatio),同时增大Survivor区(调整-XX:SurvivorRatio),减少对象过早晋升老年代的情况:

# 小结

本文是笔者近几年处理过的一些相对棘手的问题之一,因为涉及全链路的追踪排查,所以针对这种问题笔者排查思路永远是:

  1. 先针对组件可靠性角度出发看看是否存在严重缺陷,在明确没有严重问题无需进行止血之后。
  2. 我们再着重到业务层面进行代码推断分析,然后尝试复现,再将问题整理并协调资源进行进一步明确。
  3. 完成问题的闭环之后,再根据根因进行方案设计与落地。
  4. 稳步推进在测试环境针对逻辑和性能进行完整覆盖之后,推进上线。

可以看到,这篇文章笔者没有非常深入强调技术细节,而是出于架构层面考量进行思考、决策、落地,也希望对大家架构层面上的认知有所帮助。

SharkChili · 计算机路上的禅修者

开源贡献

  • mini-redis:教学级 Redis 精简实现 · https://github.com/shark-ctrl/mini-redis
  • Nightingale:深度源码研究

关注公众号,回复 【加群】 加入技术社群

# 参考

  • Seata AT 模式官方文档:https://seata.apache.org/docs/dev/mode/at-mode/ (opens new window)
  • StarRocks Flink Connector 官方文档:https://docs.starrocks.io/docs/integrations/streaming/flink/ (opens new window)
  • 通过导入实现数据变更:https://docs.starrocks.io/zh/docs/loading/Load_to_Primary_Key_tables/ (opens new window)
  • Stateful Stream Processing:https://nightlies.apache.org/flink/flink-docs-master/docs/concepts/stateful-stream-processing/ (opens new window)
编辑 (opens new window)
上次更新: 2026/05/20, 10:14:02
最近更新
01
AI 写的企业级组件不敢用?我替你验过了
05-19
02
感觉你因为AI退化了?用Redis SCAN源码打脸AI时代的孔乙己
05-17
03
千万级交易流水慢查询综合治理实践
05-14
更多文章>
Theme by Vdoing | Copyright © 2025-2026 Evan Xu | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×
×