MongoDB的水平擴(kuò)展,你做對(duì)了嗎?

大數(shù)據(jù)

作者:趙翼

分布式數(shù)據(jù)庫(kù)的前世今生

當(dāng)人們一開始使用數(shù)據(jù)庫(kù)系統(tǒng)的時(shí)候,所有數(shù)據(jù)都是跑在一臺(tái)服務(wù)器上,即所謂的單機(jī)數(shù)據(jù)庫(kù)服務(wù)器。在企業(yè)級(jí)應(yīng)用中,我們會(huì)搭建一臺(tái)應(yīng)用程序服務(wù)器,一般它會(huì)被運(yùn)行在一臺(tái)服務(wù)器或者工作站上,大多數(shù)情況下采用 Linux/Unix/Windows 操作系統(tǒng),也有人把這樣的服務(wù)器稱之為應(yīng)用程序服務(wù)器。顧名思義,他的作用是處理復(fù)雜的業(yè)務(wù)邏輯。但是一點(diǎn)需要注意的是,在這樣的構(gòu)架中,這臺(tái)應(yīng)用程序服務(wù)器不會(huì)存儲(chǔ)任何業(yè)務(wù)數(shù)據(jù),也就是說(shuō),他只負(fù)責(zé)邏輯運(yùn)算,處理用戶請(qǐng)求,真正存放數(shù)據(jù)的地方是前面提到的那臺(tái)數(shù)據(jù)庫(kù)服務(wù)器。應(yīng)用程序服務(wù)器將用戶的請(qǐng)求轉(zhuǎn)換成數(shù)據(jù)庫(kù)語(yǔ)言(通常是 SQL),運(yùn)行在數(shù)據(jù)庫(kù)中,從而進(jìn)行數(shù)據(jù)的增刪改查。數(shù)據(jù)庫(kù)服務(wù)器不會(huì)對(duì)外直接開放,管理人員也不允許直接在數(shù)據(jù)庫(kù)層面操作數(shù)據(jù)。所有的操作都會(huì)經(jīng)過(guò)應(yīng)用程序服務(wù)器來(lái)完成。應(yīng)用程序?qū)?、?shù)據(jù)庫(kù)層再加上 UI 層,被稱為傳統(tǒng)的 Web 三層構(gòu)架。

Replication

隨著數(shù)據(jù)量的增大,技術(shù)的不斷進(jìn)步以及需求的增加,安全性、可靠性、容錯(cuò)性、可恢復(fù)性等因素被人們考慮進(jìn)數(shù)據(jù)庫(kù)的設(shè)計(jì)中。于是出現(xiàn)了分布式數(shù)據(jù)庫(kù)系統(tǒng)。以前在存儲(chǔ)數(shù)據(jù)的時(shí)候,都是采用單體構(gòu)架模式,及數(shù)據(jù)全部存儲(chǔ)在一臺(tái)數(shù)據(jù)庫(kù)中,一旦數(shù)據(jù)庫(kù)出現(xiàn)問(wèn)題,所有的應(yīng)用請(qǐng)求都會(huì)受到影響。數(shù)據(jù)庫(kù)的恢復(fù)也是一個(gè)令人頭疼的問(wèn)題。有時(shí)一次數(shù)據(jù)庫(kù)的全恢復(fù)會(huì)運(yùn)行幾個(gè)小時(shí)甚至幾天的時(shí)間。在互聯(lián)網(wǎng)應(yīng)用不斷普及的今天,業(yè)務(wù)需求對(duì)構(gòu)架產(chǎn)生了嚴(yán)峻的挑戰(zhàn)。沒(méi)有哪個(gè)互聯(lián)網(wǎng)應(yīng)用會(huì)允許若干小時(shí)的宕機(jī)時(shí)間。分布式數(shù)據(jù)庫(kù)的產(chǎn)生,為我們提供了技術(shù)上的解決方案。在部署數(shù)據(jù)庫(kù)的時(shí)候,不用于以前的單體應(yīng)用,分布式下數(shù)據(jù)庫(kù)部署包括多點(diǎn)部署,一套業(yè)務(wù)應(yīng)用數(shù)據(jù)庫(kù)被分布在多臺(tái)數(shù)據(jù)庫(kù)服務(wù)器上,分主從服務(wù)器。主服務(wù)器處理日常業(yè)務(wù)請(qǐng)求,從服務(wù)器在運(yùn)行時(shí)不斷的對(duì)主服務(wù)器進(jìn)行備份,當(dāng)主服務(wù)器出現(xiàn)宕機(jī)、或者運(yùn)行不穩(wěn)定的情況時(shí),從服務(wù)器會(huì)立刻替換成主服務(wù)器,繼續(xù)對(duì)外提供服務(wù)。此時(shí),開發(fā)運(yùn)維人員會(huì)對(duì)出現(xiàn)問(wèn)題的服務(wù)器進(jìn)行搶修、恢復(fù),之后再把它投入到生產(chǎn)環(huán)境中。這樣的構(gòu)架也被稱作為高可用構(gòu)架,它支持了災(zāi)難恢復(fù),為業(yè)務(wù)世界提供了可靠的支持,也是很多企業(yè)級(jí)應(yīng)用采用的主流構(gòu)架之一。需要指出的是,在這樣的主從設(shè)計(jì)中,從數(shù)據(jù)庫(kù)常常被設(shè)計(jì)成只讀,主數(shù)據(jù)庫(kù)支持讀寫操作。一般會(huì)有一臺(tái)主數(shù)據(jù)庫(kù)連接若干臺(tái)從數(shù)據(jù)庫(kù)。在互聯(lián)網(wǎng)產(chǎn)品的應(yīng)用中,人們大多數(shù)情況下會(huì)對(duì)應(yīng)用服務(wù)器請(qǐng)求讀操作,這樣應(yīng)用服務(wù)器可以把讀操作請(qǐng)求分發(fā)到若干個(gè)從數(shù)據(jù)庫(kù)中,這樣就避免了主數(shù)據(jù)庫(kù)的并發(fā)請(qǐng)求次數(shù)過(guò)高的問(wèn)題。至于為什么大多數(shù)應(yīng)用都是讀操作,你可以想一下在你使用微信或者微博的時(shí)候,你是看別人發(fā)布的圖片多還是自己發(fā)布的時(shí)候多。當(dāng)你不斷下滑屏幕,刷新朋友圈,這些都是讀請(qǐng)求。只有當(dāng)評(píng)論、點(diǎn)贊、分享的時(shí)候才會(huì)進(jìn)行寫操作。

