Golang 10w+excel大数据量使用管道缓冲并发导入

前序

  1. 使用策略模式适应不同的环境、灵活性和可维护性等
  2. 引入缓冲层导入比不是缓冲层导入快一倍+ 10w数据3s
  3. 引入这个文件开箱即用
  4. 使用go-zero下的threading控制并发和安全并发

代码如下

import.go

package excel

import (
	"fmt"
	"sync"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/xuri/excelize/v2"
	"github.com/zeromicro/go-zero/core/threading"
)

type ExcelImportImpl interface {
	ColumnsUnique() []int
	ColumnsFilter(row []string) bool
	AddData(row [][]string) (uniqNum uint32, err error)
}

type ExcelImportBuilder struct {
	file       *excelize.File
	ProgressTx string // 进度条
	ctx        *gin.Context
	Total      uint32
	Success    uint32
	Fail       uint32
	Unique     uint32
}

func NewExcelImportBuilder(ctx *gin.Context, f *excelize.File) (*ExcelImportBuilder, error) {
	return &ExcelImportBuilder{
		file:       f,
		ProgressTx: uuid.New().String(),
		ctx:        ctx,
	}, nil
}

// start 从第几行开始读
func (builder *ExcelImportBuilder) Save(start int, impl ExcelImportImpl) error {
	rows, err := builder.file.Rows(builder.file.GetSheetName(builder.file.GetActiveSheetIndex()))
	if err != nil {
		return err
	}

	// 1000份数据写入管道
	dataChan := make(chan [][]string, 100)
	threading.GoSafe(func() {
		defer close(dataChan)
		defer rows.Close()
		columnsUnique := impl.ColumnsUnique()
		num := 1000
		results, count, i := make([][]string, 0, num), 0, 0
		dataAll := make(map[int]map[string]struct{}, len(columnsUnique))
		for rows.Next() {
			i++
			if start >= i {
				continue
			}
			builder.Total++
			row, err := rows.Columns()
			if err != nil {
				break
			}

			// 长度判断和过滤
			if len(row) <= 0 || impl.ColumnsFilter(row) {
				builder.Fail++
				continue
			}

			// 去重某些列
			var uniqueResult bool
			for i := 0; i < len(columnsUnique); i++ {
				item := columnsUnique[i]
				key := row[item]
				seen := dataAll[item]
				if seen == nil {
					seen = make(map[string]struct{})
				}
				// 判断是否存在
				_, ok := seen[key]
				if ok {
					uniqueResult = true
					continue
				}

				seen[key] = struct{}{}
				dataAll[item] = seen
			}
			if uniqueResult {
				builder.Unique++
				continue
			}

			// 加入队列切片
			count++
			results = append(results, row)

			// 重置计算器和切片
			if count-num == 0 {
				dataChan <- results
				count = 0
				results = make([][]string, 0, num)
			}
		}
		if count > 0 {
			dataChan <- results
		}
	})

	// 插入数据
	wg := sync.WaitGroup{}
	task := threading.NewTaskRunner(100)
	for rows := range dataChan {
		rows := rows
		// 并发插入
		wg.Add(1)
		task.Schedule(func() {
			defer wg.Done()
			// 插入数据
			fmt.Println(rows)
			uniqNum, err := impl.AddData(rows)
			if err != nil {
				builder.Fail++
			} else {
				builder.Success += uint32(len(rows)) - uniqNum
			}
			builder.Unique += uniqNum
		})
	}
	wg.Wait()

	return nil
}

service.go

type whiteImprot struct {
	c           *gin.Context
	creatorName string
	creatorId   int32
}

func (w *whiteImprot) ColumnsUnique() []int {
	return []int{
		0,
	}
}
func (w *whiteImprot) ColumnsFilter(row []string) bool {
	return false
}
func (w *whiteImprot) AddData(row [][]string) (uniqNum uint32, err error) {
	names := lo.Map[[]string, string](row, func(item []string, index int) string {
		return item[0]
	})
	return model.NewWhiteListModel(w.c).CreateUniqWhite(names, w.creatorName, w.creatorId)
}

// 引入缓冲层导入比不是缓冲层导入快一倍+ 10w数据3s
func ImportWhite(c *gin.Context, f *excelize.File) (*response.ImportWhiteResp, error) {
	var (
		response    = response.ImportWhiteResp{}
		creatorName = c.GetString("user_name")
		creatorId   = int32(c.GetInt("user_id"))
	)

	whiteImprot := &whiteImprot{
		c:           c,
		creatorName: creatorName,
		creatorId:   creatorId,
	}
	builder, err := excel.NewExcelImportBuilder(c, f)
	if err != nil {
		return nil, err
	}

	err = builder.Save(1, whiteImprot)
	if err != nil {
		return nil, err
	}
	response.FailTotal = builder.Fail + builder.Unique
	response.SuccessTotal = builder.Success

	return &response, nil
}

在这里插入图片描述

导出你看另一篇文档

https://blog.csdn.net/qq_39272466/article/details/132203333?spm=1001.2014.3001.5501

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值