主頁 > 資料庫 > 【萬字長文】使用 LSM-Tree 思想基于.Net 6.0 C# 實作 KV 資料庫(案例版)

【萬字長文】使用 LSM-Tree 思想基于.Net 6.0 C# 實作 KV 資料庫(案例版)

2022-07-26 11:01:44 資料庫

文章有點長,耐心看完應該可以懂實際原理到底是啥子,

這是一個KV資料庫的C#實作,目前用.NET 6.0實作的,目前算是屬于雛形,骨架都已經完備,畢竟剛完工不到一星期,

當然,這個其實也算是NoSQL的雛形,有助于深入了解相關資料庫的內部原理概念,也有助于實際入門,

適合對資料庫原理以及實作感興趣的朋友們,

整體代碼,大概1500行,核心代碼大概500行,

為啥要實作一個資料庫

大概2018年的時候,就萌生了想自己研發一個資料庫的想法了,雖然,造輪子可能不如現有各種產品的強大,但是,能造者寥寥無幾,而且,造資料庫的書更是少的可憐,當然,不僅僅是造資料庫的書少,而是各種各樣高級的產品的創造級的書都少,

雖然,現在有各種各樣的開源,但是,像我這種底子薄的,就不能輕易的了解,這些框架的架構設計,以及相關的理念,純看代碼,沒個長時間,也不容易了解其存在的含義,

恰逢其時,前一個月看到【癡者工良】大佬的一篇《【萬字長文】使用 LSM Tree 思想實作一個 KV 資料庫 》文章給我很大觸動,讓我停滯的心,又砰砰跳了起來,雖然大佬是用GO語言實作的 ,但是,對我來講,語言還是個問題么,只要技術思想一致,我完全可以用C#實作啊,也算是對【癡者工良】大佬的致敬,我這邊緊隨其后,

當然,我自己對資料的研究也是耗時很久,畢竟,研究什么都要先從原理開始研究,從谷歌三個論文《GFS,MapReduce,BigTable》開始,但是,論文,畢竟是論文,讀不懂啊,又看了網上各種大佬的文章,還是很蒙蔽,實作的時候,也沒人交流,導致各種流產,

有時候,自己實作某個產品框架的時候,總是在想,為啥BUG都讓我處理一個遍哦,后來一想,你自己從新做個產品,也不能借鑒技術要點,那還不是從零開始,自然一一遇到BUG,

下圖就是,我在想做資料庫后,自己寫寫畫畫,但是,實際做的時候,邏輯表現總沒有那么好,當然,這個是關系型資料庫,難度比較高,下面可以看看之前的手稿,都是有想法了就畫一下,

實作難度有點高,現在這個實作是KV資料庫,算是列式資料庫了,大名鼎鼎的HBase,底層資料庫引擎就是LSM-Tree的技術思想,

LSM-Tree 是啥子

LSM-Tree 英文全稱是 Log Structured Merge Tree (中文:日志結構合并樹),是一種分層,有序,面向磁盤的資料結構,其核心思想是充分了利用了,磁盤批量的順序寫要遠比隨機寫性能高的技術特點,來實作高寫入吞吐量的存盤系統的核心,

具體的說,原理就是針對硬碟,盡量追加資料,而不是隨機寫資料,追加速度要比隨機寫的速度快,這種結構適合寫多讀少的場景,所以,LSM-Tree被設計來提供比傳統的B+樹或者ISAM更好的寫操作吞吐量,通過消去隨機的本地更新操作來達到這個性能目標,

相關技術產品有Hbase、Cassandra、Leveldb、RocksDB、MongoDB、TiDB、Dynamodb、Cassandra 、Bookkeeper、SQLite 等

所以,LSM-Tree的核心就是追加資料,而不是修改資料,

LSM-Tree 架構分析

其實這個圖已經表達了整體的設計思想了,主體其實就圍繞著紅色的線與黑色的線,兩部分展開的,其中紅色是寫,黑色是讀,箭頭表示資料的方向,數字表示邏輯順序,

