Redis源碼分析(三十一)--- latency延遲分析處理

來源:互聯網
上載者:User

標籤:記憶體資料庫   nosql資料庫   效能測試   測試   

         每當提到延時統計的時候,一定想到的一個名詞就是”效能測試“,沒錯,在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 latencySample 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)\n", 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,"\n",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主流思想的學習,像一些比較細小點,也只有自己品味,自己才能夠真正的體會。

Redis源碼分析(三十一)--- latency延遲分析處理

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.