移動(dòng)端數(shù)據(jù)庫(kù)新王者:realm

realm

介紹

realm是一個(gè)跨平臺(tái)移動(dòng)數(shù)據(jù)庫(kù)引擎埠胖,支持iOS肛炮、OS X(Objective-C和Swift)以及Android举户。

2014年7月發(fā)布蕉世。由YCombinator孵化的創(chuàng)業(yè)團(tuán)隊(duì)歷時(shí)幾年打造,是第一個(gè)專門針對(duì)移動(dòng)平臺(tái)設(shè)計(jì)的數(shù)據(jù)庫(kù)狭姨。目標(biāo)是取代SQLite宰啦。

為了徹底解決性能問(wèn)題,核心數(shù)據(jù)引擎C++打造饼拍,并不是建立在SQLite之上的ORM赡模。如果對(duì)數(shù)據(jù)引擎實(shí)現(xiàn)想深入了解可以查看:Realm 核心數(shù)據(jù)庫(kù)引擎探秘。因此得到的收益就是比普通的ORM要快很多师抄,甚至比單獨(dú)無(wú)封裝的SQLite還要快漓柑。

因?yàn)槭荗RM,本身在設(shè)計(jì)時(shí)也針對(duì)移動(dòng)設(shè)備(iOS、Android)辆布,所以非常簡(jiǎn)單易用瞬矩,學(xué)習(xí)成本很低。

碾壓級(jí)性能

數(shù)據(jù)引自:introducing-realm

每秒能在20萬(wàn)條數(shù)據(jù)中進(jìn)行查詢后count的次數(shù)锋玲。realm每秒可以進(jìn)行30.9次查詢后count景用。

在20萬(wàn)條中進(jìn)行一次遍歷查詢,數(shù)據(jù)和前面的count相似:realm一秒可以遍歷20萬(wàn)條數(shù)據(jù)31次惭蹂,而coredata只能進(jìn)行兩次查詢伞插。

這是在一次事務(wù)每秒插入數(shù)據(jù)的對(duì)比,realm每秒可以插入9.4萬(wàn)條記錄盾碗,在這個(gè)比較里純SQLite的性能最好媚污,每秒可以插入17.8萬(wàn)條記錄。然而封裝了SQLite的FMDB的成績(jī)大概是realm的一半廷雅。

簡(jiǎn)單易用

實(shí)例代碼語(yǔ)言是Objective-C耗美。

Realm對(duì)象和其他對(duì)象沒(méi)有太大區(qū)別,只是需要繼承RLMObject

@interface Dog : RLMObject
@property NSString *name;
@property NSInteger age;
@end
Dog *mydog = [[Dog alloc] init];

存儲(chǔ)起來(lái)也非常簡(jiǎn)單榜轿,獲取數(shù)據(jù)庫(kù)實(shí)例幽歼,在一個(gè)事務(wù)中進(jìn)行寫入。

RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
    [realm addObject:mydog];
}];

方便的查詢谬盐,可以在一個(gè)查詢結(jié)果中再進(jìn)行查詢甸私。查詢的條件有著豐富的支持。

RLMResults *r = [Dog objectsWhere:@"age > 8"];
// Queries are chainable
r = [r objectsWhere:@"name contains 'Rex' AND  name BEGINSWITH '大'"];

zero-copy和懶加載

在通常的數(shù)據(jù)庫(kù)中飞傀,數(shù)據(jù)大多數(shù)時(shí)間都靜靜地呆在硬盤當(dāng)中皇型。當(dāng)你訪問(wèn) NSManagedObject 對(duì)象中的某個(gè)屬性的時(shí)候,Core Data 會(huì)將這個(gè)請(qǐng)求轉(zhuǎn)換為一組 SQL 語(yǔ)句砸烦,如果還未連接數(shù)據(jù)庫(kù)的話則創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)連接弃鸦,然后將這個(gè) SQL 語(yǔ)句發(fā)送給硬盤,執(zhí)行檢索幢痘,從匹配檢索的結(jié)果中讀取所有的數(shù)據(jù)唬格,然后將它們放到內(nèi)存當(dāng)中(也就是內(nèi)存分配)。然而颜说,這時(shí)候你需要對(duì)其格式進(jìn)行反序列化(deserialize)购岗,因?yàn)橛脖P上存儲(chǔ)的格式不能直接在內(nèi)存中使用,這意味著你需要調(diào)整位门粪,以便 CPU 能夠?qū)ζ溥M(jìn)行處理喊积。