整體包含大致三個部分,資料庫操作部分(主要為讀和寫),記憶體部分(快取表和不變快取表)以及硬碟部分(WAL Log 和 SSTable),這三個部分,

先對關鍵詞解釋一下

MemoryTable

記憶體表,一種臨時快取性質的資料表,可以用 二叉排序樹實作,也可以用字典來實作,我這邊是用字典實作的,

WAL Log

WAL 英文 (Write Ahead LOG) 是一種預寫日志,用于在系統故障期間提供資料的持久性,這意味著當寫入請求到來時,資料首先添加到 WAL 檔案(有時稱為日志)并重繪到更新記憶體資料結構之前的磁盤,

如果用過Mysql,應該就知道BinLog檔案,它們是一個道理,先寫入到WAL Log里,記錄起來,然后,寫入到記憶體表,如果電腦突然死機了,記憶體里的東西肯定丟失了,那么,下一次重啟,就從WAL Log 記錄表里,從新恢復資料到當前的資料狀態,

Immutable MemoryTable

Immutable(不變的),相對于記憶體表來講,它是不能寫入新資料,是只讀的,

SSTable

SSTable 英文 (Sorted Strings Table) ,有序字串表,就是有序的字串串列,使用它的好處是可以實作稀疏索引的效果,而且,合并檔案更為簡單方便,我要查某個Key,但是,它是基于 某個有序Key之間的,可以直接去檔案里查,而不用都保存到記憶體里,

這里我是用哈希表實作的,我認為浪費一點記憶體是值得的,畢竟為了快,浪費點空間是值得的,所以,目前是全索引加載到記憶體,而資料保存在SSTable里,當然,如果是為了更好的設計,也可以自己去實作有序表來用二分查找,

我這個方便實作了之后,記憶體會加載大量的索引,相對來講是快的,但是,記憶體會大一些,空間換時間的方案,

下面開始具體的流程分析

LSM-Tree Write 路線分析

看下圖,資料寫入分析

跟著紅色線走,關注我從此不迷路,

LSM-Tree Write 路線分析第一步

第一步,只有兩個部分需要注意的部分,分別是記憶體表和WAL.Log

寫入資料先存盤記憶體表,是為了快速的存盤到資料庫資料,

存盤到WAL.Log,是為了防止例外情況下資料丟失,

正常情況下,寫入到WAL.Log一份,然后,會寫入到記憶體一份,

當程式崩潰了,或者,電腦斷電例外了,重復服務后,就會先加載WAL.Log,按照從頭到尾的順序,恢復資料到記憶體表,直至結束,恢復到WAL.Log最后的狀態,也就是記憶體表資料最后的狀態,

這里要注意的是,當后面的不變表(Immutable MemoryTable)寫入到SSTable的時候,會清空WAL.Log檔案,并同時把記憶體表的資料直接寫入到WAL.log表中,

LSM-Tree Write 路線分析第二步

第二步,比較簡單,就是在記憶體表count大于一定數的時候,就新增一個記憶體表的同時, 把它變為 Immutable MemoryTable (不變表),等待SSTable的落盤操作,這個時候,Immutable MemoryTable會有多個表存在,

LSM-Tree Write 路線分析第三步

第三步,就是資料庫會定時檢查 Immutable MemoryTable (不變表)不變表是否存在,如果存在,就會直接落盤為SSTable表,不論當前記憶體里有多少 Immutable MemoryTable (不變表),

默認從記憶體落盤的第一級SSTable都是 Level 0,然后,內置了當前的時間,所以是兩級排序,先分級別,然后,分時間,

LSM-Tree Write 路線分析第四步

第四步,其實就是段合并或者級合并壓縮,就是判斷 level0 這一個級別的所有 SSTable檔案(SSTable0,SSTable1,SSTable2),判斷它們的總大小或者判斷它們的總個數來判斷,它們需不需要進行合并,

其中 Level 0 的大小如果是10M,那么 ,Level 1的大小就是 100M,依此類推,

當Level0的所有SSTable檔案超過了10M,或者限定的大小,就會從按照WAL.Log的順序思路,重新合并為一個大檔案,先老資料再新資料這樣遍歷合并,如果已經洗掉的,則直接剔除在外,只保留最新狀態,

