百万级别数据Excel导出优化

前提

这篇文章不是标题党,下文会通过一个仿真例子分析如何优化百万级别数据Excel导出。

笔者负责维护的一个数据查询和数据导出服务是一个相对远古的单点应用,在上一次云迁移之后扩展为双节点部署,但是发现了服务经常因为大数据量的数据导出频繁Full GC,导致应用假死无法响应外部的请求。因为某些原因,该服务只能够分配2GB的最大堆内存,下面的优化都是以这个堆内存极限为前提。通过查看服务配置、日志和APM定位到两个问题:

  1. 启动脚本中添加了CMS参数,采用了CMS收集器,该收集算法对内存的敏感度比较高,大批量数据导出容易瞬间打满老年代导致Full GC频繁发生。
  2. 数据导出的时候采用了一次性把目标数据全部查询出来再写到流中的方式,大量被查询的对象驻留在堆内存中,直接打满整个堆。

对于问题1咨询过身边的大牛朋友,直接把所有CMS相关的所有参数去掉,由于生产环境使用了JDK1.8,相当于直接使用默认的GC收集器参数-XX:+UseParallelGC,也就是Parallel Scavenge + Parallel Old的组合然后重启服务。观察APM工具发现Full GC的频率是有所下降,但是一旦某个时刻导出的数据量十分巨大(例如查询的结果超过一百万个对象,超越可用的最大堆内存),还是会陷入无尽的Full GC,也就是修改了JVM参数只起到了治标不治本的作用。所以下文会针对这个问题(也就是问题2),通过一个仿真案例来分析一下如何进行优化。

冷饭新炒:理解Snowflake算法的实现原理

前提

Snowflake(雪花)是Twitter开源的高性能ID生成算法(服务)。

上图是SnowflakeGithub仓库,master分支中的REAEMDE文件中提示:初始版本于2010年发布,基于Apache Thrift,早于Finagle(这里的FinagleTwitter上用于RPC服务的构建模块)发布,而Twitter内部使用的Snowflake是一个完全重写的程序,在很大程度上依靠Twitter上的现有基础架构来运行。

2010年发布的初版Snowflake源码是使用Scala语言编写的,归档于scala_28分支。换言之,大家目前使用的Snowflake算法原版或者改良版已经是十年前(当前是2020年)的产物,不得不说这个算法确实比较厉害scala_28分支中有介绍该算法的动机和要求,这里简单摘录一下:

动机:

  • Cassandra中没有生成顺序ID的工具,Twitter由使用MySQL转向使用Cassandra的时候需要一种新的方式来生成ID(印证了架构不是设计出来,而是基于业务场景迭代出来)。

要求:

  • 高性能:每秒每个进程至少产生10KID,加上网络延迟响应速度要在2ms内。
  • 顺序性:具备按照时间的自增趋势,可以直接排序。
  • 紧凑性:保持生成的ID的长度在64 bit或更短。
  • 高可用:ID生成方案需要和存储服务一样高可用。

下面就Snowflake的源码分析一下他的实现原理。

一个基于RabbitMQ的可复用的事务消息方案

前提

分布式事务是微服务实践中一个比较棘手的问题,在笔者所实施的微服务实践方案中,都采用了折中或者规避强一致性的方案。参考Ebay多年前提出的本地消息表方案,基于RabbitMQMySQLJDBC)做了轻量级的封装,实现了低入侵性的事务消息模块。本文的内容就是详细分析整个方案的设计思路和实施。环境依赖如下:

  • JDK1.8+
  • spring-boot-start-web:2.x.xspring-boot-start-jdbc:2.x.xspring-boot-start-amqp:2.x.x
  • HikariCP:3.x.xspring-boot-start-jdbc自带)、mysql-connector-java:5.1.48
  • redisson:3.12.1

谈谈对分布式事务的一点理解和解决方案

前提

最近,工作中要为现在的老系统做拆分和升级,刚好遇到了分布式事务、幂等控制、异步消息乱序和补偿方案等问题,刚好基于实践结合个人的看法记录一下一些方案和思路。