Golang整合Doris查询功能封装

Golang整合Doris查询功能封装
一、目标
  • 实现基于配置查询Doris数据库
  • 实现将查询结构封装:[]map[string]interface{},支持单条/多条结果集
  • 支持interface转目标struct,实现结果集解析
  • 实现数据分片,便于后续的分批处理
二、Doris查询功能
  • 数据库配置

    用于连接数据库,可以抽离到公共配置,便于统一维护管理,减少耦合

    type DorisDB struct {
    	UserName string // 用户名
    	Password string // 密码
    	Network  string // TCP
    	Server   string // server
    	Port     int    // 端口
    	DataBase string // 数据库
    }
    
  • 建立连接

    连接数据库,需要指定数据库,依赖上面的配置建立连接,为数据查询做准备

         //1.创建连接
    	dsn := fmt.Sprintf("%s:%s@%s(%s:%d)/%s", d.db.UserName, d.db.Password, d.db.Network, d.db.Server, d.db.Port, d.db.DataBase)
    	db, err := sql.Open("mysql", dsn)
    	defer func(db *sql.DB) {
    		err := db.Close()
    		if err != nil {
    
    		}
    	}(db)
    
    	if err != nil {
    		d.log.Fatal("err", "info:", err)
    	}
    
  • 数据查询

    sqlString:支持外部传入的sql自定义查询语句,灵活自由,便于扩展

    db.Query: 基于上面的建连,进行数据查询

    dataRows.Close(): 关闭数据库查询,节约资源

    	//2.数据查询
    	dataRows, err := db.Query(sqlString)
    
    	defer dataRows.Close()
    
    	if err != nil {
    		d.log.Fatal("err", "info:", err)
    	}
    
  • 结果集封装

    1、输入:数据查询db.Query(sqlString)返回的结果

    2、输出:[]map[string]interface{}存储返回

    3、核心:

    3.1// 预设取值地址
    for i := range vs {
    	scans[i] = &vs[i]
    }
    3.2_ = dataRows.Scan(scans...)// 取值填入预设的结构体
    3.2、更新目标存储:
    for i, col := range vs {......}
    

        /*    
         * 进行结果集处理,使用[]map[string]interface{}存储
    	 */
    	// 读取字段名称
    	columns, err := dataRows.Columns()
    	vs := make([]sql.RawBytes, len(columns))
    	scans := make([]interface{}, len(columns))
    	// 预设取值地址
    	for i := range vs {
    		scans[i] = &vs[i]
    	}
    	var result []map[string]interface{}
    	for dataRows.Next() {
    		// 取值填入预设的结构体
    		_ = dataRows.Scan(scans...)
    		each := make(map[string]interface{})
    		for i, col := range vs {
    			if col != nil {
    				each[columns[i]] = FilterHolder(string(col))
    			} else {
    				each[columns[i]] = nil
    			}
    		}
    		result = append(result, each)
    	}
    
  • 数据过滤

    用于拦截处理数据中的无效数据,脏数据等

    // FilterHolder 过滤/xfffd 其他过滤可以自行拓展
    func FilterHolder(content string) string {
    	newContent := ""
    	for _, value := range content {
    		if value != 65533 {
    			newContent += string(value)
    		}
    	}
    	return newContent
    }
    
三、结构体转interface <—> interface转结构体

灵活使用json.Marshal和 json.Unmarshal能够帮助我们从一个固定的数据结果转为另一个数据相近的结构

  • 结构体转interface :
        // 例如:
        var data []*map[string]string
    	// 10 条数据
    	for i := 0; i < 10; i++ {
    		data = append(data, &map[string]string{
    			"a": "1",
    		})
    	}
    	if data == nil {
    		data = make([]*map[string]string, 0)
    	}
        // 目标interface
    	var interfaceItem []*interface{}
    	b, _ := json.Marshal(&data)
    	err := json.Unmarshal(b, &interfaceItem)
    	if err != nil {
    		fmt.Println("err", err)
    	}
    
  • interface转结构体
        // res :interface
        resByre, resByteErr := json.Marshal(res)
    	if resByteErr != nil {
    		fmt.Printf("%v", resByteErr)
    		return
    	}
    	// 目标结构:struct、map
    	var newData [][]*map[string]string
    	jsonRes := json.Unmarshal(resByre, &newData)
    	if jsonRes != nil {
    		fmt.Printf("%v", jsonRes)
    		return
    	}
    
四、数据分片【假设Doris数据庞大】

在实际生产中,数据产生的量级往往是巨大的,一般来说,每次查询的数据量级也是很大的,如果数据消费线程的效率低,容易引发数据的实时性,系统的稳健性等问题,因此可以考虑对数据进行分片处理,便于数据每次消费过大导致的资源消费、系统性能问题。

  • 目标
    • 支持自定义分配份数
    • 支持根据默认参数分配
  • 代码
    // 以[]*interface为输入,[][]*interface为输出
    const (
    	DEFAULT_DIVLEN          = 2 // 默认份数
    	DEFAULT_DATALEN float64 = 3 // 默认的每一份最大包含数量
    )
    
    /**
      如果defaultFlag: true 则需要传入份数,根据份数divLen 更新每份长度
      如果defaultFlag: false 则受用默认参数计算,divLen传入0值即可,并且不生效,根据默认的每份长度更新divLen
    */
    func transFormat(data []*interface{}, divLen float64, defaultFlag bool) [][]*interface{} {
    	if len(data) <= DEFAULT_DIVLEN || divLen == 0 {
    		return [][]*interface{}{
    			1: data,
    		}
    	}
    	quantity := DEFAULT_DATALEN
    	if defaultFlag {
    		dLen := float64(len(data)) / DEFAULT_DATALEN // 使用默认的每份的数量,则divLen不生效
    		divLen = math.Ceil(dLen)
    	}
    	quantity = math.Ceil(float64(len(data)) / divLen) // 不使用默认,根据divLen计算
    	fmt.Println(quantity)
    	var segmens = make([][]*interface{}, 0)
    	end := 0
    	newDivLen := int(divLen)
    	for i := 1; i <= newDivLen; i++ {
    		qu := i * int(quantity)
    		if i != newDivLen {
    			segmens = append(segmens, data[i-1+end:qu])
    		} else {
    			segmens = append(segmens, data[i-1+end:])
    		}
    		end = qu - i
    	}
    	return segmens
    
    }
    
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玉言心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值