如果 Level1的(全部SSTable)大小 超過100M,那么,觸發Level1的收縮動作,執行程序跟Level0一樣的操作,只是級別不同,

這樣壓縮的好處是使資料盡可能讓檔案量盡可能的少,畢竟,檔案多,管理就不是很方便,

至此,寫入路線已經分析完畢

查詢的時候,要先新資料,后舊資料,而分段合并壓縮的時候,要先老資料墊底,新資料刷狀態,這個是實作的時候需要注意的點,

LSM-Tree Read 路線分析

這就是資料的查找程序,跟著黑線和數字標記,很容易就看到了其訪問順序

  1. MemoryTable (記憶體表)
  2. Immutable MemoryTable (不變表)
  3. Level 0-N (SSTableN-SSTable1-SSTable0) (有序字串表)

基本上來說就這三部分,而級別表是從0級開始往下找的,而每級內部的SSTable是從新到舊開始找的,找到就回傳,不論key是洗掉還是正常的狀態,

LSM-Tree 架構分析與實作

核心思想:

其實就是一個時間有序的記錄表,會記錄每個操作,相當于是一個訊息佇列,記錄一系列的動作,然后,回放動作,就獲取到了最新的資料狀態,也類似CQRS中的Event Store(事件存盤),概念是相同的,那么實作的時候,就明白是一個什么本質,

Wal.log和SSTable,都是為了保證資料能落地持久化不丟失,而MemoryTable,偏向臨時快取的概念,當然,也有為了加速訪問的作用,

所以,從這幾個點來看,就分為了以下幾個大的物件

  1. Database 資料庫( 起到對Wal.log,SSTable和MemoryTable 的管理職責)
  2. Wal.log(記錄臨時資料日志)
  3. MemoryTable(記錄資料到記憶體,同時為資料庫查找功能提供介面服務)
  4. SSTable(管理SSTable檔案,并提供SSTable的查詢功能)

所以,針對這幾個物件來設計相關的類介面設計,

KeyValue (具體資料的結構)

設計的時候,要先設計實際資料的結構,我是這樣設計的

主要有三個主要的資訊,key, DataValue,Deleted ,其中DataValue是Object型別的,我這邊寫入到檔案里的話,是直接寫入的,

/// <summary>
/// 資料資訊 kv
/// </summary>
public class KeyValue
{
    public string Key { get; set; }
    public byte[] DataValue { get; set; }
    public bool Deleted { get; set; }
    private object Value;
    public KeyValue() { }
    public KeyValue(string key, object value, bool Deleted = false)
    {
        Key = key;
        Value = https://www.cnblogs.com/kesshei/p/value;
        DataValue = value.AsBytes();
        this.Deleted = Deleted;
    }
    public KeyValue(string key, byte[] dataValue, bool deleted)
    {
        Key = key;
        DataValue = dataValue;
        Deleted = deleted;
    }

    /// 
    /// 是否存在有效資料,非洗掉狀態
    /// 
    /// 
    public bool IsSuccess()
    {
        return !Deleted || DataValue != null;
    }
    /// 
    /// 值存不存在,無論洗掉還是不洗掉
    /// 
    /// 
    public bool IsExist()
    {
        if (DataValue != null && !Deleted || DataValue == null && Deleted)
        {
            return true;
        }
        return false;
    }
    public T Get() where T : class
    {
        if (Value == null)
        {
            Value = DataValue.AsObject();
        }
        return (T)Value;
    }

    public static KeyValue Null = new KeyValue() { DataValue = null };
}

IDataBase (資料庫介面)

主要對外互動用的主體類,資料庫類,增刪改查介面,都用 get,set,delete 表現,

