上一篇介紹 LiteDB 基本的 CRUD,但是我相信這還是有點不太夠的,隨著寫入的資料愈來愈多,做非主鍵條件查詢肯定是會愈來愈慢的,LiteDB 在沒有建 Index 的情況之下,如果查詢條件不是主鍵,它是對整個 DB 做 Full Document Scan,意謂著 LiteDB 必須將每一筆資料反序化出來之後一筆一筆去比對,不僅慢又浪費記憶體。
EnsureIndex()
在 LiteDB 建 Index 的方式非常簡單,選定欄位、呼叫 EnsureIndex()
就完成了,而且只要呼叫一次就有作用,即使重覆呼叫,已經建立的 Index 也不會重建。
using (var db = new LiteDatabase(ConnectionString))
{
var collection = db.GetCollection<Sample>();
collection.EnsureIndex(x => x.Name);
}
原本 15 萬筆的資料在沒有建 Index 的情況下,查詢 Name 欄位等於某個特定值的時間要 11 秒,建完 Index 只要 3 毫秒,有沒有 Index 真的差很多。
使用 Index 加入查詢條件
舉個例子來說,我們現在要找出最晚出生的人是誰?但是,程式碼千萬不要這樣寫:
using (var db = new LiteDatabase(ConnectionString))
{
var collection = db.GetCollection<Sample>();
var firstBorn = collection.FindAll().OrderByDescending(x => x.Birthday).First();
}
這樣寫意謂著把資料都序列化後才對 Birthday 降冪排序取第一筆,慢又消耗記憶體。
我們改用 Query Method 利用已經建好的 Index 來做查詢:
using (var db = new LiteDatabase(ConnectionString))
{
var collection = db.GetCollection<Sample>();
var firstBorn = collection.Find(Query.All(nameof(Sample.Birthday), Query.OrderByDescending), limit: 1).Single();
}
消耗的資源立馬就降下來了
再加個條件,要找出 2000 年以前最晚出生的是誰?經過剛剛的例子我們知道不應該這樣寫:
using (var db = new LiteDatabase(ConnectionString))
{
var collection = db.GetCollection<Sample>();
var firstBorn = collection.Find(x => x.Birthday < new DateTime(2000, 1, 1))
.OrderByDescending(x => x.Birthday)
.First();
}
應該改這樣:
using (var db = new LiteDatabase(ConnectionString))
{
var collection = db.GetCollection<Sample>();
var firstBorn = collection.Find(
Query.Where(nameof(Sample.Birthday), value => value.AsDateTime < new DateTime(2000, 1, 1), Query.Descending),
limit: 1)
.Single();
}
限制
LiteDB 的 Index 有兩個限制:
- 拿來建立 Index 的欄位值必須小於 512 bytes。
- 每個 Collection 最多只能建 16 個 Index(包含 PrimaryKey)。