我們的世界就是這樣,當(dāng)技術(shù)為人們解決了現(xiàn)實(shí)問(wèn)題以后,新的需求會(huì)層出不窮。智能手機(jī),互聯(lián)網(wǎng) +,創(chuàng)業(yè)潮的不斷興起,點(diǎn)燃了這樣一個(gè)擁有幾千年文明歷史的民族的激情。各種新點(diǎn)子、新概念不斷的涌現(xiàn),誰(shuí)的手機(jī)里沒(méi)有安裝幾十個(gè)互聯(lián)網(wǎng)應(yīng)用,從訂餐,快遞,到住房,旅游,再到教育,養(yǎng)老,那一個(gè)環(huán)節(jié)沒(méi)有互聯(lián)網(wǎng)的支持,沒(méi)有技術(shù)的成分。我們就是生存在這樣一個(gè)的平凡而又不乏豪情的社會(huì)中。許許多多的需求和數(shù)據(jù)充斥著我們的構(gòu)架,挑戰(zhàn)著我們的存儲(chǔ)。

對(duì)此,你可能已經(jīng)想到,前面提到的分布式數(shù)據(jù)庫(kù)多點(diǎn)部署是不是會(huì)存在大量的瓶頸。比如,在主從數(shù)據(jù)庫(kù)結(jié)構(gòu)中,從數(shù)據(jù)庫(kù)的內(nèi)容基本上可以說(shuō)是主數(shù)據(jù)庫(kù)的一份全拷貝,這樣的技術(shù)稱之為Replication。Replication在實(shí)現(xiàn)主從數(shù)據(jù)同步時(shí),通常采用Transaction Log的方式,比如,當(dāng)一條數(shù)據(jù)插入到主數(shù)據(jù)庫(kù)的時(shí)候,主數(shù)據(jù)庫(kù)會(huì)像Trasaction Log中插入一條記錄來(lái)聲明這次數(shù)據(jù)庫(kù)寫紀(jì)錄的操作。之后,一個(gè)Replication Process會(huì)被觸發(fā),這個(gè)進(jìn)程會(huì)把Transaction Log中的內(nèi)容同步到從數(shù)據(jù)庫(kù)中。整個(gè)過(guò)程如下圖所示:

大數(shù)據(jù)

對(duì)于數(shù)據(jù)庫(kù)的擴(kuò)展來(lái)說(shuō),通常有兩種方法,水平擴(kuò)展和垂直擴(kuò)展。

垂直擴(kuò)展:這種擴(kuò)展方式比較傳統(tǒng),是針對(duì)一臺(tái)服務(wù)器進(jìn)行硬件升級(jí),比如添加強(qiáng)大的 CPU,內(nèi)存或者添加磁盤空間等等。這種方式的局限性是僅限于單臺(tái)服務(wù)器的擴(kuò)容,盡可能的增加單臺(tái)服務(wù)器的硬件配置。優(yōu)點(diǎn)是構(gòu)架簡(jiǎn)單,只需要維護(hù)單臺(tái)服務(wù)器。水平擴(kuò)展:這種方式是目前構(gòu)架上的主流形式,指的是通過(guò)增加服務(wù)器數(shù)量來(lái)對(duì)系統(tǒng)擴(kuò)容。在這樣的構(gòu)架下,單臺(tái)服務(wù)器的配置并不會(huì)很高,可能是配置比較低、很廉價(jià)的 PC,每臺(tái)機(jī)器承載著系統(tǒng)的一個(gè)子集,所有機(jī)器服務(wù)器組成的集群會(huì)比單體服務(wù)器提供更強(qiáng)大、高效的系統(tǒng)容載量。這樣的問(wèn)題是系統(tǒng)構(gòu)架會(huì)比單體服務(wù)器復(fù)雜,搭建、維護(hù)都要求更高的技術(shù)背景。MongoDB 中的 Sharding 正式為了水平擴(kuò)展而設(shè)計(jì)的,下面就來(lái)擠開 shard 面紗,探討一下 shard 中不同分片的技術(shù)區(qū)別以及對(duì)數(shù)據(jù)庫(kù)系統(tǒng)的影響。

分片 (Shard)

前面提到的 Replication 結(jié)構(gòu)可以保證數(shù)據(jù)庫(kù)中的全部數(shù)據(jù)都會(huì)有多分拷貝,數(shù)據(jù)庫(kù)的高可用可以保障。但是新的問(wèn)題是如果要存儲(chǔ)大量的數(shù)據(jù),不論主從服務(wù)器,都需要存儲(chǔ)全部數(shù)據(jù),這樣檢索必然會(huì)出現(xiàn)性能問(wèn)題??梢赃@樣講,Replication只能算是分布式數(shù)據(jù)庫(kù)的第一階段。主要解決的是數(shù)據(jù)庫(kù)高可用,讀數(shù)據(jù)可以水平擴(kuò)展,部分解決了主數(shù)據(jù)并發(fā)訪問(wèn)量大的問(wèn)題。但是它并沒(méi)有解決數(shù)據(jù)庫(kù)寫操作的分布式需求,此外在數(shù)據(jù)庫(kù)查詢時(shí)也只限制在一臺(tái)服務(wù)器上,并不能支持一次查詢多臺(tái)數(shù)據(jù)庫(kù)服務(wù)器。我們假設(shè),如果有一種構(gòu)架,可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)水平切分,把切分的數(shù)據(jù)分布存儲(chǔ)在不同的服務(wù)器上,這樣當(dāng)查詢請(qǐng)求發(fā)送到數(shù)據(jù)庫(kù)時(shí),可以在多臺(tái)數(shù)據(jù)庫(kù)中異步檢索符合查詢條件的語(yǔ)句,這樣不但可以利用多臺(tái)服務(wù)器的 CPU,而且還可以充分利用不同服務(wù)器上的 IO,顯而易見這樣的構(gòu)架會(huì)大大提高查詢語(yǔ)句的性能。但是這樣的實(shí)現(xiàn)卻給數(shù)據(jù)庫(kù)設(shè)計(jì)者代碼不少麻煩,首先要解決的就是事務(wù)(Transaction),我們知道在進(jìn)行一次數(shù)據(jù)庫(kù)寫操作的時(shí)候,需要定一個(gè)事務(wù)操作,這樣在操作失敗的時(shí)候可以回滾到原始狀態(tài),那當(dāng)在分布式數(shù)據(jù)庫(kù)的情況下,事務(wù)需要跨越多個(gè)數(shù)據(jù)庫(kù)節(jié)點(diǎn)以保持?jǐn)?shù)據(jù)的完整性,這給開發(fā)者帶來(lái)不少的麻煩。此外,在關(guān)系型數(shù)據(jù)庫(kù)中存在大量表關(guān)聯(lián)的情況,分布式的查詢操作就會(huì)牽扯到大量的數(shù)據(jù)遷移,顯然這必將降低數(shù)據(jù)庫(kù)性能。但是,在非關(guān)系型數(shù)據(jù)庫(kù)中,我們?nèi)趸踔寥コ耸聞?wù)和多表關(guān)聯(lián)操作,根據(jù) CAP 理論:在分布式數(shù)據(jù)庫(kù)環(huán)境中,為了保持構(gòu)架的擴(kuò)展性,在分區(qū)容錯(cuò)性不變的前提下,我們必須從一致性和可用性中取其一,那么,從這一點(diǎn)上來(lái)理解“NoSQL 數(shù)據(jù)庫(kù)是為了保證 A 與 P,而犧牲 C”的說(shuō)法,也是可以講得通的。同時(shí),根據(jù)該理論,業(yè)界有一種非常流行的認(rèn)識(shí),那就是:關(guān)系型數(shù)據(jù)庫(kù)設(shè)計(jì)選擇了一致性與可用性,NoSQL 數(shù)據(jù)庫(kù)設(shè)計(jì)則不同。其中,HBase選擇了一致性與分區(qū)可容忍性,Cassandra選擇了可用性與分區(qū)可容忍性。

