时间:指系统的处理耗时,或引申为CPU使用率、GC平均耗时等。
空间:指内存、硬盘、带宽等资源。
空间换时间,即指通过消耗更多空间类资源的手段,降低时间类资源的消耗。时间换空间也同理。 它们泛指这一类的优化思想,并不仅限于某一两种特定的方式。
首先,提出一个可能并不严谨的结论:“在优化程度相同的前提下,系统对时间和空间上的需求是可以相互转化的。”
这里有一个大前提,“优化程度相同”。因为有很多优化方法既能优化时间,也能优化空间。比如很多年前,许多公司把自己的php服务换成go语言。或本来的代码质量太差,有大量无谓的、错误的逻辑。
时间与空间的相互转换
“时间换空间”还是“空间换时间”呢? 也许有人会说,当然是“空间换时间”了。服务越快,性能越高嘛。
再次重申,时常牢记我们做性能优化的目的是什么。
目的一: 降低服务成本
目的二: 使某些技术指标达到要求或提升用户体验
有很多关于优化的分享讲“平均耗时优化了xxx毫秒”,这便是实现了目的二,即通过性能优化,降低了接口耗时,提升了用户体验。
换句话说,在这种情况下,只要缩短了系统耗时,也就相当于“提升了性能”。
但有没有例外呢?请看下面例子:
有一个从数据库中定时拉取数据,然后进行计算的定时任务,每次拉取的数据量很大,这时内存占用量会达到90%+,很容易OOM。
这时作了如下优化: 把一个拉取任务再拆成10个子任务,每个任务都只拉取原来10%的数据。
我们很容易知道,由于每次执行SQL语句的网络等损耗,这样做任务的总耗时几乎一定会增加。那这样算是反向优化了吗?
当然不是。每次只拉取10%,需要加载到内存里的数据量也只是原来的10%。这样就避免了OOM的风险。总结一下,这样也是实现了上面的目的二,即使得指标指标达到要求。(当然,这样的前提是增加的耗时仍然能满足系统的要求)
上面说这么多只是为了引出下面这段话: 在整个系统中,对时间和空间的利用往往是可以相互转化的。有时可以用空间换时间,有时则是时间换空间。无论是哪种,都要先明确优化的目的,再针对性开展。
空间换时间
尽管上面说了这么多,在互联网行业的实践中,绝大多数情况应该还是空间换时间。最经典的例子就是:缓存。
缓存本质上是一种数据的冗余。原始的数据已经存在数据库里了,但我还是选择在缓存里又存一份,而且存的这份还有可能数据不全。但换来的是读取数据的平均时间极大缩短。因此它是一种最典型的空间换时间。
数据结构中的hash table也是对空间换时间思想的使用。了解hash table实现原理的人都知道,它是一个相对比较稀疏的空间,也就是在整个hash table所占的空间里,并非每块地址上都有数据,可能只有不到50%的利用率。但换来的是O(1)的查询时间复杂度。
再举一个缓存的例子。假如现在有10G的原始数据和一个2G内存的缓存,但目前接口的耗时还是不达标,需要优化。有一个可行的思路就是直接使用10G内存的缓存,全量加载所有数据。当然,这个方法会造成成本的上升,但接口耗时一定会缩短。这时就又回到最初的问题:“性能优化的目的是什么 ”
关于资源规划
在空间与时间的互换中,还有另一个隐藏的目的,就是充分利用系统资源。
比较理想的情况下,各种系统资源都稳定在60-80%的范围内,在成本和系统稳定性上可以得到一个较为不错的平衡。但假如某一种资源的消耗量特别少,就可以采用时间与空间互换的思想进行重新规划。
比如CPU占用率65%,但内存占用率只有30%。 首先看能不能降低机型配置。如果不好降,就可以考虑一些空间换时间的思路了,即通过消耗更多的内存来提升CPU效率。
举个简单的例子:系统里某一个高频调用的功能需要维护一个进程内KV Cache,并限制了300MB的大小,命中率是60%,如果未命中就需要去读MySQL;现在既然内存富余,将Cache限制提升到1G,这时命中率提升到95%,读MySQL的次数极大减少,系统耗时就下降了。