AppBoxFuture: 二級索引及索引掃描查詢資料

白菜園發表於2019-07-24

  資料庫索引對於資料查詢的重要性不可言喻,因此作者在儲存層實現了二級索引,以及利用索引進行掃描的功能。目前僅實現了分割槽表與非分割槽表的本地索引(資料與索引共用一個Raft組管理),全域性索引及反向索引待以後再實現。

一、儲存結構:

  在介紹索引前先了解一下資料與索引是以何種結構儲存於RocksDB內的,每個節點的RocksDB例項都包含以下兩個ColumnFamily,每個列簇的儲存結構如下:

1. TableCF: 儲存實體資料

1.1 Key儲存編碼:

TableId(with OrderFlag) EntityId
32bit 128bit
  • OrderFlag用於EntityId按升序還是降序排列
  • EntityId包含實體建立時間戳,全域性惟一

1.2 Value儲存編碼:

Versions GC Flag [VersionTS + DataPtr] [Data [FieldId + Value]]
15bit 1 bit Versions * (64+32bit) Versions * nbit
  • Versions表示該條記錄有多少個Mvcc版本
  • GC Flag表示第一個版本前是否被清理掉了
  • [VersionTS + DataPtr]其中VersionTS是混合邏輯時間戳,DataPtr指向此版本的資料位置,另外DataPtr=0xFFFFFFFF表示記錄刪除標記
  • 資料部分每100個(暫定)儲存一個FullVersion的記錄資料,後跟差異部分

1.3 dbscan工具輸出示例:

AppBoxFuture: 二級索引及索引掃描查詢資料

2. IndexCF: 儲存索引資料

2.1 Key儲存編碼:

TableId IndexId IndexKey Values None unique index's EntityId
32bit 8bit [FieldId + Value] 128bit
  • IndexKey Values中的FieldId有一位是排序標誌位
  • None unique index's EntityID表示非惟一索引指向的目標,惟一索引存在於Value內

2.2 Value儲存編碼:

  與TableCF的Value編碼相同。

2.3 dbscan工具輸出示例:

AppBoxFuture: 二級索引及索引掃描查詢資料

二、索引管理:

  在新建實體模型及修改實體模型時均可新增與刪除索引(如下圖所示),需要注意的是修改模型時添刪索引會啟用非同步任務變更表結構並重建索引資料(請參考之前的文章:非同步結構變更),如果是重建惟一索引可能失敗,在實體模型設計器內可檢視索引重建狀態。在索引重建過程中或重建失敗後利用索引掃描資料會直接報錯,告知索引尚未準備好。
AppBoxFuture: 二級索引及索引掃描查詢資料

三、索引掃描:

  不同於傳統Sql資料庫解析Sql後利用索引掃描,服務模型的程式碼必須明確指定用哪個索引來查詢資料,具體參考以下示例程式碼如何利用索引掃描資料:

public async Task<object> IndexScan()
{
    //新建索引掃描,範型引數1為實體型別,引數2為索引型別
    var q = new IndexScan<Entities.VehicleState, Entities.VehicleState.IX_VID_Speed>();
    //如果是分割槽表可通過分割槽謂詞指定分割槽掃描,非分割槽表無效
    q.Partitions.Equal(t => t.VID, 3);
    //可指定索引謂詞確定掃描範圍
    q.Keys.Equal(t => t.VID, 3);
    //如果是複合索引可指定其他索引謂詞
    q.Keys.Equal(t => t.Speed, 100);
    return await q.Take(10).ToListAsync();
}
  • 索引謂詞目前僅實現了相等性判斷,其他如大於、小於等稍後實現
  • 索引掃描的附加過濾條件尚未實現

  另如果需要插入一批測試資料可參考以下示例程式碼:

public async Task<object> FillData()
{
    //第一個引數128表示並行任務數,第二個參數列示每個任務執行次數
    //作者虛擬機器(I74C8G)執行以下程式碼約每秒插入13000條記錄
    return await SimplePerfTest.Run(128, 500, async (i, j) =>
    {
        var obj = new Entities.VehicleState(i);
        obj.Speed = j;
        await EntityStore.SaveAsync(obj);
    });
}

四、本篇小結:

  本篇介紹資料及索引的儲存結構以及利用索引掃描api來查詢資料,下一步作者將實現其他謂詞條件,另外實現聚合掃描並優化單分割槽事務遞交。GitHub上的執行時已更新(包括dbscan工具)可供測試。如果您有問題或Bug報告,請留言或提交Issue,另外您的關注與點贊將是作者最大的動力。

相關文章