本文關(guān)注于非關(guān)系型數(shù)據(jù)庫(kù)中分區(qū)的技巧和性能,以 MongoDB 為例進(jìn)行說(shuō)明,在下面的章節(jié)中就圍繞這一點(diǎn)展開討論。

MongoDB 分片原理

MongoDB 中通過(guò) Shard 支持服務(wù)器水平擴(kuò)展,通過(guò) Replication 支持高可用(HA)。這兩種技術(shù)可以分開來(lái)使用,但是在大數(shù)據(jù)庫(kù)企業(yè)級(jí)應(yīng)用中通常人們會(huì)把他們結(jié)合在一起使用。

MongoDB Sharding

首先我們簡(jiǎn)要概述一下分片在 MongoDB 中的工作原理。通過(guò)分片這個(gè)單詞我們可以看出,他的意思是將數(shù)據(jù)庫(kù)表中的數(shù)據(jù)按照一定的邊界分成若干組,每一組放到一臺(tái) MongoDB 服務(wù)器上。拿用戶數(shù)據(jù)舉例,比如你有一張數(shù)據(jù)表存放用戶基本信息,可能由于你的應(yīng)用很受歡迎,短時(shí)間內(nèi)就積攢了上億個(gè)用戶,這樣當(dāng)你在這張表上進(jìn)行查詢時(shí)通常會(huì)耗費(fèi)比較長(zhǎng)的時(shí)間,這樣這個(gè)用戶表就稱為了你的應(yīng)用程序的性能瓶頸。很顯然的做法是對(duì)這張用戶表進(jìn)行拆分,假設(shè)用戶表中有一個(gè)age年齡字段,我們先做一個(gè)簡(jiǎn)單的拆分操作,按照用戶的年齡段把數(shù)據(jù)放到不同的服務(wù)器上,以 20 為一個(gè)單位,20 歲以下的用戶放到 server1,20 到 40 歲的用戶放到 server2,40-60 歲的用戶放到 server3,60 歲以上放到 server4,后面我們會(huì)講這樣的拆分是否合理。在這個(gè)例子中,用戶年齡age就是我們進(jìn)行Sharding(切分)的Shard Key(關(guān)于 Shard Key 的選擇后面會(huì)詳細(xì)介紹),拆分出來(lái)的server1,?server2,?server3和server4就是這個(gè)集群中的 4 個(gè)Shard(分區(qū))服務(wù)器。好,Shard 集群已經(jīng)有了,并且數(shù)據(jù)已經(jīng)拆分完好,當(dāng)用戶進(jìn)行一次查詢請(qǐng)求的時(shí)候我們?nèi)绾蜗蜻@四個(gè) Shard 服務(wù)器發(fā)送請(qǐng)求呢?例如:我的查詢條件是用戶年齡在 18 到 35 歲之間,這樣個(gè)查詢請(qǐng)求應(yīng)當(dāng)發(fā)送到server1和server2,因?yàn)樗麄兇鎯?chǔ)了用戶年齡在 40 以下的數(shù)據(jù),我們不希望這樣的請(qǐng)求發(fā)送到另外兩臺(tái)服務(wù)器中因?yàn)樗麄儾⒉粫?huì)返回任何數(shù)據(jù)結(jié)果。此時(shí),另外一個(gè)成員就要登場(chǎng)了,mongos,它可以被稱為 Shard 集群中的路由器,就像我們網(wǎng)絡(luò)環(huán)境中使用的路由器一樣,它的作用就是講請(qǐng)求轉(zhuǎn)發(fā)到對(duì)應(yīng)的目標(biāo)服務(wù)器中,有了它我們剛才那條查詢語(yǔ)句就會(huì)正確的轉(zhuǎn)發(fā)給server和server2,而不會(huì)發(fā)送到server3和server4上。mongos根據(jù)用戶年齡(Shard Key)分析查詢語(yǔ)句,并把語(yǔ)句發(fā)送到相關(guān)的 shard 服務(wù)器中。除了mongos和shard之外,另一個(gè)必須的成員是配置服務(wù)器,config server,它存儲(chǔ) Shard 集群中所有其他成員的配置信息,mongos會(huì)到這臺(tái)config server查看集群中其他服務(wù)器的地址,這是一臺(tái)不需要太高性能的服務(wù)器,因?yàn)樗粫?huì)用來(lái)做復(fù)雜的查詢計(jì)算,值得注意的是,在 MongoDB3.4 以后,config server必須是一個(gè)replica set。理解了上面的例子以后,一個(gè) Shard 集群就可以部署成下圖所示的結(jié)構(gòu):

大數(shù)據(jù)

其中:

shard: 每一個(gè) Shard 服務(wù)器存儲(chǔ)數(shù)據(jù)的一個(gè)子集,例如上面的用戶表,每一個(gè) Shard 存儲(chǔ)一個(gè)年齡段的用戶數(shù)據(jù)。mongos: 處理來(lái)自應(yīng)用服務(wù)器的請(qǐng)求,它是在應(yīng)用服務(wù)器和Shard 集群之間的一個(gè)接口。config server: 存儲(chǔ) shard 集群的配置信息,通常部署在一個(gè) replica set 上。

MongoDB Shard 性能分析

環(huán)境準(zhǔn)備

