Influxdb 数据写入流程

数据写入流程分析

  1. 本篇不涉及存储层的写入,只分析写入请求的处理流程
Influxdb名词介绍
  1. 如果想搞清楚Influxdb数据写入流程,Influxdb本身的用法和其一些主要的专用词还是要明白是什么意思,比如measurement, field key,field value, tag key, tag value, tag set, line protocol, point, series, query, retention policy等;
  2. 相关的专用名词解释可参考: InfluxDB glossary of terms
分析入口
  1. 我们还是以http写请求为入口来分析,在httpd/handler.go中创建Handler时有如下代码:
    Route{
            "write", // Data-ingest route.
            "POST", "/write", true, writeLogEnabled, h.serveWrite,
        }

因此对写入请求的处理就在函数 func (h *Handler) serveWrite(w http.ResponseWriter, r *http.Request, user meta.User)中。

  1. Handler.serveWrite流程梳理:
    2.1 获取写入的db并判断db是否存在
database := r.URL.Query().Get("db")
    if database == "" {
        h.httpError(w, "database is required", http.StatusBadRequest)
        return
    }

    if di := h.MetaClient.Database(database); di == nil {
        h.httpError(w, fmt.Sprintf("database not found: %q", database), http.StatusNotFound)
        return
    }

2.2 权限验证

if h.Config.AuthEnabled {
        if user == nil {
            h.httpError(w, fmt.Sprintf("user is required to write to database %q", database), http.StatusForbidden)
            return
        }

        if err := h.WriteAuthorizer.AuthorizeWrite(user.ID(), database); err != nil {
            h.httpError(w, fmt.Sprintf("%q user is not authorized to write to database %q", user.ID(), database), http.StatusForbidden)
            return
        }
    }

2.3 获取http请求的body部分,如需gzip解压缩则解压,并且作body size的校验,因为有body size大小限制

    body := r.Body
    if h.Config.MaxBodySize > 0 {
        body = truncateReader(body, int64(h.Config.MaxBodySize))
    }
    ...
    _, err := buf.ReadFrom(body)

2.4 从http body中解析出 points

points, parseError := models.ParsePointsWithPrecision(buf.Bytes(), time.Now().UTC(),
                       r.URL.Query().Get("precision"))

2.5 将解析出的points写入db

h.PointsWriter.WritePoints(database, r.URL.Query().Get("rp"), consistency, user, points); 
Points的解析
  1. 将http body解析成Points是写入前的最主要的一步, 相关内容定义在 models/points.go中;
  2. 我们先来看一下一条写入语句是什么样子的: insert test_mea_1,tag1=v1,tag2=v2 cpu=1,memory=10
    其中test_mea_1是measurement, tag key是tag1和tag2, 对应的tag value是v1和v2, field key是cpu和memory, field value是1和10;
  3. 先来看下point的定义,它实现了Point interface
type point struct {
    time time.Time

    //这个 key包括了measurement和tag set, 且tag set是排序好的   
    key []byte

    // text encoding of field data
    fields []byte

    // text encoding of timestamp
    ts []byte

    // cached version of parsed fields from data
    cachedFields map[string]interface{}

    // cached version of parsed name from key
    cachedName string

    // cached version of parsed tags
    cachedTags Tags

    //用来遍历所有的field
    it fieldIterator
}
  1. 解析出Points
func ParsePointsWithPrecision(buf []byte, defaultTime time.Time, precision string) ([]Point, error) {
    points := make([]Point, 0, bytes.Count(buf, []byte{'\n'})+1)
    var (
        pos    int
        block  []byte
        failed []string
    )
    for pos < len(buf) {
        pos, block = scanLine(buf, pos)
        pos++
  
        ...

        pt, err := parsePoint(block[start:], defaultTime, precision)
        if err != nil {
            failed = append(failed, fmt.Sprintf("unable to parse '%s': %v", string(block[start:]), err))
        } else {
            points = append(points, pt)
        }

    }

    return points, nil
}

这里的解析并没有用正则之类的方案,纯的字符串逐次扫描,这里不详细展开说了.

PointsWriter分析
  1. 定义在coordinator/points_writer.go
  2. 主要负责将数据写入到本地的存储,我们重点分析下WritePointsPrivileged
func (w *PointsWriter) WritePointsPrivileged(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error {
    ....
    
    //将point按time对应到相应的Shar上, 这个对应关系存储在shardMappings里, 这个MapShareds我们后面会分析
    shardMappings, err := w.MapShards(&WritePointsRequest{Database: database, RetentionPolicy: retentionPolicy, Points: points})
    if err != nil {
        return err
    }

    // Write each shard in it's own goroutine and return as soon as one fails.
    ch := make(chan error, len(shardMappings.Points))
    for shardID, points := range shardMappings.Points {
    
        // 每个 Shard启动一个goroutine作写入操作, 真正的写入操作w.writeToShard
        go func(shard *meta.ShardInfo, database, retentionPolicy string, points []models.Point) {
            err := w.writeToShard(shard, database, retentionPolicy, points)
            if err == tsdb.ErrShardDeletion {
                err = tsdb.PartialWriteError{Reason: fmt.Sprintf("shard %d is pending deletion", shard.ID), Dropped: len(points)}
            }
            ch <- err
        }(shardMappings.Shards[shardID], database, retentionPolicy, points)
    }
    ...
    
    // 写入超时会return ErrTimeout
    timeout := time.NewTimer(w.WriteTimeout)
    defer timeout.Stop()
    for range shardMappings.Points {
        select {
        case <-w.closing:
            return ErrWriteFailed
        case <-timeout.C:
            atomic.AddInt64(&w.stats.WriteTimeout, 1)
            // return timeout error to caller
            return ErrTimeout
        case err := <-ch:
            if err != nil {
                return err
            }
        }
    }
    return err
}
  1. Point到Shard的映谢
    3.1 先根据point的time找到对应的ShardGroup, 没有就创建新的ShardGroup;
    3.2 按Point的key(measurement + tag set取hash)来散
sgi.Shards[hash%uint64(len(sgi.Shards))]
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容