然而Realm跳過(guò)了整個(gè)拷貝數(shù)據(jù)到內(nèi)存的過(guò)程,稱之為zero-copy玄妈。做到這點(diǎn)是因?yàn)槲募冀K是內(nèi)存映射的乾吻,無(wú)論文件是或否在內(nèi)存當(dāng)中髓梅,你都能夠訪問(wèn)文件的任何內(nèi)容。關(guān)于核心文件格式的重要一點(diǎn)就是绎签,確保硬盤上的文件格式都是內(nèi)存可讀的枯饿,這樣就無(wú)需執(zhí)行任何反序列化操作了。

這樣就帶來(lái)了一個(gè)問(wèn)題诡必,難道數(shù)據(jù)全加載到內(nèi)存里了鸭你?所以這里懶加載應(yīng)運(yùn)而生,比如在查詢到一組數(shù)據(jù)后擒权,只有當(dāng)你真正訪問(wèn)對(duì)象的時(shí)候才真正加載進(jìn)來(lái)。

VS SQLite

SQLite第一個(gè)版本發(fā)布于2000年阁谆,至今已16年碳抄。以當(dāng)今的角度來(lái)看,它的編程抽象程度非常低场绿。業(yè)務(wù)上我們其實(shí)只想把這些對(duì)象存進(jìn)去剖效,可以查詢出來(lái)。

即便已經(jīng)是封裝過(guò)的FMDB焰盗,要寫這樣的代碼心里也依舊難受:

FMDatabase *db = [FMDatabase databaseWithPath:@"/tmp/tmp.db"];
if (![db open]) {
[db release];
return;
}
NSString *sql = @"create table bulktest1 (id integer primary key autoincrement, x text);"
"create table bulktest2 (id integer primary key autoincrement, y text);"
"create table bulktest3 (id integer primary key autoincrement, z text);"
"insert into bulktest1 (x) values ('XXX');"
"insert into bulktest2 (y) values ('YYY');"
"insert into bulktest3 (z) values ('ZZZ');";
success = [db executeStatements:sql];
sql = @"select count(*) as count from bulktest1;"
"select count(*) as count from bulktest2;"
"select count(*) as count from bulktest3;";
success = [self.db executeStatements:sql withResultBlock:^int(NSDictionary *dictionary) {
NSInteger count = [dictionary[@"count"] integerValue];
XCTAssertEqual(count, 1, @"expected one record for dictionary %@", dictionary);
return 0;
}];
[db close];

VS CoreData

詳細(xì)的比較推薦看這篇:CoreData VS Realm璧尸。

下面給出一個(gè)查詢的比較:

// Core Data
let fetchRequest = NSFetchRequest(entityName: "Specimen")
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString)
fetchRequest.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "name", ascending: true)
fetchRequest.sortDescriptors = [sortDescriptor]
let error = NSError()
let results = managedObjectContext?.executeFetchRequest(fetchRequest, error:&error)

Realm則簡(jiǎn)單的多:

    
// Realm
let predicate = NSPredicate(format: "name BEGINSWITH [c]%@", searchString);
let specimens = Specimen.objectsWithPredicate(predicate).arraySortedByProperty("name", ascending: true)

總結(jié)一下Realm對(duì)CoreData的優(yōu)勢(shì):
- 不需要架構(gòu)Context那種煩人的東西
- CoreData 是一個(gè)博大精深的技術(shù),不要妄想幾天之內(nèi)可以用的很溜熬拒。
- 支持 NSPredicate
- CoreData多個(gè)持久化文件很麻煩爷光,Realm輕松支持這個(gè)功能

劣勢(shì):

- 是會(huì)增加應(yīng)用大概1MB的體積。CoreData原生支持澎粟,不會(huì)增加App體積蛀序。

Realm大部分源碼公開(kāi)在github上:realm。項(xiàng)目在新建兩年多時(shí)間活烙,已經(jīng)得到開(kāi)源社區(qū)大量關(guān)注:

官方也承諾會(huì)持續(xù)解決用戶反饋的各種問(wèn)題徐裸。也可以直接在他們twitter上去@他們。

需要知道的一些問(wèn)題