這樣的服務(wù)器構(gòu)架是否合理,或者說(shuō)是否能夠滿足數(shù)據(jù)量不斷增長(zhǎng)的需求。如果僅僅是通過(guò)理論解釋恐怕很難服眾,我已經(jīng)信奉理論結(jié)合實(shí)際的工作方式,所以在我的文章中除了闡述理論之外,一定會(huì)有一定的示例為大家驗(yàn)證理論的結(jié)果。接下來(lái)我們就根據(jù)上面的例子做一套本地運(yùn)行環(huán)境。由于 MongoDB 的便捷性,我們可以在任何一臺(tái) PC 上搭建這樣一個(gè)數(shù)據(jù)庫(kù)集群環(huán)境,并且不限制操作系統(tǒng)類型,任何 Windows/Linux/Mac 的主流版本都可以運(yùn)行這樣的環(huán)境。在本文中,我才用 MongoDB3.4 版本。

對(duì)于如何創(chuàng)建一個(gè) MongoDB Shard 環(huán)境,網(wǎng)上有很多教程和命令供大家選擇,創(chuàng)建一個(gè)有 3 個(gè) Mongos,每個(gè) Mongos 連接若干個(gè) Shards,再加上 3 個(gè) config server cluster,通常需要 20 幾臺(tái) MongoDB 服務(wù)器。如果一行命令一行命令的打,即便是在非常熟練的情況下,沒(méi)有半個(gè)小時(shí)恐怕搭建不出來(lái)。不過(guò)幸運(yùn)的是有第三方庫(kù)幫我們做這個(gè)事情,大家可以查看一下mtools。他是用來(lái)創(chuàng)建各種 MongoDB 環(huán)境的命令行工具,代碼使用python寫的,可以通過(guò)pip install安裝到你的環(huán)境上。具體的使用方法可以參考https://github.com/rueckstiess/mtools/wiki/mlaunch。也可以通過(guò)https://github.com/zhaoyi0113/mongo-cluster-docker上面的腳本把環(huán)境搭載 Docker 上面。

下面的命令用來(lái)在本地創(chuàng)建一個(gè) MongoDB Shard 集群,包含 1 個(gè)mongos路由,3 個(gè)shardreplica,每個(gè) replica 有 3 個(gè)shard服務(wù)器,3 個(gè)config服務(wù)器。這樣一共創(chuàng)建 13 個(gè)進(jìn)程。

mlaunch init --replicaset --sharded 3 --nodes 3 --config 3 --hostname localhost --port 38017 --mongos 1

服務(wù)器創(chuàng)建好以后我們可以連接到mongos上看一下 shard 狀態(tài),端口是上面制定的 38017。

mongos> sh.status()--- Sharding Status ---  ?... ?shards: ? ?{ ?"_id" : "shard01", ?"host" : "shard01/localhost:38018,localhost:38019,localhost:38020", ?"state" : 1 } ? ?{ ?"_id" : "shard02", ?"host" : "shard02/localhost:38021,localhost:38022,localhost:38023", ?"state" : 1 } ? ?{ ?"_id" : "shard03", ?"host" : "shard03/localhost:38024,localhost:38025,localhost:38026", ?"state" : 1 } ?active mongoses: ? ?"3.4.0" : 1 ?...

可以看到剛才創(chuàng)建的 shard 服務(wù)器已經(jīng)加入到這臺(tái) mongos 中了,這里有 3 個(gè) shard cluster,每個(gè) cluster包含 3 個(gè) shard 服務(wù)器。除此之外,我們并沒(méi)有看到關(guān)于 Shard 更多的信息。這是因?yàn)檫@臺(tái)服務(wù)器集群還沒(méi)有任何數(shù)據(jù),而且也沒(méi)有進(jìn)行數(shù)據(jù)切分。

數(shù)據(jù)準(zhǔn)備

首先是數(shù)據(jù)的錄入,為了分析我們服務(wù)器集群的性能,需要準(zhǔn)備大量的用戶數(shù)據(jù),幸運(yùn)的是mtools提供了mgenerate方法供我們使用。他可以根據(jù)一個(gè)數(shù)據(jù)模版向 MongoDB 中插入任意條 json 數(shù)據(jù)。下面的 json 結(jié)構(gòu)是我們?cè)诶又行枰褂玫臄?shù)據(jù)模版:

{ ? ?"user": { ? ? ? ?"name": { ? ? ? ? ? ?"first": {"$choose": ["Liam", "Aubrey", "Zoey", "Aria", "Ellie", "Natalie", "Zoe", "Audrey", "Claire", "Nora", "Riley", "Leah"] }, ? ? ? ? ? ?"last": {"$choose": ["Smith", "Patel", "Young", "Allen", "Mitchell", "James", "Anderson", "Phillips", "Lee", "Bell", "Parker", "Davis"] } ? ? ? ?},  ? ? ? ?"gender": {"$choose": ["female", "male"]}, ? ? ? ?"age": "$number",  ? ? ? ?"address": { ? ? ? ? ? ?"zip_code": {"$number": [10000, 99999]}, ? ? ? ? ? ?"city": {"$choose": ["Beijing", "ShangHai", "GuangZhou", "ShenZhen"]} ? ? ? ?}, ? ? ? ?"created_at": {"$date": ["2010-01-01", "2014-07-24"] } ? ?}}

把它保存為一個(gè)叫user.json的文件中,然后使用mgenerate插入一百條隨機(jī)數(shù)據(jù)。隨機(jī)數(shù)據(jù)的格式就按照上面json文件的定義。你可以通過(guò)調(diào)整?–num的參數(shù)來(lái)插入不同數(shù)量的 Document。(Link to mgenerate wiki)

mgenerate user.json --num 1000000 --database test --collection users --port 38017

上面的命令會(huì)像test數(shù)據(jù)庫(kù)中users?collection 插入一百萬(wàn)條數(shù)據(jù)。在有些機(jī)器上,運(yùn)行上面的語(yǔ)句可能需要等待一段時(shí)間,因?yàn)樯梢话偃f(wàn)條數(shù)據(jù)是一個(gè)比較耗時(shí)的操作,之所以生成如此多的數(shù)據(jù)是方便后面我們分析性能時(shí),可以看到性能的顯著差別。當(dāng)然你也可以只生成十萬(wàn)條數(shù)據(jù)來(lái)進(jìn)行測(cè)試,只要能夠在你的機(jī)器上看到不同find語(yǔ)句的執(zhí)行時(shí)間差異就可以。

