Redisソース分析(31)---latency遅延分析処理


遅延統計に言及するたびに、「パフォーマンステスト」という名詞が考えられます.間違いなく、Redisのredis_benchmarkファイルでは、遅延ファイルに関する情報が使用されています.Redisの公式解釈では、このファイルは次のとおりです.
/* The latency monitor allows to easily observe the sources of latency
 * in a Redis instance using the LATENCY command. Different latency
 * sources are monitored, like disk I/O, execution of commands, fork
 * system call, and so forth.
 *
 *         Redis            ,  I/O    ,      ,
 * fork           。
 * ----------------------------------------------------------------------------

Redisでの遅延動作では、プロセス全体の原理は非常に簡単であり、各イベントに対して統計リストを維持し、各リストには収集された一連のサンプルが含まれており、各サンプルには、このサンプルの作成時間とこのサンプルの遅延時間が含まれている.event==』はSampleSeriesListに対して辞書のマッピング関係である.次に、latencySample採集点という重要な採集点の構造定義を見てみましょう.
/* Representation of a latency sample: the sampling time and the latency
 * observed in milliseconds. */
/*        */
struct latencySample {
	//  Sample     
    int32_t time; /* We don't use time_t to force 4 bytes usage everywhere. */
    //       ,      
    uint32_t latency; /* Latency in milliseconds. */
};
辞書で維持されているのはSampleノードではなく、ノードリスト構造体です.
/* The latency time series for a given event. */
/*               sample */
struct latencyTimeSeries {
	//     Sample   
    int idx; /* Index of the next sample to store. */
    //     
    uint32_t max; /* Max latency observed for this event. */
    //       
    struct latencySample samples[LATENCY_TS_LEN]; /* Latest history. */
};
Redisコードの設計では、遅延はテストおよび結果分析のために使用されるので、著者らは、後で分析レポートで使用されるデータ統計構造体も設計した.
/* Latency statistics structure. */
/*   sample           */
struct latencyStats {
	//         
    uint32_t all_time_high; /* Absolute max observed since latest reset. */
    //  Sample    
    uint32_t avg;           /* Average of current samples. */
    //Sample       
    uint32_t min;           /* Min of current samples. */
    //Sample       
    uint32_t max;           /* Max of current samples. */
    //      ,       
    uint32_t mad;           /* Mean absolute deviation. */
    //samples   
    uint32_t samples;       /* Number of non-zero samples. */
    //             
    time_t period;          /* Number of seconds since first event and now. */
};
は非常に直接的な意味ですが、簡単なSampleはどのようにしてイベントの検出を行いますか?
/* Start monitoring an event. We just set the current time. */
/*          ,            */
#define latencyStartMonitor(var) if (server.latency_monitor_threshold) { \
    var = mstime(); \
} else { \
    var = 0; \
}

/* End monitoring an event, compute the difference with the current time
 * to check the amount of time elapsed. */
/*     ,         */
#define latencyEndMonitor(var) if (server.latency_monitor_threshold) { \
    var = mstime() - var; \
}

簡単です.記録開始時間、記録終了時間、中間の差は遅延時間です.指定した時間範囲を超えた場合、遅延リストに追加されます.
/* Add the sample only if the elapsed time is >= to the configured threshold. */
/*         server.latency_monitor_threshold,  Sample        */
#define latencyAddSampleIfNeeded(event,var) \
    if (server.latency_monitor_threshold && \
        (var) >= server.latency_monitor_threshold) \
          latencyAddSample((event),(var));
注目してみましょう.latencyAddSampleは、サンプリングノードを記録に追加します.手順は次のとおりです.
1.受信eventイベントに基づいてserver.latency_eventsはkeyがeventイベントであるval、すなわちlatencyTimeSeriesを見つけた.
2.このlatencyTimeSeriesのstruct latencySamples samples[LATENCY_TS_LEN]に新しいSampleを追加
実装コードは次のとおりです.
/* Add the specified sample to the specified time series "event".
 * This function is usually called via latencyAddSampleIfNeeded(), that
 * is a macro that only adds the sample if the latency is higher than
 * server.latency_monitor_threshold. */
/*   Sample    Event   Sample    */
void latencyAddSample(char *event, mstime_t latency) {
	//  Event     Sample     
    struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);
    time_t now = time(NULL);
    int prev;

    /* Create the time series if it does not exist. */
    if (ts == NULL) {
        ts = zmalloc(sizeof(*ts));
        ts->idx = 0;
        ts->max = 0;
        memset(ts->samples,0,sizeof(ts->samples));
        //  ts  ,    ,  Event,    latencyTimeSeries
        dictAdd(server.latency_events,zstrdup(event),ts);
    }

    /* If the previous sample is in the same second, we update our old sample
     * if this latency is > of the old one, or just return. */
    prev = (ts->idx + LATENCY_TS_LEN - 1) % LATENCY_TS_LEN;
    if (ts->samples[prev].time == now) {
        if (latency > ts->samples[prev].latency)
            ts->samples[prev].latency = latency;
        return;
    }

	// Sample  
    ts->samples[ts->idx].time = time(NULL);
    ts->samples[ts->idx].latency = latency;
    if (latency > ts->max) ts->max = latency;

    ts->idx++;
    if (ts->idx == LATENCY_TS_LEN) ts->idx = 0;
}
ノードが出てきたら、もちろん構造の分析統計を行います.この時、latencyStats構造体を使いました.
/* Analyze the samples avaialble for a given event and return a structure
 * populate with different metrics, average, MAD, min, max, and so forth.
 * Check latency.h definition of struct latenctStat for more info.
 * If the specified event has no elements the structure is populate with
 * zero values. */