其實(shí)我自己覺(jué)得這些是可以接受的問(wèn)題啸盏。技術(shù)很多時(shí)候就是權(quán)衡重贺,為了達(dá)到一些目的,總是要犧牲掉一些東西回懦。

  • 所有的存儲(chǔ)對(duì)象需要繼承RealmObject

比如我現(xiàn)在的項(xiàng)目的數(shù)據(jù)從網(wǎng)絡(luò)請(qǐng)求回來(lái)都會(huì)繼承自己寫的一個(gè)方便解析的基類气笙,在這里就需要做出一些適應(yīng)。

但是該問(wèn)題在swift中是不存在的粉怕。因?yàn)閟wift是天生的面向協(xié)議編程范式健民。

  • 不能自定義getter、setter

realm會(huì)自動(dòng)生成getter贫贝、setter秉犹,如果自定義getter蛉谜、setter存儲(chǔ)就會(huì)有影響。如果要規(guī)避這個(gè)問(wèn)題崇堵,可以通過(guò)設(shè)置這個(gè)對(duì)象的忽略屬性型诚。

比如有個(gè)屬性id,需要自定義setter鸳劳≌幔可以在對(duì)象屬性里把id設(shè)置為忽略屬性,這樣realm就不會(huì)為它自動(dòng)生成getter赏廓、setter涵紊,但是也不會(huì)把id存入數(shù)據(jù)庫(kù)。接著自定義一個(gè)用于存儲(chǔ)的屬性比如realm_id幔摸。在id的setter中可以把把值也賦給realm_id摸柄。

這個(gè)問(wèn)題在swift中也是不存在的,因?yàn)閟wfit中使用的是willset既忆、didset這種通知機(jī)制驱负。

  • 查詢的結(jié)果不是數(shù)組

為了能夠支持查詢結(jié)果的鏈?zhǔn)讲樵儯瑀ealm自定義了一個(gè)集合類型患雇≡炯梗可以枚舉,但是不是熟悉的數(shù)組了苛吱。又因?yàn)閞ealm的懶加載機(jī)制酪术,所以不建議在數(shù)據(jù)層把這個(gè)枚舉轉(zhuǎn)成數(shù)組類型。這樣的缺點(diǎn)就是上層知道了數(shù)據(jù)的存儲(chǔ)邏輯翠储。嚴(yán)格的說(shuō)這里不應(yīng)該讓上層知道拼缝。但是這樣設(shè)計(jì)也許是為了方便上層進(jìn)行再次檢索,realm有著優(yōu)越的查詢性能彰亥。

具體的使用可在realm文檔中查看
感謝作者 原文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咧七,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子任斋,更是在濱河造成了極大的恐慌继阻,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件废酷,死亡現(xiàn)場(chǎng)離奇詭異瘟檩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)澈蟆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門墨辛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人趴俘,你說(shuō)我怎么就攤上這事睹簇∽嘧福” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵太惠,是天一觀的道長(zhǎng)磨淌。 經(jīng)常有香客問(wèn)我,道長(zhǎng)凿渊,這世上最難降的妖魔是什么梁只? 我笑而不...
    開(kāi)封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮埃脏,結(jié)果婚禮上搪锣,老公的妹妹穿的比我還像新娘。我一直安慰自己彩掐,他們只是感情好淤翔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著佩谷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪监嗜。 梳的紋絲不亂的頭發(fā)上谐檀,一...
    開(kāi)封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音裁奇,去河邊找鬼桐猬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛刽肠,可吹牛的內(nèi)容都是我干的溃肪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼音五,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼惫撰!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起躺涝,我...
    開(kāi)封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤厨钻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后坚嗜,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體夯膀,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年苍蔬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诱建。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碟绑,死狀恐怖俺猿,靈堂內(nèi)的尸體忽然破棺而出茎匠,到底是詐尸還是另有隱情,我是刑警寧澤辜荠,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布汽抚,位于F島的核電站,受9級(jí)特大地震影響伯病,放射性物質(zhì)發(fā)生泄漏造烁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一午笛、第九天 我趴在偏房一處隱蔽的房頂上張望惭蟋。 院中可真熱鬧,春花似錦药磺、人聲如沸告组。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至氏仗,卻和暖如春围辙,著一層夾襖步出監(jiān)牢的瞬間我碟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工姚建, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留矫俺,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓掸冤,卻偏偏與公主長(zhǎng)得像厘托,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子稿湿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容