/// <summary>
/// 資料庫介面
/// </summary>
public interface IDataBase : IDisposable
{
    /// <summary>
    /// 資料庫配置
    /// </summary>
    IDataBaseConfig DataBaseConfig { get; }
    /// <summary>
    /// 獲取資料
    /// </summary>
    KeyValue Get(string key);
    /// <summary>
    /// 保存資料(或者更新資料)
    /// </summary>
    bool Set(KeyValue keyValue);
    /// <summary>
    /// 保存資料(或者更新資料)
    /// </summary>
    bool Set(string key, object value);
    /// <summary>
    /// 獲取全部key
    /// </summary>
    List<string> GetKeys();
    /// <summary>
    /// 洗掉指定資料,并回傳存在的資料
    /// </summary>
    KeyValue DeleteAndGet(string key);
    /// <summary>
    /// 洗掉資料
    /// </summary>
    void Delete(string key);
    /// <summary>
    /// 定時檢查
    /// </summary>
    void Check(object state);
    /// <summary>
    /// 清除資料庫所有資料
    /// </summary>
    void Clear();
}

IDataBase.Check (定期檢查)

這個是定期檢查Immutable MemoryTable(不變表)的定時操作,主要依賴IDataBaseConfig.CheckInterval 引數配置其觸發間隔,

它的職責是檢查記憶體表和檢查SSTable 是否觸發分段合并壓縮的操作,

public void Check(object state)
{
    //Log.Info($"定時心跳檢查!");
    if (IsProcess)
    {
        return;
    }
    if (ClearState)
    {
        return;
    }
    try
    {
        Stopwatch stopwatch = Stopwatch.StartNew();
        IsProcess = true;
        checkMemory();
        TableManage.Check();
        stopwatch.Stop();
        GC.Collect();
        Log.Info($"定時心跳處理耗時:{stopwatch.ElapsedMilliseconds}毫秒");
    }
    finally
    {
        IsProcess = false;
    }
}

IDataBaseConfig (資料庫組態檔)

資料庫的組態檔,資料庫保存在哪里,以及生成SSTable時的閾值配置,還有檢測間隔時間配置,

/// <summary>
/// 資料庫相關配置
/// </summary>
public interface IDataBaseConfig
{
    /// <summary>
    /// 資料庫資料目錄
    /// </summary>
    public string DataDir { get; set; }
    /// <summary>
    /// 0 層的 所有 SsTable 檔案大小總和的最大值,單位 MB,超過此值,該層 SsTable 將會被壓縮到下一層
    /// 每層資料大小是上層的N倍
    /// </summary>
    public int Level0Size { get; set; }
    /// <summary>
    /// 層與層之間的倍數
    /// </summary>
    public int LevelMultiple { get; set; }
    /// <summary>
    /// 每層數量閾值
    /// </summary>
    public int LevelCount { get; set; }
    /// <summary>
    /// 記憶體表的 kv 最大數量,超出這個閾值,記憶體表將會被保存到 SsTable 中
    /// </summary>
    public int MemoryTableCount { get; set; }
    /// <summary>
    /// 壓縮記憶體、檔案的時間間隔,多久進行一次檢查作業
    /// </summary>
    public int CheckInterval { get; set; }
}

IMemoryTable (記憶體表)

這個表其實算是對記憶體資料的管理表了,主要是管理 MemoryTableValue 物件,這個物件是通過哈希字典來實作的,當然,你也可以選擇其他結構,比如有序二叉樹等,

/// <summary>
/// 記憶體表(排序樹,二叉樹)
/// </summary>
public interface IMemoryTable : IDisposable
{
    IDataBaseConfig DataBaseConfig { get; }
    /// <summary>
    /// 獲取總數
    /// </summary>
    int GetCount();
    /// <summary>
    /// 搜索(從新到舊,從大到小)
    /// </summary>
    KeyValue Search(string key);
    /// <summary>
    /// 設定新值
    /// </summary>
    void Set(KeyValue keyValue);
    /// <summary>
    /// 洗掉key
    /// </summary>
    void Delete(KeyValue keyValue);
    /// <summary>
    /// 獲取所有 key 資料串列
    /// </summary>
    /// <returns></returns>
    IList<string> GetKeys();
    /// <summary>
    /// 獲取所有資料
    /// </summary>
    /// <returns></returns>
    (List<KeyValue> keyValues, List<long> times) GetKeyValues(bool Immutable);
    /// <summary>
    /// 獲取不變表的數量
    /// </summary>
    /// <returns></returns>
    int GetImmutableTableCount();
    /// <summary>
    /// 開始交換
    /// </summary>
    void Swap(List<long> times);
    /// <summary>
    /// 清空全部資料
    /// </summary>
    void Clear();
}