插入完數(shù)據(jù)之后,我們想看一下剛剛插入的數(shù)據(jù)在服務(wù)器集群中是如何分配的。通常,可以通過(guò)sh.status()?MongoDB shell 命令查看。不過(guò)對(duì)于一套全新的集群服務(wù)器,再?zèng)]有切分任何 collection 之前,我們是看不到太多有用的信息。不過(guò),可以通過(guò) explain 一條查詢語(yǔ)句來(lái)看一下數(shù)據(jù)的分布情況。這里不得不強(qiáng)調(diào)一下在進(jìn)行數(shù)據(jù)性能分析時(shí)一個(gè)好的 IDE 對(duì)工作效率有多大的影響,我選擇 dbKoda 作為 MongoDB 的 IDE 主要原因是他是目前唯一一款對(duì) MongoDB Shell 的完美演繹,對(duì)于 MongoDB Shell 命令不太熟悉的開發(fā)人員來(lái)說(shuō)尤為重要,幸運(yùn)的是這款 IDE 還支持 Windows/Mac/Linux 三種平臺(tái),基本上覆蓋了絕大多數(shù)操作系統(tǒng)版本。下面是對(duì)剛才建立的一百萬(wàn)條 collection 的一次 find 的 explain 結(jié)果。(對(duì)于 Explain 的應(yīng)用,大家可以參考我的另外一片文章:如何通過(guò) MongoDB 自帶的 Explain 功能提高檢索性能?)

大數(shù)據(jù)

從上圖中可以看到,我們插入的一百萬(wàn)條數(shù)據(jù)全部被分配到了第一個(gè) shard 服務(wù)器中,這并不是我們想看到的結(jié)果,不要著急,因?yàn)槲疫€沒(méi)有進(jìn)行數(shù)據(jù)切分,MongoDB 并不會(huì)自動(dòng)的分配這些數(shù)據(jù)。下面我們來(lái)一點(diǎn)一點(diǎn)分析如何利用 Shard 實(shí)現(xiàn)高效的數(shù)據(jù)查詢。

配置 Shard 數(shù)據(jù)庫(kù)

環(huán)境搭建好并且數(shù)據(jù)已經(jīng)準(zhǔn)備完畢以后,接下來(lái)的事情就是配置數(shù)據(jù)庫(kù)并切分?jǐn)?shù)據(jù)。方便起見,我們把用戶分為三組,20 歲以下(junior),20 到 40 歲(middle)和 40 歲以上(senior),為了節(jié)省篇幅,我在這里不過(guò)多的介紹如何使用 MongoDB 命令,按照下面的幾條命令執(zhí)行以后,我們的數(shù)據(jù)會(huì)按照用戶年齡段拆分成若干個(gè) chunk,并分發(fā)到不同的 shard cluster 中。如果對(duì)下面的命令不熟悉,可以查看 MongoDB 官方文檔關(guān)于 Shard Zone/Chunk 的解釋。

db.getSiblingDB('test').getCollection('users').createIndex({'user.age':1})sh.setBalancerState(false)sh.addShardTag('shard01', 'junior')sh.addShardTag('shard02', 'middle')sh.addShardTag('shard03', 'senior')sh.addTagRange('test.users', {'user.age': MinKey}, {'user.age':20}, 'junior')sh.addTagRange('test.users', {'user.age': 21}, {'user.age':40}, 'middle')sh.addTagRange('test.users', {'user.age': 41}, {'user.age': MaxKey}, 'senior')sh.enableSharding('test')sh.shardCollection('test.users', {'user.age':1})sh.setBalancerState(true)

從上面的命令中可以看出,我們首先要為 Shard Key 創(chuàng)建索引,之后禁止 Balancer 的運(yùn)行,這么做的原因是不希望在 Shard Collection 的過(guò)程中還運(yùn)行 Balancer。之后將數(shù)據(jù)按照年齡分成三組,分別標(biāo)記為junior,?middle,senior并把這三組分別分配到三個(gè) Shard 集群中。 之后對(duì) test 庫(kù)中的 users collection 進(jìn)行按用戶年齡字段的切分操作,如果 Shard collection 成功返回,你會(huì)得到下面的輸出結(jié)果:{ “collectionsharded” : “test.users”, “ok” : 1 }。

關(guān)于 Shard 需要注意的幾點(diǎn)

一旦你對(duì)一個(gè) Colleciton 進(jìn)行了 Shard 操作,你選擇的 Shard Key 和它對(duì)應(yīng)的值將成為不可變對(duì)象,所以:你無(wú)法在為這個(gè) collection 重新選擇 Shard Key你不能更新 Shard key 的取值

隨后不要忘記,我們還需要將 Balancer 打開:sh.setBalancerState(true)。剛打開以后運(yùn)行sh.isBalancerRunning()應(yīng)當(dāng)返回true,說(shuō)明 Balancer 服務(wù)正在運(yùn)行,他會(huì)調(diào)整 Chunk 在不同 Shards 服務(wù)器中的分配。一般 Balancer 會(huì)運(yùn)行一段時(shí)間,因?yàn)樗獙?duì)分組的數(shù)據(jù)重新分配到指定的 shard 服務(wù)器上,你可以通過(guò)sh.isBalancerRunning()命令查看 Balancer 是否正在運(yùn)行?,F(xiàn)在可以稍事休息一下喝杯咖啡或看看窗外的風(fēng)景。

為了理解數(shù)據(jù)如何分布在 3 個(gè) shard 集群中,我們有必要分析一下 chunk 和 zone 的劃分,下圖是在 dbKoda 上顯示 Shard Cluster 統(tǒng)計(jì)數(shù)據(jù),可以看到數(shù)據(jù)總共被分成 6 個(gè) chunks,每個(gè) shard 集群存儲(chǔ) 2 個(gè) chunk。

大數(shù)據(jù)

對(duì)此有些同學(xué)會(huì)有疑問(wèn),為什么我們的數(shù)據(jù)會(huì)被分為 6 個(gè) chunks,而且每個(gè) shard 集群個(gè)分配了 2 個(gè) chunk。是誰(shuí)來(lái)保證數(shù)據(jù)的均勻分配?下面我就給大家解釋一下他們的概念以及我們應(yīng)當(dāng)如何使用。

Chunk

