之前我們介紹過Key-value數(shù)據(jù)的分區(qū)模型。如果數(shù)據(jù)只能由主鍵進(jìn)行訪問,我們可以由Key確定分區(qū),并用它路由到Key所在的分區(qū)進(jìn)行讀取操作。
如果引入二級索引(Secondary Indexes),情況會更加復(fù)雜。二級索引通常不像主鍵那樣,可以唯一確定數(shù)據(jù),二級索引更多用來查找出現(xiàn)某個(gè)特定值的所有數(shù)據(jù),比如:查找用戶123的所有行為,查找包含hogwash的所有文章等。
二級索引是數(shù)據(jù)庫的重要功能,在文檔型數(shù)據(jù)庫中也很常見,但并不是所有數(shù)據(jù)庫都支持它。許多Key-Value數(shù)據(jù)庫,例如HBase和Voldemort,由于二級索引的復(fù)雜性,因此并未支持;其他數(shù)據(jù)庫,比如Risk,考慮到二級索引對數(shù)據(jù)模型的重要性而做了支持;類似Solr和Elasticsearch這種數(shù)據(jù)庫,天生是為二級索引而存在的。
二級索引的最大問題是它和數(shù)據(jù)分區(qū)不是完全對應(yīng)的,因此有兩種設(shè)計(jì)二級索引的方法,分別為:基于文檔的二級索引分區(qū)和基于詞的二級索引分區(qū)。
基于文檔的二級索引分區(qū)
假設(shè)我們有一個(gè)售車網(wǎng)站,每輛車有一個(gè)唯一ID,稱為文檔ID,數(shù)據(jù)庫的分區(qū)基于此ID,比如分區(qū)0中包含的數(shù)據(jù)ID范圍為0-499,分區(qū)1中包含的數(shù)據(jù)ID范圍為500-999等。
現(xiàn)在用戶想要查找車輛,我們允許它通過顏色和品牌過濾,因此需要在顏色和品牌上添加二級索引。當(dāng)一個(gè)紅色的卡添加到數(shù)據(jù)庫時(shí),數(shù)據(jù)庫分區(qū)中自動將文檔ID添加到color:red的索引列表里,如下圖所示。

在這種索引方式下,每個(gè)分區(qū)維護(hù)自己的二級索引,只管理分區(qū)內(nèi)的文檔,它并不關(guān)心其他分區(qū)存儲的數(shù)據(jù)。當(dāng)寫數(shù)據(jù)庫時(shí),只需要修改文檔ID對應(yīng)的分區(qū)。因此,這種索引方式也被稱為本地索引。
在查詢紅色的車時(shí),需要查詢所有的分區(qū),并將每個(gè)分區(qū)的結(jié)果合并在一起,這種查詢分區(qū)數(shù)據(jù)庫的方式稱為scatter/gather。該查詢方式使得二級索引的查詢成本很高,很容易出現(xiàn)尾延遲的情況。特別是如果查詢多個(gè)二級索引字段的情況,很難避免查詢所有的分區(qū)。即使如此,它也被廣泛使用,比如MongoDB、Riak、Cassandra、Elasticsearch、SolrCloud和VoltDB等。
基于詞(Term)的二級索引分區(qū)
基于詞的二級索引分區(qū),也稱為全局索引,將所有二級索引的內(nèi)容進(jìn)行統(tǒng)一維護(hù),并按照和主鍵不同的方式進(jìn)行分區(qū)。比如汽車的顏色,將顏色字母從a到r的存儲在分區(qū)0,從s到z的存儲在分區(qū)1。

可以用詞本身,或者詞的Hash確定二級索引的分區(qū)。使用詞本身的話對范圍查找支持更好,比如查詢車的價(jià)格在某區(qū)間范圍的場景;使用詞的Hash會將負(fù)載分配得更加平均。
采用這種二級索引的方式,如果查找紅色的車,客戶端不需要讀取所有的分區(qū),使讀更加有效??蛻舳讼认蚨壦饕@個(gè)詞的分區(qū)發(fā)送請求,然后再根據(jù)索引指向的位置,向數(shù)據(jù)所在的分區(qū)發(fā)起請求。但寫會更復(fù)雜,并且性能會下降,因?yàn)閷懭胍粋€(gè)文檔會操作更多分區(qū),同時(shí)還會引入分布式事務(wù)的問題。
使用基于詞的二級索引分區(qū),通常是以異步的方式,可能是并不是立即讀到寫入的數(shù)據(jù),比如Amazon的DynamoDB。
有一些數(shù)據(jù)庫允許用戶從這兩種二級索引分區(qū)方式進(jìn)行選擇,比如Riak。
小結(jié)
本節(jié)介紹了關(guān)于數(shù)據(jù)二級索引的設(shè)計(jì)方式,基于文檔的二級索引分區(qū)和基于詞的二級索引分區(qū),和它們各自的特點(diǎn)和優(yōu)缺點(diǎn)對比。