1. 程式人生 > >Redis原始碼分析(二十六)--- slowLog和hyperloglog

Redis原始碼分析(二十六)--- slowLog和hyperloglog

            今天學習的是是2個log的檔案,2個檔案的實現功能都超出我原本理解的意思。開始時我以為就是記錄不同的型別的日誌,後來才慢慢的明白了額,slowLog記錄的是超時的查詢記錄,而hyperloglog其實跟日誌一點關係都沒有,好吧,我再一次傻眼了,他其實是一種基數統計演算法,應該分開了看,hyper + loglog的計算。好,接下來,我們開始學習一下Redis程式碼中是如何實現的。

       slowLog的官方解釋:

/* Slowlog implements a system that is able to remember the latest N
 * queries that took more than M microseconds to execute.
 *
 * The execution time to reach to be logged in the slow log is set
 * using the 'slowlog-log-slower-than' config directive, that is also
 * readable and writable using the CONFIG SET/GET command.
 *
 * The slow queries log is actually not "logged" in the Redis log file
 * but is accessible thanks to the SLOWLOG command.
 *
 *大致意思就是SlowLog記錄的是系統最近N個超過一定時間的查詢,就是比較耗時的查詢
 * ----------------------------------------------------------------------------
裡面定義了一個slowLog entry的結構體:
/* This structure defines an entry inside the slow log list */
/* 慢日誌結構體,將會插入到slowLogList,慢日誌列表中 */
typedef struct slowlogEntry {
    robj **argv;
    int argc;
    //自身的id標識
    long long id;       /* Unique entry identifier. */
    //query操作所消耗的時間,單位為nanoseconds
    long long duration; /* Time spent by the query, in nanoseconds. */
    //查詢發發生的時間
    time_t time;        /* Unix time at which the query was executed. */
} slowlogEntry;

/* Exported API */
void slowlogInit(void); /* slowlog初始化操作 */
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration); /* slowLogEntry壓入列表操作 */

/* Exported commands */
/* 開放給系統使用的命令 */
void slowlogCommand(redisClient *c);
裡面定義的方法也非常簡單。初始化init方法和插入方法,在服務端的server端,維護了一個slowLog的列表,會按照時間順序插入超時的查詢記錄,也就是slowLogEntry記錄:
/* Initialize the slow log. This function should be called a single time
 * at server startup. */
/* slowLog的初始化操作 */
void slowlogInit(void) {
	//建立slowLog的List
    server.slowlog = listCreate();
    //第一個entry_id宣告為0
    server.slowlog_entry_id = 0;
    listSetFreeMethod(server.slowlog,slowlogFreeEntry);
}
插入列表的方法:
/* Push a new entry into the slow log.
 * This function will make sure to trim the slow log accordingly to the
 * configured max length. */
/* 插入一個entry到slowLog列表中,如果時間超出給定的時間範圍時 */
void slowlogPushEntryIfNeeded(robj **argv, int argc, long long duration) {
    if (server.slowlog_log_slower_than < 0) return; /* Slowlog disabled */
    if (duration >= server.slowlog_log_slower_than)
    	//如果entry的duration時間超出slowlog_log_slower_than時間,則新增
        listAddNodeHead(server.slowlog,slowlogCreateEntry(argv,argc,duration));

    /* Remove old entries if needed. */
    while (listLength(server.slowlog) > server.slowlog_max_len)
    	//如果列表長度已經超出slowLog的最大值,移除最後一個slowLogEntry
        listDelNode(server.slowlog,listLast(server.slowlog));
}
slowLog就是這樣,非常簡單明瞭,重點學習一下hyperloglog,作為一種基數統計演算法,比如統計一篇莎士比亞的文章中,不同單詞出現的個數,如果按照平常我們想到的做法,把裡面的單詞都存到hashset中,求出容量即可,但是當面對的是海量資料的時候,這得佔據多大的記憶體呢,所以就有了後來我們說的“點陣圖法“,點陣圖可以快速、準確地獲取一個給定輸入的基數。點陣圖的基本思想是使用雜湊函式把資料集對映到一個bit位,每個輸入元素與bit位是一一對應。這樣Hash將沒有產生碰撞衝突,並減少需要計算每個元素對映到1個bit的空間。雖然Bit-map大大節省了儲存空間,但當統計很高的基數或非常大的不同的資料集,它們仍然有問題。但是比較幸運的是,基數統計作為一個新興的領域,也已經有了許多開源演算法的實現,基數統計演算法的思想是用準確率換取空間,準確率可以稍稍差一點點,但是可以大大的縮減佔用的空間。下面在網上找了3個比較典型的基數統計演算法,這三種技術是:Java HashSet、Linear Probabilistic Counter以及一個Hyper LogLog Counter,我說其中的第二種和第三種。

       Linear Probabilistic Counter線性概率計數器是高效的使用空間,並且允許實現者指定所需的精度水平。該演算法在注重空間效率時是很有用的,但你需要能夠控制結果的誤差。該演算法分兩步執行:第一步,首先在記憶體中分配一個初始化為都為0的Bit-map,然後使用雜湊函式對輸入資料中的每個條目進行hash計算,雜湊函式運算的結果是將每條記錄(或者是元素)對映到Bit-map的一個Bit位上,該Bit位被置為1;第二步,演算法計算空的bit位數量,並使用這個數輸入到下面的公式來進行估算:
