最近在对一个已经运行了4年的老项目进行性能优化,这是一个app,我主要还是从后台优化。这个项目后台提供的API并不多,只有十几个,但是性能非常差。有些API连每分钟2000的request都扛不住,虽然后台的并发node已经增加到8个。一看代码就知道为什么了,原来这些API都需要返回很多的数据,而且是级联的数据,就像你要获得一个“军”的数据,“军”下面又有“师”,然后是旅团营连排,这样的树状结构,大概有5层,而且是一对多的关系。原来的代码中是先获得第一层的数据,再循环获得下面的第二层,然后是循环获得第三层,依次类推。这样带来了很多的数据库访问和循环操作。结果就是这个API不仅慢而且非常耗CPU。
一开始看到这样的结构,我是排斥的。。。。一对多如果写SQL来join的话,就会join出很多的数据来,因为毕竟是相乘的关系。之前就有这种SQL把服务整挂的经验,当时的情景是用用户表去join电话表,地址表,eamil表等,一个人可以有多个电话,地址,email。。。所以是一对多的关系,想要一下子全都拿出来,结果在测试环境中测试人员一直往同一个用户下面加地址,电话等,结果就是这个人有几百个地址,几百个电话。。。。最后join出几百万条数据,hibernate搞不定这个数据量。
回到这次的问题,怎么办,这个是比那个更多的join。首先数据量如果不大的话,我把它写成一个sql,用很多的join应该是没有关系的。于是去找了公司的DBE一块商量,DBE说先用这样的方式试一下。于是我开始写SQL,join了七八张表,用MyBatis。开始的自己测试的时候发现还可以,给性能测试一跑就完了,刚开始跑几分钟CPU就100%,后来分析了一下果然数据量还是很大的,性能测试的数据是从产品环境拷过来的。就SQL的执行时间而言,并不慢,因为join的地方都有索引,也规避了一些可能造成低效的写法,主要的问题还是出在CPU时间上。看来在这一点上MyBatis和Hibernate是一样的,毕竟需要把查询出来的那么多数据进行合并再生成bean。分析了其中的一个测试,发现这条查询可以查出50万条数据。经过分析,我决定把一个SQL分成两个,两次查询再将结果自己组合,这样其中一个查询的数据大概是40条,另外一个12000条。后来又觉得12000条的这个查询数据也很多,而且可能还