我們已經(jīng)知道 MongoDB 是通過(guò) shard key 來(lái)對(duì)數(shù)據(jù)進(jìn)行切分,被切分出來(lái)的數(shù)據(jù)被分配到若干個(gè) chunks 中。一個(gè) chunk 可以被認(rèn)為是一臺(tái) shard 服務(wù)器中數(shù)據(jù)的子集,根據(jù) shard key,每個(gè) chunk 都有上下邊界,在我們的例子中,邊界值就是用戶年齡。chunk 有自己的大小,數(shù)據(jù)不斷插入到 mongos 的過(guò)程中,chunk 的大小會(huì)發(fā)生變化,chunk 的默認(rèn)大小是 64M。當(dāng)然 MongoDB 允許你對(duì) chunk 的大小進(jìn)行設(shè)置,你也可以把一個(gè) chunk 切分成若干個(gè)小 chunk,或者合并多個(gè) chunk。一般我不建議大家手動(dòng)操作 chunk 的大小,或者在 mongos 層面切分或合并 chunk,除非真有合適的原因才去這么做。原因是,在數(shù)據(jù)不斷插入到我們的集群中時(shí),mongodb 中的 chunk 大小會(huì)發(fā)生很大的變化,當(dāng)一個(gè) chunk 的大小超過(guò)了最大值,mongo 會(huì)根據(jù) shard key 對(duì) chunk 進(jìn)行切分,在必要的時(shí)候,一個(gè) chunk 可能會(huì)被切分成多個(gè)小 chunk,大多數(shù)情況下這種自動(dòng)行為已經(jīng)滿足了我們?nèi)粘5臉I(yè)務(wù)需求,無(wú)需進(jìn)行手動(dòng)操作,另一點(diǎn)原因是當(dāng)進(jìn)行 chunk 切分后,直接的結(jié)果會(huì)導(dǎo)致數(shù)據(jù)分配的不均勻,此時(shí) balancer 會(huì)被調(diào)用來(lái)進(jìn)行數(shù)據(jù)重新分配,很多時(shí)候這個(gè)操作會(huì)運(yùn)行很長(zhǎng)時(shí)間,無(wú)形中導(dǎo)致了內(nèi)部結(jié)構(gòu)的負(fù)載平衡,因此不建議大家手動(dòng)拆分。當(dāng)然,理解 chunk 的分配原理還是有助于大家分析數(shù)據(jù)庫(kù)性能的必要條件。我在這里不過(guò)多的將如何進(jìn)行這些操作,有興趣的讀者可以參考 MongoDB 官方文檔,上面有比較全面的解釋。這里我只強(qiáng)調(diào)在進(jìn)行 chunk 操作的時(shí)候,要注意一下幾個(gè)方面,這些都是影響你 MongoDB 性能的關(guān)鍵因素。

如果存在大量體積很小的 chunk,他可以保證你的數(shù)據(jù)均勻的分布在 shard 集群中但是可能會(huì)導(dǎo)致頻繁的數(shù)據(jù)遷移。這將加重 mongos 層面上的操作。大的 chunk 會(huì)減少數(shù)據(jù)遷移,減輕網(wǎng)絡(luò)負(fù)擔(dān),降低在 mongos 路由層面上的負(fù)載,但弊端是有可能導(dǎo)致數(shù)據(jù)在 shard 集群中分布的不均勻。Balancer 會(huì)在數(shù)據(jù)分配不均勻的時(shí)候自動(dòng)運(yùn)行,那么 Balancer 是如何決定什么情況下需要進(jìn)行數(shù)據(jù)遷移呢?答案是 Migration Thresholds,當(dāng) chunk 的數(shù)量在不同 shard replica 之間超過(guò)一個(gè)定值時(shí),balancer 會(huì)自動(dòng)運(yùn)行,這個(gè)定值根據(jù)你的 shard 數(shù)量不同而不同。

Zones

可以說(shuō) chunk 是 MongoDB 在多個(gè) shard 集群中遷移數(shù)據(jù)的最小單元,有時(shí)候數(shù)據(jù)的分配不會(huì)按照我們臆想的方向進(jìn)行,就拿上面的例子來(lái)說(shuō),雖然我們選擇了用戶年齡作為 shard key,但是 MongoDB 并不會(huì)按照我們?cè)O(shè)想的那樣來(lái)分配數(shù)據(jù),如何進(jìn)行數(shù)據(jù)分配就是通過(guò) Zones 來(lái)實(shí)現(xiàn)。Zones 解決了 shard 集群與 shard key 之間的關(guān)系,我們可以按照 shard key 對(duì)數(shù)據(jù)進(jìn)行分組,每一組稱之為一個(gè) Zone,之后把 Zone 在分配給不同的 Shard 服務(wù)器。一個(gè) Shard 可以存儲(chǔ)一個(gè)或多個(gè) Zone,前提是 Zone 之間沒(méi)有數(shù)據(jù)沖突。Balancer 在運(yùn)行的時(shí)候會(huì)把在 Zone 里的 chunk 遷移到關(guān)聯(lián)這個(gè) Zone 的 shard 上。

理解了這些概念以后,我們對(duì)數(shù)據(jù)的分配就有了更清楚的認(rèn)識(shí)。我們對(duì)前面提到的問(wèn)題就有了充分的解釋。表面上看,數(shù)據(jù)的分布貌似均勻,我們執(zhí)行幾個(gè)查詢語(yǔ)句看看性能怎樣。這里再次用到 dbKoda 中的 explain 視圖。

大數(shù)據(jù)

上圖中查找年齡在 18 周歲以上的用戶,根據(jù)我們的分組定義,三個(gè) shard 上都有對(duì)應(yīng)的紀(jì)錄,但是 shard1 對(duì)應(yīng)的年齡組是 20 歲以下,應(yīng)該包括數(shù)量較少的數(shù)據(jù),所以在圖中 shard 里表里現(xiàn)實(shí)的 shard01 返回了 9904 條記錄,遠(yuǎn)遠(yuǎn)少于其他兩個(gè) shard,這也符合我們的數(shù)據(jù)定義。在上面性能描述中也可以看出,這條語(yǔ)句在 shard01 上面運(yùn)行的時(shí)間也是相對(duì)較少的。

再看看下面的例子,如果我們查找 25 周歲以上的用戶,得到的結(jié)果中并沒(méi)有出現(xiàn) shard1 的身影,這也是符合我們的數(shù)據(jù)分配,因?yàn)?shard1 只存儲(chǔ)了年齡小于 20 周歲的用戶。

大數(shù)據(jù)

你選擇的 Shard Key 合適嗎?

了解了數(shù)據(jù)是如何分布的以后,咱們?cè)倩剡^(guò)頭來(lái)看看我們選擇的 shard key 是否合理。細(xì)心的讀者已經(jīng)發(fā)現(xiàn),上面運(yùn)行的 explain 結(jié)果中存在一個(gè)問(wèn)題,就是 shard3 存儲(chǔ)了大量的數(shù)據(jù),如果我們看一下每個(gè)年齡組的紀(jì)錄個(gè)數(shù),會(huì)發(fā)現(xiàn) shard1、shard2、shard3 分別包括 198554, 187975, 593673,顯然年齡大于 40 歲的用戶占了大多數(shù)。這并不是我們希望的結(jié)果,因?yàn)?shard3 成為了集群中的一個(gè)瓶頸,數(shù)據(jù)庫(kù)操作語(yǔ)句在 shard3 上運(yùn)行的速度會(huì)大大超過(guò)另外兩個(gè) shard,這點(diǎn)從上面的 explain 結(jié)果中也可以看到,查詢語(yǔ)句在 shard3 上的運(yùn)行時(shí)間是另外兩個(gè) shard 的兩倍以上。更重要的是,隨著用戶數(shù)量的不斷增加,數(shù)據(jù)的分布也會(huì)出現(xiàn)顯著變化,在系統(tǒng)運(yùn)行一段時(shí)間以后,可能 shard2 的用戶數(shù)超過(guò) shard3,也有可能 shard1 稱為存儲(chǔ)數(shù)據(jù)量最多的服務(wù)器。這種數(shù)據(jù)不平衡是我們不希望看到的。原因在哪里呢?是不是覺(jué)得我們選擇的用戶年齡作為分組條件是一個(gè)不太理想的 key。那么什么樣的 key 能夠保證數(shù)據(jù)的均勻分布呢?接下來(lái)我們分析一下 shard key 的種類。