n=-m ln Vn
注意:ln Vn=Loge(Vn) 自然對數
在公式中,m是 Bit-map的大小,Vn是空bit位和map的大小的比率。需要重點注意的是原始Bit-map的大小,可以遠小於預期的最大基數。到底小多少取決於你可以承受誤差的大小。因為Bit-map的大小m小於不同元素的總數將會產生碰撞。雖然碰撞可以節省空間,但同時也造成了估算結果出現誤差。所以通過控制原始map的大小,我們可以估算碰撞的次數,以致我們將在最終結果中看到誤差有多大。

      hyperLogLog提供了比上面效率更高的演算法。顧名思義,Hyper LogLog計數器就是估算Nmax為基數的資料集僅需使用loglog(Nmax)+O(1) bits就可以。如線性計數器的Hyper LogLog計數器允許設計人員指定所需的精度值,在Hyper LogLog的情況下,這是通過定義所需的相對標準差和預期要計數的最大基數。大部分計數器通過一個輸入資料流M,並應用一個雜湊函式設定h(M)來工作。這將產生一個S = h(M) of {0,1}^∞字串的可觀測結果。通過分割雜湊輸入流成m個子字串,並對每個子輸入流保持m的值可觀測 ,這就是相當一個新Hyper LogLog(一個子m就是一個新的Hyper LogLog)。利用額外的觀測值的平均值,產生一個計數器,其精度隨著m的增長而提高,這隻需要對輸入集合中的每個元素執行幾步操作就可以完成。其結果是,這個計數器可以僅使用1.5 kb的空間計算精度為2%的十億個不同的資料元素。與執行 HashSet所需的120 兆位元組進行比較,這種演算法的效率很明顯。這就是傳說中的”如何僅用1.5KB記憶體為十億物件計數“。

相關推薦

Redis原始碼分析--- slowLoghyperloglog

            今天學習的是是2個log的檔案,2個檔案的實現功能都超出我原本理解的意思。開始時我以為就是記錄不同的型別的日誌,後來才慢慢的明白了額,slowLog記錄的是超時的查詢記錄,而hyperloglog其實跟日誌一點關係都沒有,好吧,我再一次傻眼了,他其實

Redis原始碼分析--- Redis中的11大優秀設計

            堅持了一個月左右的時間,從最開始的對Redis的程式碼做分類,從struct結構體分析開始,到最後分析main主程式結束,中間,各大模組的程式碼逐個擊破,學習,總之,收穫了非常多,好久沒有這麼久的耐心把一個框架學透,學習一個框架,會用那只是小小的一部

osgEarth的Rex引擎原理分析TileNode的_renderModel作用

目標:(十八)中的問題59 osgEarth::Drivers::RexTerrainEngine::TileRenderModel主要維護兩個變數:取樣器osgEarth::Drivers::RexTerrainEngine::Sampler向量_sharedSamplers和渲染通道osgE

Redis原始碼分析--- redis.c服務端的實現分析2

       在Redis服務端的程式碼量真的是比較大,如果一個一個API的學習怎麼實現,無疑是一種效率很低的做法,所以我今天對服務端的實現程式碼的學習,重在他的執行流程上,而對於他的模組設計在上一篇中我已經分析過了,不明白的同學可以接著看上篇。所以我學習分析redis服務

Android原始碼解析-->截圖事件流程

今天這篇文章我們主要講一下Android系統中的截圖事件處理流程。用過android系統手機的同學應該都知道,一般的android手機按下音量減少鍵和電源按鍵就會觸發截圖事件(國內定製機做個修改的這裡就不做考慮了)。那麼這裡的截圖事件是如何觸發的呢?觸發之後

Redis原始碼分析--- redis.h服務端的實現分析1

       上次剛剛分析過了客戶端的結構體分析,思路比較簡答,清晰,最後學習的是服務端的實現,服務端在Redis可是重中之重,裡面基本上囊括了之前模組中涉及到的所有知識點,從redis的標頭檔案就可以看出了,redis.h程式碼量就已經破1000+行了,而且都還只是一些變

