优化一个已有的 Go 程序 | 青训营

63 阅读2分钟

本文将通过一个例子简单介绍下如何优化一个已有的Go程序,通过分析代码、优化算法和数据结构、并发编程等方面进行性能优化和资源占用减少。通过这些实践步骤,你可以有效改善Go程序的运行效率和资源利用率。

一、程序分析

本例中,我们的程序是一个查询服务端的设计,该服务端将根据用户给定的密钥和目标查询,进行身份验证以及目标的 value 值进行返回

项目结构
- project
  |- main.go
  |- core
     |- auth.go
     |- search.go
  |- go.mod
  |- go.sum

在项目中主要包含 auth 和 search 两部分,因此整个项目逻辑较为清晰,我们的优化也就从这两方面进行入手

二、优化查询逻辑

在查询的核心部分如下

func (s *server) Search(id int) string {
	var result string
	s.DB.First(&result, id)
	return result
}

可以发现,这里的查询过程虽然足够简单,但同数据库的交互过程相对较慢,因此我们可以通过构建一个类似 cache 的结构对某些高频数据进行更快的返回

type pair struct {
	id    int
	value string
}

type server struct {
	DB        *gorm.DB
	cacheSize int
	cache     []pair
}

func (s *server) Search(id int) string {
	var value string
	if s.cache[id%s.cacheSize].id == id {
		return s.cache[id%s.cacheSize].value
	}
	s.DB.First(&value, id)
	s.cache[id%s.cacheSize] = pair{
		id:    id,
		value: value,
	}
	return value
}

三、优化验证逻辑

要提升验证的效率,官方来说可以使用更高效的算法和数据结构,优化数据访问方式,进行并行处理,并考虑缓存结果和数据预处理等方法。不过验证部分一般是很难进行优化的。

四、并行处理

虽然这个服务包含两个核心服务验证以及查询,核心代码部分如下

func (s *server) serve(token string, id int) (string, error) {
	err := s.Auth(token)
	if err != nil {
		return "", err
	}

	value := s.Search(id)

	return value, nil

}

虽然这样一步一步来可以正确的实现我们的服务逻辑,不过一般来说,查询和验证的代码是不相干扰的,这意味着我们可以平行处理


func (s *server) Search(res chan<- string, id int) {
	...
	res <- value
	...
}

func (s *server) Auth(e chan<-error, token string)  {
	...
	e <- err
	...
}

func (s *server) serve(token string, id int) (string, error) {
	valChan := make(chan string)
	errChan := make(chan error)

	go s.Auth(errChan, token)
	go s.Search(valChan, id)

	err := <-errChan
	val := <-valChan

	if err != nil {
		return "", err
	}
	return val, nil
}

通过 channel 来获取查询和验证的结果,最终实现二者的并行处理,由此我们的服务应该可以以一个较高的效率执行下去了

当然以上只是一些简单的优化方式,用到了缓存的思想,以及并行的方式。为了进行更加精细的优化需要用到 Go 的性能分析工具(如pprof)获得程序在不同方面的性能数据,如CPU使用率、内存分配等。根据分析结果,找到性能瓶颈所在的代码区域,确定优化的重点。