使用缓存
缓存在系统设计中不可缺少,实现了以空间换时间,提高了系统的性能和减少了系统的处理时间。
例如这样的一个简单问题:我们要计算1到n(n>1)自然数的和? 这个可以很简单的通过编写程序实现
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
var input string
var output int
var num int
for {
output = 0
fmt.Println("Please enter calc num: ")
fmt.Scanln(&input)
startTime := time.Now()
num, _ = strconv.Atoi(input)
output = sum(num)
timeCost := time.Since(startTime)
fmt.Println(output)
fmt.Println("run cost time", timeCost)
fmt.Println("\n")
}
}
func sum(num int) (sum int) {
for i := 1; i <= num; i++ {
sum += i
}
return
}
这里不做性能测试了,简单的通过一定的数据执行,观察下结果
怎么使用缓存来优化这个程序,提高程序的处理性能?借用缓存,把每次计算的结果都记录下来,再次计算该数时,从缓存中获取结果。使用缓存优化后的代码
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
var input string
var output int
var num int
var cache = make(map[int]int)
for {
output = 0
fmt.Println("Please enter calc num: ")
fmt.Scanln(&input)
startTime := time.Now()
num, _ = strconv.Atoi(input)
if data, ok := cache[num]; ok {
output = data
} else {
output = sum(num)
cache[num] = output
}
timeCost := time.Since(startTime)
fmt.Println(output)
fmt.Println("run cost time", timeCost)
fmt.Println("\n")
}
}
func sum(num int) (sum int) {
for i := 1; i <= num; i++ {
sum += i
}
return
}
添加缓存后的执行结果
通过对比以上的执行结果,发现程序的执行效率有了很大的提升,这就是缓存的存在的理由。以存储的方式,减少cpu的运算。
如果每次都输入不同的值,这样缓存就无法命中,会导致cache变得越来越大,最终会内存不足。
怎么优化这个问题呢?
首先通过让cache中的数据失效, 基于以上的示例,做简单的优化,我的处理思路是设置一个定时器,到期后,map中的key全部失效。
package main
import (
"fmt"
"strconv"
"time"
)
func main() {
var input string
var output int
var num int
var cache = make(map[int]int)
go gc(cache)
for {
output = 0
fmt.Println("start cache", cache)
fmt.Println("Please enter calc num: ")
fmt.Scanln(&input)
startTime := time.Now()
num, _ = strconv.Atoi(input)
if data, ok := cache[num]; ok {
output = data
} else {
output = sum(num)
cache[num] = output
}
timeCost := time.Since(startTime)
fmt.Println(output)
fmt.Println("run cost time", timeCost)
fmt.Println("\n")
fmt.Println("end cache", cache)
}
}
func sum(num int) (sum int) {
for i := 1; i <= num; i++ {
sum += i
}
return
}
func gc(dic map[int]int) {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
<-ticker.C
fmt.Println("cache 失效")
for key, _ := range dic {
delete(dic, key)
}
}
}
使用goroutine运行一个定时任务,5秒清理一次map,这个cache数据过期处理的粒度有些过大,在做cache设计时,不能这样处理,需要将数据过期的粒度控制在存储的数据这一层。这里不做展开,在后边实现cache时,在做实现说明。这里只是简单的展示了缓存数据过期处理。看一下执行的结果
为了防止缓存过大,可以使用通过限制一个阀值和制定一个淘汰规则,这样就可以保证缓存不会过大
再次对这三种缓存的淘汰算法做了简单的说明,其中会设计到一些数据结构的使用,在这里先不做展开,后边再开一个专题来介绍。