Angular/Groovy应用9000条Step数据序列化及渲染性能优化咨询
优化Angular/Groovy应用性能(9000条Step数据处理)
哇,150秒的耗时确实让人头疼,咱们从后端Groovy序列化和前端Angular渲染两个核心方向入手,结合你的代码给出具体优化方案:
一、后端Groovy & Hibernate优化
1. 解决Hibernate N+1查询问题(最可能的性能瓶颈)
你当前用Step.findAllByUserName(userName)查询,默认是懒加载关联对象(比如step.week、step.names),这意味着在marshalMinimalStep循环里,每处理一条Step都会触发额外的数据库查询——9000条数据就会产生上万次数据库请求,这绝对是性能杀手!
改成Fetch Join一次性加载所有关联数据,用HQL或者Criteria查询:
def getAll(String userName) { // 用HQL一次性查询Step及其关联的week、names,避免N+1 def steps = Step.executeQuery("SELECT s FROM Step s JOIN FETCH s.week JOIN FETCH s.names WHERE s.userName = :userName", [userName: userName]) def payload = marshalSteps(steps) payload ?: NO_STEPS }
同时设置Hibernate的fetchSize,减少数据库往返次数:
// 如果用Criteria查询,可以设置fetchSize def criteria = Step.createCriteria() def steps = criteria.list(fetchSize: 500) { eq("userName", userName) fetch "week", JoinType.FETCH fetch "names", JoinType.FETCH }
2. 优化序列化逻辑
你现在手动循环构建Map的方式在9000条数据下开销很大,试试更高效的序列化方案:
- 用Jackson直接序列化:配置ObjectMapper只序列化需要的字段,比手动循环快得多:
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.SerializationFeature private static final ObjectMapper mapper = new ObjectMapper() .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) .addMixIn(Step.class, StepMinimalMixIn.class) // 用MixIn指定要序列化的字段,避免序列化多余属性 abstract class StepMinimalMixIn { abstract Long getId() abstract String getName() abstract List<Translation> getNames() abstract String getShortcut() abstract Double getWeek() abstract Boolean getIsRelevant() } private static def marshalSteps(List<Step> steps) { mapper.convertValue(steps, List.class) // 直接转成List<Map>返回 }
- 避免循环内重复操作:如果
marshalTranslation是处理每个step.names的方法,试试批量处理所有names,减少重复调用开销。
3. 强制分页(最立竿见影的优化)
9000条数据一次性返回完全没必要!后端实现分页,比如每页返回100条,前端通过分页控件或滚动加载获取数据:
def getAll(String userName, int page, int pageSize) { def steps = Step.createCriteria().list(max: pageSize, offset: (page-1)*pageSize) { eq("userName", userName) fetch "week", JoinType.FETCH fetch "names", JoinType.FETCH order("name", "asc") // 后端提前排序,省得前端做 } [data: marshalSteps(steps), total: steps.totalCount] }
二、前端Angular渲染优化
1. 用虚拟滚动替代全量渲染
ng-repeat渲染9000条DOM元素会直接卡死浏览器,因为每个元素都会创建watchers,DOM节点数量也爆炸。改用虚拟滚动,只渲染当前视口内的元素:
- 可以用成熟的库比如
angular-ui-scroll,或者自定义简单的虚拟滚动逻辑,把DOM节点数量控制在几十条以内。
2. 减少Watchers数量
- 对不会变化的数据用单向绑定:把
{{step.week}}改成{{::step.week}},translations="step.names"如果是静态数据也可以改成单向绑定translations="::step.names"。 - 移除前端排序:把
orderBy:'name'移到后端处理(刚才后端代码里已经加了order("name", "asc")),前端不用再做大量排序计算。
3. 优化DOM结构
你的ng-repeat嵌套了div.col-md-10,可以简化结构,减少不必要的DOM层级:
<ul class="list-group" ng-show="stepCtrl.steps.length > 0"> <li class="list-group-item overview col-md-10" ng-repeat="step in ::stepCtrl.steps track by step.id" ng-click="stepCtrl.edit(step.id)"> <display-name translations="::step.names"></display-name> <span class="pull-right">{{::step.week}}</span> </li> </ul>
总结优化优先级
- 先做后端分页,把单次处理的数据量从9000降到100以内,耗时会直接砍到原来的几十分之一;
- 解决Hibernate的N+1查询问题,消除上万次数据库请求;
- 优化前端渲染,用虚拟滚动减少DOM数量;
- 最后再优化序列化细节。
内容的提问来源于stack exchange,提问作者blown.eye
相关产品推荐
相关产品推荐

