golang的mongo批量写入压测

     在实际生产环境下,某场景下,数以千万乃至上亿的数据会批量落入db,nosql,而根据数据的ttl过期,会在某个多久之后的时间给这批数据del.

加入每天9点都在批量insert—all,而前n天的数据恰好在这个时间节点(n天后的9点)ttl过期,观察db的io负载,会急速飚上去,io打的很高,甚至到100%。

根据生产上的如上情况,做了下面的测试:

 在实际场景中,操作nosql是很常用的,下面分享下压测mongo的场景和具体结果: 

测试用例代码如下:

 

package main

import (
    "fmt"
    "gopkg.in/mgo.v2/bson"
    "time"
)

func main() {

    for j := 0; j < 20; j ++ {
        go     pressTest()
    }
    pressTest()        //测试mongo写入压测
}

func pressTest (){

    defer timeCost()()        //注意,是对 timeCost()返回的函数进行调用,因此需要加两对小括号
    for j := 0; j < 2; j ++ {
        InsertMfAndroid("abc",1)
    }
}

// 耗时统计函数
func timeCost() func() {
    start := time.Now()
    return func() {
        tc := time.Since(start)
        fmt.Printf("time cost0000 = %v\n", tc)
    }
}


//可插入多条数据(先做循环插入mongo) MId string, token_arry, userIds []string, p, appId int
func InsertMfAndroid(newMid string, tokenNums int) {

    //defer timeCost()()        //注意,是对 timeCost()返回的函数进行调用,因此需要加两对小括号

        session := GetSession().Clone()
        defer session.Close()

        timeUnix := time.Now().Unix()
        timeUnixString := strconv.FormatInt(timeUnix,10)            // int64到string
        sendTime, _ := strconv.Atoi(timeUnixString)                   // string到int

        var docs []interface{}

        for j := 0; j < tokenNums; j ++  {
                docs = append(docs, bson.M{"mid": newMid, "t": "1","sut": sendTime, "cut": time.Now(), "expireTime":getExpireTime()})
        }

        collection := session.DB(beego.AppConfig.String("mongo::db")).C("token_msg")
        err := collection.Insert(docs...)
        if err != nil {
                beego.Error("mongo存储消息流水:[%s]", err)
        }
}

//获取过期时间
func getExpireTime() time.Time {

  timeStr := time.Now().Format("2006-01-02")

  //使用Parse 默认获取为UTC时区 需要获取本地时区 所以使用ParseInLocation
  t, _ := time.ParseInLocation("2006-01-02 15:04:05", timeStr+" 23:59:59", time.Local)
  zeroTime := t.Unix() + 1
  //时间戳 to 时间
  tm := time.Unix(zeroTime + 356400, 0)
  return tm
}

 

 

 

UAT环境压测 mongo副本集:

816G 虚拟机,(上面已搭若干实例),已使用10G,空闲内存6G左右前提下,压测mongo服务。

 

(插入字段和生产线上字段格式长度基本相同。)

1)单进程(携程)写入:单次批量写入,1万条/次,耗时221ms

2)单进程(携程)写入:单次批量写入,1千条/次,耗时38ms40ms左右);

3)单进程(携程)写入: 连续遍历1千次,每次insert_all 1k/次,插入总数量1百万条记录,总耗时19s

4)单进程(携程)写入: 连续遍历1万次,每次insert_all 1k/,  插入总量1千万条记录,总耗时3min12s3min14s),cpu 使用率 150%左右,mongo状态每秒钟insert操作5w多次;

5)单进程(携程)写入: 遍历两次,每次insert-all 1k/次,耗时77ms

6)并发写入:20个进程同时批量写入,每个进程总共insert_all 100万条记录,连续遍历1000次,每次写入1000条记录,mongo的写入次数每秒钟/20万次写操作,每个进程写完1min40

S20个进程写入基本为1min40s左右。

7)2千万的表中,单纯的做TTL过期,iostat查看awaitutil%util%达到30-40%左右,说明这个操作比较消耗IO性能.

8) 并发写入:20个进程,2千万条记录,每个进程写入100w条记录的IoStat状态

 

 

 9)最左边是Mongo每秒钟的写操作次数,连接数和等待队列正常,无明显增高变化;

 

 

 10)用固定集合capCollection指定集合(表)大小和条数,Insert每秒钟2w多条操作,iostat的util最高在30%多,io正常.

 
结合前几篇写的博客, 地址如下,https://www.cnblogs.com/unqiang/p/11987684.html

创建mongo删除的索引,根据业务,如果是白天忙时,晚上闲时,最好选择晚上去做TTL过期,这个不会在业务高峰的时候,批量插入和删除都去抢占IO操作,

而单纯的TTL过期,就会把io的util%打到30%,业务数据量如上,总计2000w数据,然后TTL索引过期.

要是再加上,2000w的数据,20个进程每个进程100w同时批量写,IO util%一下会飙升上去.

故建议根据业务选择删除mongo的数据,或者选择写脚本或程序手动控制或自动删除.

 

posted @ 2020-01-16 17:08  孤独信徒  阅读(2913)  评论(0编辑  收藏  举报