MemoryTableValue (物件的實作)

主要是通過 Immutable 這個屬性實作了對不可變記憶體表的標記,具體實作是通過判斷 IDataBaseConfig.MemoryTableCount (記憶體表的 kv 最大數量)來實作標記的,

public class MemoryTableValue : IDisposable
{
    public long Time { get; set; } = IDHelper.MarkID();
    /// <summary>
    /// 是否是不可變
    /// </summary>
    public bool Immutable { get; set; } = false;
    /// <summary>
    /// 資料
    /// </summary>
    public Dictionary<string, KeyValue> Dic { get; set; } = new();

    public void Dispose()
    {
        Dic.Clear();
    }

    public override string ToString()
    {
        return $"Time {Time} Immutable:{Immutable}";
    }
}

什么時機表狀態轉換為 Immutable MemoryTable(不變表)的

我這里實作的是從Set的入口處實作的,如果數目大于IDataBaseConfig.MemoryTableCount (記憶體表的 kv 最大數量)就改變其狀態

public void Check()
{
    if (CurrentMemoryTable.Dic.Count() >= DataBaseConfig.MemoryTableCount)
    {
        var value = https://www.cnblogs.com/kesshei/p/new MemoryTableValue();
        dics.Add(value.Time, value);
        CurrentMemoryTable.Immutable = true;
    }
}

IWalLog

wallog,就簡單許多,就直接把KeyValue 寫入到檔案即可,為了保證WalLog的持續寫,所以,物件內部保留了此檔案的句柄,而SSTable,就沒有必要了,隨時讀,

/// <summary>
/// 日志
/// </summary>
public interface IWalLog : IDisposable
{
    /// <summary>
    /// 資料庫配置
    /// </summary>
    IDataBaseConfig DataBaseConfig { get; }
    /// <summary>
    /// 加載Wal日志到記憶體表
    /// </summary>
    /// <returns></returns>
    IMemoryTable LoadToMemory();
    /// <summary>
    /// 寫日志
    /// </summary>
    void Write(KeyValue data);
    /// <summary>
    /// 寫日志
    /// </summary>
    void Write(List<KeyValue> data);
    /// <summary>
    /// 重置日志檔案
    /// </summary>
    void Reset();
}

ITableManage (SSTable表的管理)

為了更好的管理SSTable,需要有一個管理層,這個介面就是它的管理層,其中SSTable會有多層,每次用 Level+時間戳+db 作為檔案名,用作外部識別,

/// <summary>
/// 表管理項
/// </summary>
public interface ITableManage : IDisposable
{
    IDataBaseConfig DataBaseConfig { get; }
    /// <summary>
    /// 搜索(從新到老,從大到小)
    /// </summary>
    KeyValue Search(string key);
    /// <summary>
    /// 獲取全部key
    /// </summary>
    List<string> GetKeys();
    /// <summary>
    /// 檢查資料庫檔案,如果檔案無效資料太多,就會觸發整合檔案
    /// </summary>
    void Check();
    /// <summary>
    /// 創建一個新Table
    /// </summary>
    void CreateNewTable(List<KeyValue> values, int Level = 0);
    /// <summary>
    /// 清理某個級別的資料
    /// </summary>
    /// <param name="Level"></param>
    public void Remove(int Level);
    /// <summary>
    /// 清除資料
    /// </summary>
    public void Clear();
}

ISSTable(SSTable 檔案)

SSTable的內容管理,應該就是LSM-Tree的核心了,資料的合并,以及資料的查詢,寫入,加載,都是偏底層的操作,需要一丟丟的資料庫知識,

