分而治之


当使用数据库(尤其是PostgreSQL)时,我有一个想法(使用Go GoP)从表中并行选择数据。 我想知道,“是否可以在单个goroutine中扫描样本行?”

事实证明,无法在gourutin中同时调用func(* Rows)Scan 。 基于这一限制,我决定与扫描行同时进行其他处理,特别是准备结果数据。

因为 扫描根据指针将数据堆叠起来,我决定制作两个切片(稍后再解释为什么要分割两个切片),在这两个切片之间切换扫描,而其余的gourutin将处理已选择的数据。

最初,我需要知道示例列的数量:

columns, err = rows.Columns() count := len(columns) 

接下来,我创建两个具有值和指向这些值的指针的切片(在行扫描期间将在其中添加数据):

 values := make([]interface{}, count) valuesPtrs := make([]interface{}, count) values_ := make([]interface{}, count) valuesPtrs_ := make([]interface{}, count) for i := range columns { valuesPtrs[i] = &values;[i] valuesPtrs_[i] = &values;_[i] } 

在此示例中,我将选择结果添加到map [string]字符串中,其中列名将作为键。 您可以使用指示类型的特定结构,但是由于 该出版物的目的是从军政府中找出拟议方法的可行性,让我们关注地图中的选择。

接下来,我将分离两个gorutin,其中一个将形成结果图:

 func getData(deleteNullValues bool, check, finish chan bool, dbData chan interface{}, columns []string, data *[]map[string]string) { lnc := len(columns) for <-check { row := make(map[string]string) for i := 0; i < lnc; i++ { el := <-dbData b, ok := el.([]byte) if ok { row[columns[i]] = string(b) } else { if el == nil { if deleteNullValues == false { row[columns[i]] = "" } } else { row[columns[i]] = fmt.Sprint(el) } } } *data = append(*data, row) } finish <- true } 

第二个将使用由Scan生成的值在两个切片之间切换,并将它们发送到上一个gourutin的通道(形成结果):

 func transferData(values, values_ []interface{}, dbData chan interface{}, swtch, working, check chan bool) { for <-working { check <- true switch <-swtch { case false: for _, v := range values { dbData <- v } default: for _, v := range values_ { dbData <- v } } } } 

主要过程将在指针切片之间切换并选择数据:

 for rows.Next() { switch chnl { case false: if err = rows.Scan(valuesPtrs...); err != nil { fmt.Printf("rows.Scan: %s\n%s\n%#v\n", err, query, args) return nil, nil, err } default: if err = rows.Scan(valuesPtrs_...); err != nil { fmt.Printf("rows.Scan: %s\n%s\n%#v\n", err, query, args) return nil, nil, err } } working <- true swtch <- chnl chnl = !chnl } 

在数据库中,我形成了一个包含32列的表,并向其中添加了10万行。
测试的结果(采样数据50次时),我得到了以下数据:
花费时间:1m8.022277124s-使用单个切片对结果进行采样
花费时间:1m7.806109441s-使用两个切片对结果进行采样

随着迭代次数增加到100:
花费时间:2m15.973344023s-使用单个切片选择结果
花费时间:2m15.057413845s-使用两个切片对结果进行采样

差异随着数据量的增加和表中列的增加而增加。
但是,观察到相反的结果是数据量减少或表的列数减少,这在原则上是可以理解的,因为 准备步骤的开销和古鲁丁部门“吃掉了”宝贵的时间,结果得到了平衡。

至于两个切片和两个gorutin:我对大量切片进行了测试,但是采样时间增加了,因为显然,getData和transferData函数处理数据的速度比从数据库中扫描值更快。 因此,即使有大量内核,也没有必要为Scan和其他goroutine添加新切片(非常复杂的数据量除外)。

在github代码中,我给出了这种方法的有效示例 。 我的任务还使用了其他软件包,当我从上面清除了这些软件包时,但是主要思想不应该因此受到影响。

总的来说,我希望有兴趣的社区提出建设性的批评。 谢谢你

Source: https://habr.com/ru/post/zh-CN485056/


All Articles