Ranged Shard Key

我們上面選擇的年齡分組就是用的這種 shard key。根據(jù) shard key 的取值,它把數(shù)據(jù)切分成連續(xù)的幾個(gè)區(qū)間。取值相近的紀(jì)錄會(huì)放進(jìn)同一個(gè) shard 服務(wù)器。好處是查詢連續(xù)取值紀(jì)錄時(shí),查詢效率可以得到保證。當(dāng)數(shù)據(jù)庫(kù)查詢語(yǔ)句發(fā)送到 mongos 中時(shí),mongos 會(huì)很快的找到目標(biāo) shard,而且不需要將語(yǔ)句發(fā)送到所有的 shard 上,一般只需要少量的 shard 就可以完成查詢操作。缺點(diǎn)是不能保證數(shù)據(jù)的平均分配,在數(shù)據(jù)插入和修改時(shí)會(huì)產(chǎn)生比較嚴(yán)重的性能瓶頸。

Hashed Shard Key

于 Ranged Shard Key 對(duì)應(yīng)的一種被稱之為 Hashed Shard Key,它采用字段的索引哈希值作為 shard key 的取值,這樣做可以保證數(shù)據(jù)的均勻分布。在 mongos 和各個(gè) shard 集群之間存在一個(gè)哈希值計(jì)算方法,所有的數(shù)據(jù)在遷移時(shí)都是根據(jù)這個(gè)方法來(lái)計(jì)算數(shù)據(jù)應(yīng)當(dāng)被遷移到什么地方。當(dāng) mongos 接收到一條語(yǔ)句時(shí),通常他會(huì)把這條語(yǔ)句廣播到所有的 shard 上去執(zhí)行。

有了上面的認(rèn)識(shí),我們?nèi)绾卧?Ranged 和 Shard 之間進(jìn)行選擇呢?下面兩個(gè)屬性是我們選擇 shard key 的關(guān)鍵。

Shard Key Cardinality (集)

Cardinality指的是 shard key 可以取到的不同值的個(gè)數(shù)。他會(huì)影響到 Balancer 的運(yùn)行,這個(gè)值也可以被看做是 Balancer 可以創(chuàng)建的最大 chunk 個(gè)數(shù)。以我們年齡字段為例,假如一個(gè)人的年齡在 100 歲以下,那么這個(gè)字段的 cardinality 可以取 100 個(gè)不同的值。對(duì)于一個(gè)唯一的年齡數(shù)據(jù),不會(huì)出現(xiàn)在不同的 chunk 中。如果你選擇的 Shard Key 的 cardinality 很小,比如只有 4 個(gè),那么數(shù)據(jù)最多會(huì)被分發(fā)到 4 個(gè)不同的 shard 中,這樣的結(jié)構(gòu)也不適合服務(wù)器的水平擴(kuò)展,因?yàn)椴粫?huì)有數(shù)據(jù)被分割到第五個(gè) shard 服務(wù)器上。

Shard Key Frequency(頻率)

Frequency指的是 shard key 的重復(fù)度,也就是對(duì)于一個(gè)字段,有多少取值相同的紀(jì)錄。如果大部分?jǐn)?shù)據(jù)的 shard key 取值相同,那么存儲(chǔ)他們的 chunk 會(huì)成為數(shù)據(jù)庫(kù)的一個(gè)瓶頸。而且,這些 chunk 也變成了不可再切分的 chunk,嚴(yán)重影響了數(shù)據(jù)庫(kù)的水平擴(kuò)展。在這種情況下應(yīng)當(dāng)考慮使用組合索引的方式來(lái)創(chuàng)建 shard key。所以,盡量選擇低頻率的字段作為 shard key。

Shard Key Increasing Monotonically (單調(diào)增長(zhǎng))

單調(diào)增長(zhǎng)在這里的意思是在數(shù)據(jù)被切分以后,新增加的數(shù)據(jù)會(huì)按照其 shard key 取值向 shard 中插入,如果新增的數(shù)據(jù)的 key 值都是向最大值方向增加,那么這些新的數(shù)據(jù)會(huì)被插入到同一個(gè) shard 服務(wù)器上。例如我們前面的用戶年齡分組字段,如果系統(tǒng)的新增用戶都是年齡大于 40 歲的,那么 shard3 將會(huì)存儲(chǔ)所有的新增用戶,shard3 會(huì)成為系統(tǒng)的性能瓶頸。在這種情況下,應(yīng)當(dāng)考慮使用 Hashed Shard Key。

重新設(shè)計(jì) Shard Key

通過(guò)上面的分析我們可以得出結(jié)論,前面例子中的用戶年齡字段是一個(gè)很糟糕的方案。有幾個(gè)原因:

用戶的年齡不是固定不變的,由于 shard key 是不可變字段,一旦確定下來(lái)以后不能進(jìn)行修改,所以年齡字段顯然不是很合適,畢竟沒(méi)有年齡永遠(yuǎn)不增長(zhǎng)的用戶。一個(gè)系統(tǒng)的用戶在不同年齡階段的分布是不一樣的,對(duì)于像游戲、娛樂(lè)方面的應(yīng)用可能會(huì)吸引年輕人多一些。而對(duì)于醫(yī)療、養(yǎng)生方面也許會(huì)有更多老年人關(guān)注。從這一點(diǎn)上說(shuō),這樣的切分也是不恰當(dāng)?shù)?。選擇年齡字段并沒(méi)有考慮到未來(lái)用戶增長(zhǎng)方面帶來(lái)的問(wèn)題,有可能在數(shù)據(jù)切分的時(shí)候年齡是均勻分布的,但是系統(tǒng)運(yùn)行一段時(shí)間以后有可能出現(xiàn)不平等的數(shù)據(jù)分布,這點(diǎn)會(huì)給數(shù)據(jù)維護(hù)帶來(lái)很大的困擾。