/// <summary>
/// 檔案資訊表 (存盤在IO中)
/// 元資料 | 索引串列 | 資料區(資料修改只會新增,并修改索引串列資料) 
/// </summary>
public interface ISSTable : IDisposable
{
    /// <summary>
    /// 資料地址
    /// </summary>
    public string TableFilePath();
    /// <summary>
    /// 重寫檔案
    /// </summary>
    public void Write(List<KeyValue> values, int Level = 0);
    /// <summary>
    /// 資料位置
    /// </summary>
    public Dictionary<string, DataPosition> DataPositions { get; }
    /// <summary>
    /// 獲取總數
    /// </summary>
    /// <returns></returns>
    public int Count { get; }
    /// <summary>
    /// 元資料
    /// </summary>
    public ITableMetaInfo FileTableMetaInfo { get; }
    /// <summary>
    /// 查詢資料
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public KeyValue Search(string key);
    /// <summary>
    /// 有序的key串列
    /// </summary>
    /// <returns></returns>
    public List<string> SortIndexs();
    /// <summary>
    /// 獲取位置
    /// </summary>
    DataPosition GetDataPosition(string key);
    /// <summary>
    /// 讀取某個位置的值
    /// </summary>
    public object ReadValue(DataPosition position);
    /// <summary>
    /// 加載所有資料
    /// </summary>
    /// <returns></returns>
    public List<KeyValue> ReadAll(bool incloudDeleted = true);
    /// <summary>
    /// 獲取所有keys
    /// </summary>
    /// <returns></returns>
    public List<string> GetKeys();
    /// <summary>
    /// 獲取表名
    /// </summary>
    /// <returns></returns>
    public long FileTableName();
    /// <summary>
    /// 檔案的大小
    /// </summary>
    /// <returns></returns>
    public long FileBytes { get; }
    /// <summary>
    /// 獲取級別
    /// </summary>
    public int GetLevel();
}

IDataPosition(資料稀疏索引算是)

方便資料查詢方便和方便從SSTable里讀取到實際的資料內容,

/// <summary>
/// 資料的位置
/// </summary>
public interface IDataPosition
{
    /// <summary>
    /// 索引起始位置
    /// </summary>
    public long IndexStart { get; set; }
    /// <summary>
    /// 開始地址
    /// </summary>
    public long Start { get; set; }
    /// <summary>
    /// 資料長度
    /// </summary>
    public long Length { get; set; }
    /// <summary>
    /// key的長度
    /// </summary>
    public long KeyLength { get; set; }
    /// <summary>
    /// 是否已經洗掉
    /// </summary>
    public bool Deleted { get; set; }
    public byte[] GetBytes();
}

資料結構分析

內部表的結構就不用說了,很簡單,就是一個哈希字典,而有兩個結構是要具體分析的,那就是 WALLog和SSTable檔案,

WALLog 結構分析

這個圖橫向不好畫,我畫成豎向了,WalLog里面存盤的就是時間序的KeyValue資料,當它加載到Memory Table的時候,其實就是按照我所標的數字順序依次疊加到最后的狀態的,

同理,SSTable 資料分段合并壓縮的時候,其實是跟這個一個原理的,

SSTable 結構分析

SSTable,它本身是一個檔案 名字大致如下:

0_16586442986880000.db

格式為 層級_時間戳.db 這樣的方式搞的命名規則,為此我還搞了一個生成時間序不重復 ID的簡單演算法,

SSTable 資料區

資料區就很簡單,把KeyValue.DataValue直接ToJson 就可以了,然后,直接寫檔案,

SSTable 稀疏索引區

這個區是按照與資料區對應的key的順序寫入的,主要是把DataValue對應的開始地址和結束地址放入到這個資料區了,另外把key也寫入進去了,

好處是為了,當此SSTable加載索引(IDataPosition)到記憶體,省的把資料區的內容也加載進去,查找就方便許多,這也是索引的作用,

元資料區

這個按照協議來講,屬于協議頭,但是為啥放最后面呢,其實是為了計算方便,這也算是一個小妙招,

其中不僅包含了資料區的開始和結束,稀疏索引區的開始和結束,還包含了,此SSTable的版本和創建時間,以及當前SSTable所在的級別,