叉樹的經典面試題分析

        我們之前學習了二叉樹相關的概念,那麼我們今天來分析下二叉樹中的一些經典面試題。         1、單度結點的刪除  

Redis 原始碼分析 一個 rehash 也不阻塞的雜湊表

Redis 的架構設計挺巧妙的,捨棄了主流的多執行緒架構,別出心裁的使用單執行緒架構,說實話,作為一個 kv,我一開始認為多執行緒並行的訪問應該是一個預設選項,但是 Redis 的高效,用事實證明,這顯然不是。這個單執行緒的事件系統另開一坑再聊吧,今天主要是看一

OpenCV探索之路:如何去除票據上的印章

com 票據 uid amp 去除 album 探索 ace 十六 http://pic.cnhubei.com/space.php?uid=1774&do=album&id=1338281http://pic.cnhubei.com/space.php?u

企業分布式微服務雲SpringCloud SpringBoot mybatis 集成apidoc

命令 準備工作 目錄 說明 在外 多文件 cimage ref 簡單 一、apidoc簡介 apidoc通過在你代碼的註釋來生成api文檔的。它對代碼沒有侵入性,只需要你寫好相關的註釋即可,並且它僅通過寫簡單的配置就可以生成高顏值的api接口頁面。它基於node.js,所以

Linux學習筆記grep

grepgrepgrep [-cinvABC] ‘word‘ filename -c 行數-i 不區分大小寫-n 顯示行號-v 取反-r 遍歷所有子目錄-A 後面跟數字,過濾出符合要求的行以及下面n行-B 同上,過濾出符合要求的行以及上面n行-C 同上,同時過濾出符合要求的行以及上下各n行 mkdir /tm

Linux學習總結防火墻規則之firewalld

firewalld iptables 一iptables 規則備份 service iptables save //會把規則保存到/etc/sysconfig/iptables把iptables規則備份到my.ipt文件中iptables-save > my.ipt恢復剛才備份的規則 iptab

python2.7練習小例子

mat list num 題目 stdout AC number python2.7 打印 26):題目:給一個不多於5位的正整數,要求:一、求它是幾位數,二、逆序打印出各位數字。 程序分析:學會分解出每一位數。 程序源代碼: #!/usr/b

初探 C++ 標準庫

C++ 標準庫 cout cin 操作符重載 今天我們來看下 C++ 中的標準庫,這幾天我們一直學習的是 C++ 中的一大難點,操作符重載。那麽我們想想操作符左移 << 可以重載嗎?操作符 << 的原生語義是按位左移,如:1 << 2,則

函數對象分析

C++ 函數對象 函數調用操作符重載 斐波拉契數列 通過前面對 C++ 的學習,客戶又有一個新的需求擺在我們的面前了。編寫一個函數:函數可以獲得斐波拉契數列每項的值;沒調用一次便返回一個值;函數可根據需要重復使用。 我們之前在 C 語言中也講過斐波拉契數列,相信這

Java學習總計——JavaScript正則表達式,Js表單驗證,原生js+css頁面時鐘

text 先來 helloword 郵箱 用戶名 就是 lac round 外部 一.JavaScript正則表達式1.exec檢索字符串中指定的值,返回找到的值,並確定其位置2.test檢索字符串中指定的值,返回true或false3.正則表達式對象的創建:(1)方式一:

成員變量與局部變量

show run 作用 bench 變量 被垃圾回收 [] 局部變量 sha /* *局部變量和成員變量的區別: * 1.局部變量沒有默認值,成員變量有默認值 * 2.局部變量再棧中開辟內存,成員變量再堆中開辟內存 * 3.局部變量作用範圍有限旨在定義的環境中,成員

MySQL數據庫8存儲過程

com 直接 形參 基本上 一次 cal inf 分享 技術分享 存儲過程 存儲過程的概念 存儲過程(stored procedure)是在大型數據庫系統中,一組為了完成特定功能的SQL語句集,存儲在數據庫中,經過第一次編譯後再次調用不需要再次編譯,(效率比較高)用戶通

菜鳥好文推薦——避免Java堆空間錯誤的5個步驟

牢記以下五個步驟可以為你減少很多頭痛的問題並且避免Java堆空間錯誤。 通過計算預期的記憶體消耗。 檢查JVM是否有足夠的可用空間。 檢查JVM的設定是否正確。 限制節點使用交換空間和記憶體分頁。 設定例項slot數量小於JobTracker web GU

MySql--分庫分表--基礎

  實在沒招的時候,才考慮分庫分表。 一個經驗值,mysql一張表的記錄不要超過500萬條。 https://www.cnblogs.com/sunny3096/p/8595058.html https://blog.csdn.net/hello12345