美文网首页
基于亿条数据的内存,redis两种聚合GO计算的实战思考

基于亿条数据的内存,redis两种聚合GO计算的实战思考

作者: hugoren | 来源:发表于2020-11-13 19:51 被阅读0次

    总结:

    (1) go 的 map读,写是线程不安全的
    (2) sync.map 用空间换时间的思想,适合读多写少的场景,读,写是线程安全,但遍历是线程不安全的,运行几个小时以后,出现性能巨降的情况。刚开始18w条/分,后来1w条/分;
    (3) orcaman/concurrent-map的思路是把大内存的map划分为若干小内存map
    (4)后面redis替换内存后,13w条/分,开了四个goroutine

    image.png

    每天亿条的数据量


    image.png

    背景

    数据经过filebeat ---->logstash--->kafka--->按一定维度聚合计算------>写入mysql---->grafana展示
    

    聚合计算的数据结构

    k, v的map内存存储
    计算的数据:
    最小,最大,平均,总数,成功,失败, 耗时........

    内存版本

    go 的 map 不加锁

    type dataStruct struct {
        cost int64
    }
    
    var mapData = make(map[string]dataStruct)
    
    func write(){
        fmt.Println("write")
        //if mapData["cost"].cost != 0 {
        //  fmt.Println("exit")
        //}
        mapData["cost"]=dataStruct{10}
    }
    func read(){
        fmt.Println("read")
        v := mapData["cost"]
        fmt.Println(v)
    }
    

    运行结果:死锁,说明map读,写线程是不安全的


    image.png

    go map 加锁

    type simpleLock struct {
        mu sync.Mutex
        mapData map[string]string
    
    }
    
    var l simpleLock
    
    func write(){
        fmt.Println("write")
        l.mapData = make(map[string]string)
        l.mu.Lock()
        l.mapData["cat"] = "hobb"
        l.mu.Unlock()
    }
    func read(){
        l.mapData = make(map[string]string)
        fmt.Println("read")
        l.mu.Lock()
        v := l.mapData["cost"]
        l.mu.Unlock()
        fmt.Println(v)
    
    

    或者这样的写法

    var counter = struct{
        sync.RWMutex
        m map[string]int
    }{m: make(map[string]int)}
    
    counter.RLock()
    n := counter.m["some_key"]
    counter.RUnlock()
    fmt.Println("some_key:", n)
    
    counter.Lock()
    counter.m["some_key"]++
    counter.Unlock()
    

    运行的结果


    image.png

    sync.map

    ar syncMap sync.Map
    
    func write(){
        fmt.Println("write")
        syncMap.Store("hugo", "boss")
    }
    func read(){
        v, _ := syncMap.Load("hugo")
        fmt.Println(v)
    }
    

    运行结果


    image.png

    以上总结

    上面的写法,适合,量少,读多写少的场景

    大量的读写,还得看redis, mq

    通过实践,用redis替代内存后,每分钟的数据由原来的2w条/分达到了 12w条/s。
    这性能还可以再优化,通过跟踪,时间有三分之二耗在了消费kafka数据,1条/3ms


    image.png
    image.png

    实践的结果

    积压的3亿条数据经过一天的消费,只剩下条4000w数据


    image.png

    参考

    深度解密 Go 语言之 sync.map

    Golang:一文解决Map并发问题
    https://cloud.tencent.com/developer/article/1539049
    go 分段锁ConcurrentMap,map+读写锁,sync.map的效率测试
    https://blog.csdn.net/yzf279533105/article/details/98636679

    相关文章

      网友评论

          本文标题:基于亿条数据的内存,redis两种聚合GO计算的实战思考

          本文链接:https://www.haomeiwen.com/subject/ankybktx.html