SSTable 分段合并壓縮

剛看這段功能邏輯的時候,腦子是懵的,使勁看了好久,分析了好久,還是把它寫出來了,剛開始不理解,后來理解了,寫著就容易許多了,

看下圖:

其實合并是有狀態的,這個就是中間態,我把他放到了圖中間,然后,用白色的虛框表示,

整體邏輯就是,先從記憶體中定時把不變表生成為0級的SSTable,然后,0級就會有許多檔案,如果這些檔案大小超過了閾值,就合并此級的檔案為一個大檔案,按照WalLog的合并原理,然后把資訊重新寫入到本地為1級SSTable即可,

以此類推,

下面一個動圖說明其合并效果,

這個動圖也說明一些事情,有此圖,估計對原理就會多懂一些,

LSMDatabase 性能測驗

目前我這邊測驗用例都挺簡單,如果有bug,就直接改了,
我這邊測驗是,直接寫入一百萬條資料,測驗結果如下:

keyvalue 資料長度:151 實際檔案大小:217 MB 插入1000000條資料 耗時:79320毫秒 或79.3207623秒,平均每秒插入:52631條

keyvalue 資料長度:151 實際檔案大小:221 MB 插入1000000條資料 耗時:27561毫秒 或 27.5616519 秒,平均每秒插入:37037條

  1. keyvalue 資料長度:176
  1. 實際檔案大小:215 MB
  2. 插入1000000條資料 耗時:29545毫秒 或 29.5457999 秒,
  3. 平均每秒插入:34482條 或 30373 等( 配置不一樣,環境不一樣,會有不同,但是大致差不多)
  4. 多次插入資料長度不同,配置不同,插入速度都會受到影響

加載215 MB 1000000條資料條資料 耗時:2322 毫秒,也就是2秒(加載SSTable)

記憶體穩定后占用500MB左右,

穩定查詢耗時: 百條查詢平均每條查詢耗時: 0毫秒,可能是因為用了字典的緣故,查詢速度會快點,但是,特別點查詢會有0.300左右的耗時個別現象,

查詢keys,一百萬條耗時3秒,這個有點耗時,應該是資料量太大了,

至此,此專案已經結束,雖然,還沒有經歷過壓力測驗,但是,整體骨架和內容已經完備,可以根據具體情況修復完善,目前我這邊是沒啥子問題的,

總結

任何事情的開始都是艱難的,跨越時間的長河,一步一步的學習,才有了今天它的誕生,會了就是會了,那么,應對下一個相關問題就會容易許多,我對這樣的壁壘稱之為,知識的屏障,

一葉障目,還真是存在,如何突破,唯有好奇心,堅持下去,一點點挖掘,

參考資料

【萬字長文】使用 LSM Tree 思想實作一個 KV 資料庫

https://www.cnblogs.com/whuanle/p/16297025.html

肖漢松:《從0開始:500行代碼實作 LSM 資料庫》

https://mp.weixin.qq.com/s/kCpV0evSuISET7wGyB9Efg

cstack : 讓我們建立一個簡單的資料庫

https://cstack.github.io/db_tutorial/

資料庫內核雜談 - 一小時實作一個基本功能的資料庫

https://www.jianshu.com/p/76e5cb53c864

谷歌三大論文 GFS,MapReduce,BigTable 中的GFS和BigTable

致謝名單

  1. 癡者工良
  2. 陶德

雖然與以上大佬沒有太過深入的交流,畢竟咖位還是有點高的,但是,通過文章以及簡單的交流中,讓我對資料庫的研究更深一步,甚至真實的搞出來了,再次感謝,

代碼地址

https://github.com/kesshei/LSMDatabaseDemo.git

https://gitee.com/kesshei/LSMDatabaseDemo.git

一鍵三連呦!,感謝大佬的支持,您的支持就是我的動力!

著作權

藍創精英團隊(公眾號同名,CSDN同名)

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/500327.html

標籤:其它

上一篇:MySQL實戰45講 17

下一篇:JetBrains DataGrip 2022 Mac(多引擎資料庫管理工具)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more