那么我們應(yīng)當(dāng)如何進(jìn)行選擇呢?看一下用戶表的所有屬性可以發(fā)現(xiàn),其中有一個(gè)created_at字段,它指的是紀(jì)錄創(chuàng)建的時(shí)間戳。如果采用 Ranged Key 那么在數(shù)據(jù)增長(zhǎng)方向上會(huì)出現(xiàn)單調(diào)增長(zhǎng)問(wèn)題,在分析一下發(fā)現(xiàn)這個(gè)字段重復(fù)的紀(jì)錄不多,他有很高的cardinality和非常低的頻率,這樣 Harded key 就成為了很好的備選方案。

分析完理論以后咱們實(shí)踐一下看看效果,不幸的是我們并不能修改 shard key,最好的方法就是備份數(shù)據(jù),重新創(chuàng)建 shard 集群。創(chuàng)建和數(shù)據(jù)準(zhǔn)備的過(guò)程我就不在重復(fù)了,你們可以根據(jù)前面的例子自己作一遍。

下圖中是我新建的一個(gè)userscollection,并以created_at為索引創(chuàng)建了 Hashed Shard Key,注意created_at必須是一個(gè) hash index 才能成為 hashed shard key。下面是針對(duì)用戶表的一次查詢結(jié)果。

大數(shù)據(jù)

從圖中可以看到,explain 的結(jié)果表示了三個(gè) shard 服務(wù)器基本上均勻分布了所有的數(shù)據(jù),三個(gè) shard 上執(zhí)行時(shí)間也都基本均勻,在 500 到 700 多毫秒以內(nèi)。還記得上面的幾次查詢結(jié)果嗎?在數(shù)據(jù)比較多的 shard 上的運(yùn)行時(shí)間在 1 到 2 毫秒。可以看到總的性能得到了顯著提高。

選擇完美的 Shard Key

在 shard key 的選擇方面,我們需要考慮很多因素,有些是技術(shù)的,有些是業(yè)務(wù)層面的。通常來(lái)講應(yīng)當(dāng)注意下面幾點(diǎn):

所有增刪改查語(yǔ)句都可以發(fā)送到集群中所有的 shard 服務(wù)器中任何操作只需要發(fā)送到與其相關(guān)的 shard 服務(wù)器中,例如一次刪除操作不應(yīng)當(dāng)發(fā)送到?jīng)]有包括要?jiǎng)h除的數(shù)據(jù)的 shard 服務(wù)器上

權(quán)衡利弊,實(shí)際上沒(méi)有完美的 shard key,只有選擇 shard key 時(shí)應(yīng)當(dāng)注意和考慮的要素。不會(huì)出現(xiàn)一種 shard key 可以滿足所有的增刪改查操作。你需要從給你的應(yīng)用場(chǎng)景中抽象出用來(lái)選擇 shard key 的元素,考量這些要素并作出最后選擇,例如:你的應(yīng)用是處理讀操作多還是寫操作多?最常用的寫操作場(chǎng)景是什么樣子的?

小結(jié)

在 shard key 的選擇方面沒(méi)有一個(gè)統(tǒng)一的方法,要根據(jù)具體的需求和數(shù)據(jù)增長(zhǎng)的方向來(lái)設(shè)計(jì)。在我們?nèi)粘5拈_發(fā)過(guò)程中,并不是所有技術(shù)問(wèn)題都應(yīng)當(dāng)由技術(shù)人員來(lái)解決,這個(gè)世界是一個(gè)業(yè)務(wù)驅(qū)動(dòng)的時(shí)代,而技術(shù)主要是為業(yè)務(wù)服務(wù),我們要提高對(duì)需求變化的相應(yīng)速度。像本文中如何選擇 Shard Key 的問(wèn)題,我覺(jué)得并不能單純的通過(guò)技術(shù)來(lái)考量,更多的是要和業(yè)務(wù)人員討論各個(gè)數(shù)據(jù)字段的意義,使用的業(yè)務(wù)價(jià)值以及未來(lái)業(yè)務(wù)的增長(zhǎng)點(diǎn)。如果在一開始 shard key 的選擇出現(xiàn)錯(cuò)誤,那么在接下來(lái)的應(yīng)用過(guò)程中想要改變 shard key 是一件極其繁瑣的過(guò)程??赡苣阈枰獋浞菽愕?collection,然后重新創(chuàng)建 shard 服務(wù)并恢復(fù)數(shù)據(jù),這個(gè)過(guò)程很可能需要運(yùn)行很長(zhǎng)一段時(shí)間。在互聯(lián)網(wǎng)應(yīng)用的今天,服務(wù)器的宕機(jī)事件都是以秒為單位計(jì)算,很可能錯(cuò)誤的 shard key 選擇會(huì)給你的應(yīng)用帶來(lái)災(zāi)難性的后果。希望此文能給各位一點(diǎn)啟示,在項(xiàng)目初期的設(shè)計(jì)階段充分考慮到各方面的因素。

免責(zé)聲明:本網(wǎng)站內(nèi)容主要來(lái)自原創(chuàng)、合作伙伴供稿和第三方自媒體作者投稿,凡在本網(wǎng)站出現(xiàn)的信息,均僅供參考。本網(wǎng)站將盡力確保所提供信息的準(zhǔn)確性及可靠性,但不保證有關(guān)資料的準(zhǔn)確性及可靠性,讀者在使用前請(qǐng)進(jìn)一步核實(shí),并對(duì)任何自主決定的行為負(fù)責(zé)。本網(wǎng)站對(duì)有關(guān)資料所引致的錯(cuò)誤、不確或遺漏,概不負(fù)任何法律責(zé)任。任何單位或個(gè)人認(rèn)為本網(wǎng)站中的網(wǎng)頁(yè)或鏈接內(nèi)容可能涉嫌侵犯其知識(shí)產(chǎn)權(quán)或存在不實(shí)內(nèi)容時(shí),應(yīng)及時(shí)向本網(wǎng)站提出書面權(quán)利通知或不實(shí)情況說(shuō)明,并提供身份證明、權(quán)屬證明及詳細(xì)侵權(quán)或不實(shí)情況證明。本網(wǎng)站在收到上述法律文件后,將會(huì)依法盡快聯(lián)系相關(guān)文章源頭核實(shí),溝通刪除相關(guān)內(nèi)容或斷開相關(guān)鏈接。

2017-11-09
MongoDB的水平擴(kuò)展,你做對(duì)了嗎?
作者:趙翼 分布式數(shù)據(jù)庫(kù)的前世今生 當(dāng)人們一開始使用數(shù)據(jù)庫(kù)系統(tǒng)的時(shí)候,所有數(shù)據(jù)都是跑在一臺(tái)服務(wù)器上,即所謂的單機(jī)數(shù)據(jù)庫(kù)服務(wù)器。在企業(yè)級(jí)應(yīng)用中,我們會(huì)搭建一臺(tái)應(yīng)用

長(zhǎng)按掃碼 閱讀全文