这篇文章不是标题党,下文会通过一个仿真例子分析如何优化百万级别数据Excel
导出。
笔者负责维护的一个数据查询和数据导出服务是一个相对远古的单点应用,在上一次云迁移之后扩展为双节点部署,但是发现了服务经常因为大数据量的数据导出频繁Full GC
,导致应用假死无法响应外部的请求。因为某些原因,该服务只能够分配2GB
的最大堆内存,下面的优化都是以这个堆内存极限为前提。通过查看服务配置、日志和APM
定位到两个问题:
CMS
参数,采用了CMS
收集器,该收集算法对内存的敏感度比较高,大批量数据导出容易瞬间打满老年代导致Full GC
频繁发生。对于问题1咨询过身边的大牛朋友,直接把所有CMS
相关的所有参数去掉,由于生产环境使用了JDK1.8
,相当于直接使用默认的GC
收集器参数-XX:+UseParallelGC
,也就是Parallel Scavenge + Parallel Old
的组合然后重启服务。观察APM
工具发现Full GC
的频率是有所下降,但是一旦某个时刻导出的数据量十分巨大(例如查询的结果超过一百万个对象,超越可用的最大堆内存),还是会陷入无尽的Full GC
,也就是修改了JVM
参数只起到了治标不治本的作用。所以下文会针对这个问题(也就是问题2),通过一个仿真案例来分析一下如何进行优化。
Snowflake
(雪花)是Twitter
开源的高性能ID
生成算法(服务)。
上图是Snowflake
的Github
仓库,master
分支中的REAEMDE
文件中提示:初始版本于2010
年发布,基于Apache Thrift
,早于Finagle
(这里的Finagle
是Twitter
上用于RPC
服务的构建模块)发布,而Twitter
内部使用的Snowflake
是一个完全重写的程序,在很大程度上依靠Twitter
上的现有基础架构来运行。
而2010
年发布的初版Snowflake
源码是使用Scala
语言编写的,归档于scala_28
分支。换言之,大家目前使用的Snowflake
算法原版或者改良版已经是十年前(当前是2020
年)的产物,不得不说这个算法确实比较厉害。scala_28
分支中有介绍该算法的动机和要求,这里简单摘录一下:
动机:
Cassandra
中没有生成顺序ID
的工具,Twitter
由使用MySQL
转向使用Cassandra
的时候需要一种新的方式来生成ID
(印证了架构不是设计出来,而是基于业务场景迭代出来)。要求:
10K
个ID
,加上网络延迟响应速度要在2ms
内。ID
的长度在64 bit
或更短。ID
生成方案需要和存储服务一样高可用。下面就Snowflake
的源码分析一下他的实现原理。
分布式事务是微服务实践中一个比较棘手的问题,在笔者所实施的微服务实践方案中,都采用了折中或者规避强一致性的方案。参考Ebay
多年前提出的本地消息表方案,基于RabbitMQ
和MySQL
(JDBC
)做了轻量级的封装,实现了低入侵性的事务消息模块。本文的内容就是详细分析整个方案的设计思路和实施。环境依赖如下:
JDK1.8+
spring-boot-start-web:2.x.x
、spring-boot-start-jdbc:2.x.x
、spring-boot-start-amqp:2.x.x
HikariCP:3.x.x
(spring-boot-start-jdbc
自带)、mysql-connector-java:5.1.48
redisson:3.12.1
最近,工作中要为现在的老系统做拆分和升级,刚好遇到了分布式事务、幂等控制、异步消息乱序和补偿方案等问题,刚好基于实践结合个人的看法记录一下一些方案和思路。