Golang并发把excel数据插入到数据库

golang对excel的下载和读取,批量插入、go并发处理

1.下载Excel文档并读取

由于我的excel文档在阿里云里,所以需要先用链接把excel下载到本地来,代码如下

func Download(url, excelName string) {
  res, err := http.Get(url)	//下载excel
  if err != nil {
    return
  }

  defer res.Body.Close()

  fileName := fmt.Sprintf("%v.%s", time.Now().Format("2006-01-02_15:04:05"), excelName)
  path := fmt.Sprintf("temp/%s", fileName)

  localFile, err := os.Create(path)	//在相对路径./temp下创建一个空excel文档
  if err != nil {
    return
  }

  _, err = io.Copy(localFile, res.Body) //把body里的内容复制到本地excel文件
  if err != nil {
    return
  }
}

//读取excel的函数
func CsvReadAll(csvFileName string) ([]string, [][]string, error) {
	csvFile, err := os.Open(csvFileName)
	if err != nil {
		return nil, nil, err
	}
	defer csvFile.Close()

	hdr := make([]string, 0)
	lines := make([][]string, 0)

	reader := csv.NewReader(csvFile)
	for {
		record, err := reader.Read()
		if err == io.EOF {
			break
		} else if err != nil {
			return nil, nil, err
		}

		if len(hdr) == 0 {
			hdr = record
		} else {
			lines = append(lines, record)
		}
	}

	return hdr, lines, nil
}

2.批量插入

每次批量插入100条数据到数据库

func InsertBatch(temp [][]string, length int) error {
	db := modelUtils.GetDB() //gorm库

	sqlStr := "INSERT INTO other_sn_mac(a, b, c) VALUES"
	for i := 0; i < length; i++ { // 批量插入
		if i == 0 {
			sqlStr += fmt.Sprintf("('%s', '%s', '%s')", temp[i][0], temp[i][1], temp[i][2])
		} else {
			sqlStr += fmt.Sprintf(",('%s', '%s', '%s')", temp[i][0], temp[i][1], temp[i][2])
		}
	}

	if err := db.Exec(sqlStr).Error; err != nil {
		return err
	}

	return nil
}

3.并发调用

每100个批量插入就放进channel中成为一个缓存,channel的缓存大小为50,每50个channel开启一个goroutine去并发执行,所以没5000条数据就开启一个goroutine并发执行

func ExcelBatchProcess(path string) {
  _, data, err := csvUtils.CsvReadAll(path)	//从excel读数据,data里存放的就是excel里除去顶部标题的数据
  if err != nil {
    return
  }
  
  tempData := make(map[int][][]string, 50)
  
  go func() {
    for j := 0; j < getModSecond; j++ {
      s <- tempData[j]
    }
    close(s)
  }()

  GoConcurrency(s)
}

func GoConcurrency(s chan [][]string) {
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			z, ok := <-s
			if !ok {
				break
			}

			err := model.InsertBatch(z, 100)
			if err != nil {
				log.Println("db insert err: ", err)
				return
			}
		}
	}()

	wg.Wait()
}

代码里我省去了业务逻辑代码,也省去了边角数据的处理逻辑

最终结果:

大概有40多万条数据,每5000条数据一个goroutine,那么大概需要80多个goroutine,总共花费的时间大概是1分钟,因为阿里云服务器为2核4G的性能,所以主要的性能瓶颈在于服务器上的数据库,虽然开启的数据库池,但是单条数据的插入时间是固定的

我还试了一下把同样40多万的数据插入到亚马逊云的数据库,要16分钟左右,因为服务器在国外,所以受网络影响较大