/*       Event     ,      latencyStats     */
void analyzeLatencyForEvent(char *event, struct latencyStats *ls) {
    struct latencyTimeSeries *ts = dictFetchValue(server.latency_events,event);
    int j;
    uint64_t sum;
	
	//               
    ls->all_time_high = ts ? ts->max : 0;
    ls->avg = 0;
    ls->min = 0;
    ls->max = 0;
    ls->mad = 0;
    ls->samples = 0;
    ls->period = 0;
    if (!ts) return;

    /* First pass, populate everything but the MAD. */
    sum = 0;
    for (j = 0; j < LATENCY_TS_LEN; j++) {
        if (ts->samples[j].time == 0) continue;
        ls->samples++;
        if (ls->samples == 1) {
            ls->min = ls->max = ts->samples[j].latency;
        } else {
        	//              
            if (ls->min > ts->samples[j].latency)
                ls->min = ts->samples[j].latency;
            if (ls->max < ts->samples[j].latency)
                ls->max = ts->samples[j].latency;
        }
        sum += ts->samples[j].latency;

        /* Track the oldest event time in ls->period. */
        if (ls->period == 0 || ts->samples[j].time < ls->period)
        	//             
            ls->period = ts->samples[j].time;
    }

    /* So far avg is actually the sum of the latencies, and period is
     * the oldest event time. We need to make the first an average and
     * the second a range of seconds. */
    if (ls->samples) {
        ls->avg = sum / ls->samples;
        ls->period = time(NULL) - ls->period;
        if (ls->period == 0) ls->period = 1;
    }

    /* Second pass, compute MAD. */
    //        ,       
    sum = 0;
    for (j = 0; j < LATENCY_TS_LEN; j++) {
        int64_t delta;

        if (ts->samples[j].time == 0) continue;
        delta = (int64_t)ls->avg - ts->samples[j].latency;
        if (delta < 0) delta = -delta;
        sum += delta;
    }
    if (ls->samples) ls->mad = sum / ls->samples;
}
もちろん、これらの採集点を利用して、マイクロ線図を描いて、よりイメージ的に展示することもできます.
#define LATENCY_GRAPH_COLS 80
/*      Sample ,         */
sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts) {
    int j;
    struct sequence *seq = createSparklineSequence();
    sds graph = sdsempty();
    uint32_t min = 0, max = 0;

    for (j = 0; j < LATENCY_TS_LEN; j++) {
        int i = (ts->idx + j) % LATENCY_TS_LEN;
        int elapsed;
        char *label;
        char buf[64];

        if (ts->samples[i].time == 0) continue;
        /* Update min and max. */
        if (seq->length == 0) {
            min = max = ts->samples[i].latency;
        } else {
            if (ts->samples[i].latency > max) max = ts->samples[i].latency;
            if (ts->samples[i].latency < min) min = ts->samples[i].latency;
        }
        /* Use as label the number of seconds / minutes / hours / days
         * ago the event happened. */
        elapsed = time(NULL) - ts->samples[i].time;
        if (elapsed < 60)
            snprintf(buf,sizeof(buf),"%ds",elapsed);
        else if (elapsed < 3600)
            snprintf(buf,sizeof(buf),"%dm",elapsed/60);
        else if (elapsed < 3600*24)
            snprintf(buf,sizeof(buf),"%dh",elapsed/3600);
        else
            snprintf(buf,sizeof(buf),"%dd",elapsed/(3600*24));
        label = zstrdup(buf);
        sparklineSequenceAddSample(seq,ts->samples[i].latency,label);
    }

    graph = sdscatprintf(graph,
        "%s - high %lu ms, low %lu ms (all time high %lu ms)
", event, (unsigned long) max, (unsigned long) min, (unsigned long) ts->max); for (j = 0; j < LATENCY_GRAPH_COLS; j++) graph = sdscatlen(graph,"-",1); graph = sdscatlen(graph,"
",1); // sparkline graph = sparklineRender(graph,seq,LATENCY_GRAPH_COLS,4,SPARKLINE_FILL); freeSparklineSequence(seq); // return graph; }
は、Redisにおいて、外部呼び出しのためのいくつかのコマンドもカプセル化されている.ここでは、上記の方法の複合呼び出しは分析されない.
/* ---------------------------- Latency API --------------------------------- */
void latencyMonitorInit(void) /*          ,  Event     */
void latencyAddSample(char *event, mstime_t latency) /*   Sample    Event   Sample    */
int latencyResetEvent(char *event_to_reset) /*   Event     ,      event    */
void analyzeLatencyForEvent(char *event, struct latencyStats *ls) /*       Event     ,      latencyStats     */
sds createLatencyReport(void) /*     Sample   ,              */
void latencyCommandReplyWithSamples(redisClient *c, struct latencyTimeSeries *ts)
void latencyCommandReplyWithLatestEvents(redisClient *c)
sds latencyCommandGenSparkeline(char *event, struct latencyTimeSeries *ts)
void latencyCommand(redisClient *c)
Redisの遅延ファイルの分析も終わり、こんなに長い間RedisのRedisコードを分析して、すべてのコードに彼のハイライトがあると感じて、30期以上分析して、やはり多くのネット上で学べない知識を学んで、ネット上でもっと多いのはRedisの主流の思想の学習で、いくつかの比較的細かい点のようで、自分で味わうだけで、自分でやっと本当の体得することができます.