自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

再見 MongoDB,你好 PostgreSQL

數(shù)據(jù)庫 其他數(shù)據(jù)庫 PostgreSQL MongoDB
某個(gè)時(shí)刻,我們需要從MongoDB中刪除一百萬個(gè)文檔,以后再把這些數(shù)據(jù)重新插入到MongoDB里。在另一實(shí)例中我們注意到我們的應(yīng)用程序的性能降低和設(shè)法跟蹤到的 MongoDB 集群。這只是兩個(gè)例子,我們已經(jīng)有過許多這樣的情況。這個(gè)問題的核心是,這不只數(shù)據(jù)庫在運(yùn)行,而且無論我們何時(shí)察看它都沒有絕對的跡象表明是什么原因?qū)е碌膯栴}。

Olery 差不多成立于5年前。始于Ruby代理開發(fā)的單一產(chǎn)品(Olery Reputation),隨著時(shí)間的推移,我們開始致力于一系列不同的產(chǎn)品和應(yīng)用程序。當(dāng)今,我們的產(chǎn)品不僅有(Olery) Reputation,還有Olery Feedback, Hotel Review Data API,widgets ,在不久的將來它可以嵌入到網(wǎng)站和更多產(chǎn)品/服務(wù)中。

我們增加了很多應(yīng)用程序的數(shù)量。當(dāng)今,我們部署了超過25個(gè)不同的應(yīng)用程序(全為Ruby),它們中的一些是web應(yīng)用程序(Rails或者Sinatra),但大多數(shù)的是后臺運(yùn)行程序。

我們最引以為豪的是迄今為止我們所取得的成就,不過在這些成就的背后總閃現(xiàn)著一樣?xùn)|西,即基礎(chǔ)數(shù)據(jù)庫。從Olery成立之日起,我們就安裝了數(shù)據(jù)庫,它用 MySQL來存儲(用戶、合同等等)核心數(shù)據(jù),用MongoDB來存儲評論及其類似的數(shù)據(jù)(即哪些在數(shù)據(jù)丟失的情況下很容易恢復(fù)的數(shù)據(jù))。一開始,這樣的安裝運(yùn)行的非常好,然而,隨著公司的成長,我們開始遇到了各種各樣的問題,尤其是MongoDB的問題居多。其中一些問題是由于應(yīng)用與數(shù)據(jù)庫的交互方式而引起的,一些則是由數(shù)據(jù)庫本身而產(chǎn)生的。

例如,某個(gè)時(shí)刻,我們需要從MongoDB中刪除一百萬個(gè)文檔,以后再把這些數(shù)據(jù)重新插入到MongoDB里。這樣的處理方法使得整個(gè)數(shù)據(jù)庫幾乎要被鎖定數(shù)個(gè)小時(shí),自然服務(wù)性能就會降低。而且直到對數(shù)據(jù)庫執(zhí)行修復(fù)(即在MongoDB上執(zhí)行repairDatabase命令)后才會解鎖。而且完成修復(fù)還要花費(fèi)數(shù)個(gè)小時(shí),修復(fù)所花的小時(shí)數(shù)要根據(jù)數(shù)據(jù)庫的大小來確定。

在另一實(shí)例中我們注意到我們的應(yīng)用程序的性能降低和設(shè)法跟蹤到的 MongoDB 集群。然而,經(jīng)過進(jìn)一步檢查,我們無法找到問題的真正原因。無論我們怎么安裝,或使用什么工具敲了什么命令我們都找不到原因。直到我們更換了集群的初選,性能才恢復(fù)正常。

這只是兩個(gè)例子,我們已經(jīng)有過許多這樣的情況。這個(gè)問題的核心是,這不只數(shù)據(jù)庫在運(yùn)行,而且無論我們何時(shí)察看它都沒有絕對的跡象表明是什么原因?qū)е碌膯栴}。

無模式的問題

另外,我們面對的核心問題是mongoDB的重要特征之一:模式的缺乏。模式的缺乏可能聽起來是有趣的,并且在一些情況下是有好處的。然而,對于許多無模式存儲引擎的用法,其導(dǎo)致了一些模式之間的內(nèi)部問題。這些模式?jīng)]有通過你的存儲引擎定義而是通過你的應(yīng)用的行為及其可能的需要而定義的。

例如:你可能有一頁存儲你的應(yīng)用需要的字符串類型的title字段的集合。這兒這個(gè)模式是非常符合當(dāng)前情形的,即使它沒有被明確的定義。但如果這個(gè)數(shù)據(jù)結(jié)果改變超時(shí),尤其是如果原來的數(shù)據(jù)沒有被遷移到新的數(shù)據(jù)結(jié)構(gòu),這就成了問題(在一些無模式的存儲引擎上是相當(dāng)有問題的)。例如,你可能有下面這樣的 Ruby代碼:

  1. post_slug = post.title.downcase.gsub(/\W+/, '-'

這樣,針對每一個(gè)有“title”字段并返回一個(gè)String的文檔,它都能正常工作。然而,對于那些使用不同字段名字(例如:post_title)或者根本沒有標(biāo)題字段的文檔來說,它將不能正常工作。為了處理這種情況,你需要將代碼調(diào)整為下面內(nèi)容:

  1. if post.title 
  2. post_slug = post.title.downcase.gsub(/\W+/, '-'
  3. else 
  4. # ... 
  5. end 

另一種處理方法是,在你的模型中定義一個(gè)模式。例如 Mongoid,一個(gè)流行的針對Ruby的MongoDB ODM,就能讓你做到這一點(diǎn)。然而,當(dāng)使用這些工具定義一個(gè)模式時(shí),你可能會好奇為什么它們不在數(shù)據(jù)庫內(nèi)定義該模式。實(shí)際上,這樣做可以解決另一個(gè)問題:可重用性。如果你只有一個(gè)應(yīng)用程序,那么在代碼中定義模式并不是什么大問題。然而,如果你有許多應(yīng)用程序的話,這將很快會成為一個(gè)大麻煩。

無模式存儲引擎希望通過刪除對模式的限制的方式,讓你的工作變得更簡單。但現(xiàn)實(shí)的情況是,確保數(shù)據(jù)一致性的責(zé)任推到了用戶自己的身上。有時(shí)候無模式引擎可以工作,但我打賭,更多的時(shí)候是事與愿違。

#p#

好數(shù)據(jù)庫的需求

Olery有了更多的特殊需求后,迫使我尋求一款更好的數(shù)據(jù)庫來解決問題。對于系統(tǒng),特別是數(shù)據(jù)庫,我們非常注重以下幾點(diǎn):

  1. 一致性
  2. 數(shù)據(jù)和系統(tǒng)行為的可視化
  3. 正確性和明確性
  4. 可拓展

一致性是重要的在于它有助于幫助我們對系統(tǒng)設(shè)定明確的期望。如果數(shù)據(jù)總是按照同樣的方式存儲,那么系統(tǒng)可以很方便的使用這些數(shù)據(jù)。如果在數(shù)據(jù)庫層面要求表的莫一列必須存在,那么在應(yīng)用層面就不用檢查這列數(shù)據(jù)是否存在。數(shù)據(jù)庫即使實(shí)在高壓情況下,也必須保證每一次操作的完整性。沒有什么事情比單純的插入數(shù)據(jù),過了幾分鐘后卻找不到數(shù)據(jù)的事更讓人沮喪了。

可見性包含了兩點(diǎn):系統(tǒng)本身以及從中獲取數(shù)據(jù)的容易程度。如果一個(gè)系統(tǒng)出錯那么應(yīng)該易于調(diào)試。反過來,用戶應(yīng)很容易查到想要查詢的數(shù)據(jù)。

正確性是指系統(tǒng)的行為如我們所期望的那樣。如果某個(gè)字段定義為一個(gè)數(shù)值型,沒有人可以像其中插入文本。這方面MySQL是臭名昭著,一旦你這樣做你將得到偽結(jié)果。

可擴(kuò)展性不僅針對性能而言,而且也涉及金融方面和系統(tǒng)能夠多么好地應(yīng)對不斷變化的需求。一個(gè)系統(tǒng)在沒有大量資金成本或減緩系統(tǒng)所依賴的開發(fā)周期情況下,很難表現(xiàn)得非常好。

搬離MongoDB

上面的需求牢記于心后,我們就開始尋找一個(gè)取代MongoDB的數(shù)據(jù)庫。上面提到的特性通常是傳統(tǒng)RDBM特征的一組核心集,所以我們鎖定了兩個(gè)候選者:MySQL和PostgreSQL。

本來,MySQL是第一候選,因?yàn)槲覀兊囊恍╆P(guān)鍵數(shù)據(jù)已經(jīng)在使用它存儲。然而,MySQL也有一些問題。例如,當(dāng)將一個(gè)字段定義為int(11)時(shí),你卻可以輕松地向該字段插入文本數(shù)據(jù),因?yàn)镸ySQL會試圖對它進(jìn)行轉(zhuǎn)換。下面是一些例子:

  1. mysql> create table example ( `number` int(11) not null ); 
  2. Query OK, 0 rows affected (0.08 sec) 
  3. mysql> insert into example (number) values (10); 
  4. Query OK, 1 row affected (0.08 sec) 
  5. mysql> insert into example (number) values ('wat'); 
  6. Query OK, 1 row affected, 1 warning (0.10 sec) 
  7. mysql> insert into example (number) values ('what is this 10 nonsense'); 
  8. Query OK, 1 row affected, 1 warning (0.14 sec) 
  9. mysql> insert into example (number) values ('10 a'); 
  10. Query OK, 1 row affected, 1 warning (0.09 sec) 
  11. mysql> select * from example; 
  12. +--------+ 
  13. | number | 
  14. +--------+ 
  15. | 10 | 
  16. | 0 | 
  17. | 0 | 
  18. | 10 | 
  19. +--------+ 
  20. 4 rows in set (0.00 sec) 

值得注意的是,MySQL在這些情況下會發(fā)出警告。但是,僅僅是警告而已,它們通常(若非總是)會被忽略。

此外,MySQL的另一個(gè)問題是,任何表的修改操作(例如:添加一列)都會導(dǎo)致表被鎖,此時(shí)將無法進(jìn)行讀或?qū)懖僮鳌_@就意味著,使用這種表的任何操作都不得不等待修改完成之后才能進(jìn)行。對于包含有大量數(shù)據(jù)的表,這可能會花費(fèi)幾個(gè)小時(shí)才能完成,很可能會導(dǎo)致應(yīng)用程序宕機(jī)。這已經(jīng)導(dǎo)致一些公司(例如 SoundCloud)不得不自己開發(fā)工具(例如lhm)來解決該問題。

了解到上面的問題后,我們開始調(diào)查PostgreSQL。PostgreSQL可以解決很多MySQL不能解決的問題。例如,PostgreSQL中你不能將文本數(shù)據(jù)插入一個(gè)數(shù)字字段:

  1. olery_development=# create table example ( number int not null ); 
  2. CREATE TABLE 
  3. olery_development=# insert into example (number) values (10); 
  4. INSERT 0 1 
  5. olery_development=# insert into example (number) values ('wat'); 
  6. ERROR: invalid input syntax for integer: "wat" 
  7. LINE 1: insert into example (number) values ('wat'); 
  8. olery_development=# insert into example (number) values ('what is this 10 nonsense'); 
  9. ERROR: invalid input syntax for integer: "what is this 10 nonsense" 
  10. LINE 1: insert into example (number) values ('what is this 10 nonsen... 
  11. olery_development=# insert into example (number) values ('10 a'); 
  12. ERROR: invalid input syntax for integer: "10 a" 
  13. LINE 1: insert into example (number) values ('10 a'); 

PostgreSQL 還具有在許多方式中不需要每一個(gè)操作都上鎖就可以改寫表的能力。例如,添加一列沒有默認(rèn)值卻可以設(shè)置為null的列并能夠快速完成無需鎖定整個(gè)表。

還有其他各種有趣的功能,如在 PostgreSQL 可以:trigram 為基礎(chǔ)的索引和檢索,全文檢索,支持JSON查詢,支持查詢/存儲鍵-值對,支持發(fā)布/訂閱等更多。

最重要的是PostgreSQL在性能,可靠性,正確性和一致性之間能夠權(quán)衡。

#p#

遷移到PostgreSQL

最后,為了在所關(guān)心的各種項(xiàng)目之中達(dá)到平衡,我們決定使用PostgreSQL。但是,將整個(gè)平臺從MongoDB遷移到一個(gè)截然不同的數(shù)據(jù)庫并不是很容易的事。為了使轉(zhuǎn)移工作簡單化,我們將此過程分成了3個(gè)步驟:

  1. 搭建一個(gè)PostgreSQL數(shù)據(jù)庫,并遷移數(shù)據(jù)的一個(gè)小子集。
  2. 更新所有依賴于MongoDB的應(yīng)用程序,連同任何需要的重構(gòu),都用依賴于PostgreSQL的程序替代。
  3. 將產(chǎn)品數(shù)據(jù)遷移到新數(shù)據(jù)庫上,然后部署新平臺。

部分?jǐn)?shù)據(jù)遷移

在考慮把所有數(shù)據(jù)遷移到新數(shù)據(jù)庫之前,我們先遷移了一小部分?jǐn)?shù)據(jù)來做測試。如果僅僅是遷移一小部分?jǐn)?shù)據(jù),就有非常多的麻煩的話,那么數(shù)據(jù)庫遷移也就沒什么意義了。

盡管有現(xiàn)成的工具可以利用,但還是有些數(shù)據(jù)(比如,列重命名,數(shù)據(jù)類型不一致)要做轉(zhuǎn)換,對于這些數(shù)據(jù)我們自己開發(fā)了些工具。這些工具中,大部分都是Ruby寫的一次性腳步,用于刪除一些評論,整理數(shù)據(jù)編碼,修正主鍵發(fā)生序列等等。

在測試開始階段盡管有些數(shù)據(jù)上的問題,并沒有出現(xiàn)大的會阻礙遷移的問題。例如,有些用戶提交的數(shù)據(jù)沒有完全按格式編碼,導(dǎo)致這些數(shù)據(jù)被重新編碼之前,不能被導(dǎo)入到新數(shù)據(jù)庫。例外一個(gè)有意思的改變是,之前評論的數(shù)據(jù)存的是評論用的語言的名稱(如“荷蘭語”,“英語”等),現(xiàn)在改了存語言的編碼,因?yàn)槲覀冃碌恼Z義分析系統(tǒng)使用的是語言編碼,而不再是語言名稱。

更新應(yīng)用

目前為止,花費(fèi)時(shí)間最多的就是更新應(yīng)用,尤其是那些嚴(yán)重依賴MongoDB聚合框架的應(yīng)用。扔掉那少數(shù)幾個(gè)遺留的Rails應(yīng)用吧,光是測試就會花掉你幾個(gè)星期的時(shí)間。更新應(yīng)用的過程大致如下:

  1. 用PostgreSQL的相關(guān)代碼來替換掉MongoDB的驅(qū)動/設(shè)置模塊的代碼
  2. 運(yùn)行測試
  3. 修復(fù)Bugs
  4. 反復(fù)運(yùn)行測試,直到所有測試通過

對于非Rails應(yīng)用,我們推薦使用 Sequel,對于Rails應(yīng)用,我們現(xiàn)在還無法擺脫ActiveRecord(至少是現(xiàn)在)。Sequel是一個(gè)非常好的數(shù)據(jù)庫工具集,它支持絕大多數(shù)(如果不是全部)我們想使用的PostgreSQL特性。相較于ActiveRecord,它基于DSL的query要強(qiáng)大的多,盡管可能耗時(shí)會有點(diǎn)長。

舉個(gè)例子,假設(shè)你想計(jì)算有多少用戶使用某種語言,并計(jì)算每種語言所占的比例(相對于整個(gè)集合)。純粹的SQL查詢語句如下所示:

  1. SELECT locale,count(*) AS amount, 
  2. (count(*) / sum(count(*)) OVER ()) * 100.0 AS percentageFROM users 
  3. GROUP BY localeORDER BY percentage DESC; 

在我們的例子中,將會產(chǎn)生以下輸出(當(dāng)使用PostgreSQL命令行界面時(shí)):

  1. locale | amount | percentage 
  2. --------+--------+-------------------------- 
  3. en | 2779 | 85.193133047210300429000 
  4. nl | 386 | 11.833231146535867566000 
  5. it | 40 | 1.226241569589209074000 
  6. de | 25 | 0.766400980993255671000 
  7. ru | 17 | 0.521152667075413857000 
  8. 7 | 0.214592274678111588000 
  9. fr | 4 | 0.122624156958920907000 
  10. ja | 1 | 0.030656039239730227000 
  11. ar-AE | 1 | 0.030656039239730227000 
  12. eng | 1 | 0.030656039239730227000 
  13. zh-CN | 1 | 0.030656039239730227000 
  14. (11 rows) 

Sequel允許你使用純Ruby編寫上面的查詢,而不需要字符串分段(ActiveRecord經(jīng)常需要):

  1. star = Sequel.lit('*')User.select(:locale) 
  2. .select_append { count(star).as(:amount) } 
  3. .select_append { ((count(star) / sum(count(star)).over) * 100.0).as(:percentage) } 
  4. .group(:locale) 
  5. .order(Sequel.desc(:percentage)) 

如果你不喜歡使用“Sequel.lit(“*”)”,你也可以使用下面的語法:

  1. User.select(:locale) 
  2. .select_append { count(users.*).as(:amount) } 
  3. .select_append { ((count(users.*) / sum(count(users.*)).over) * 100.0).as(:percentage) } 
  4. .group(:locale) 
  5. .order(Sequel.desc(:percentage)) 

雖然這可能有些冗長,但是上面的兩種查詢都使得它們更易于重用,而無需進(jìn)行字符串連接。

未來可能也會將我們的Rails應(yīng)用程序遷移到Sequel,但是考慮到Rails與ActiveRecord耦合得如此緊密,所以我們還不完全確定這是否值得花費(fèi)時(shí)間和精力。

遷移生產(chǎn)數(shù)據(jù)

最終我們來到遷移生產(chǎn)數(shù)據(jù)的過程。一般有兩種方法來做這件事:

  1. 關(guān)掉整個(gè)平臺,直到所有數(shù)據(jù)都已遷移完成。
  2. 遷移數(shù)據(jù)的同時(shí)保持系統(tǒng)運(yùn)行

第一個(gè)選項(xiàng)具有一個(gè)明顯的缺點(diǎn):停機(jī)時(shí)間。第二個(gè)選項(xiàng)不需要停機(jī)但是很難處理。例如,在這個(gè)方案中,當(dāng)你遷移數(shù)據(jù)的同時(shí),你必須要考慮所有將要添加的數(shù)據(jù),否則你就會損失數(shù)據(jù)。

幸運(yùn)的是,Olery有一個(gè)獨(dú)特的方案就是我們的數(shù)據(jù)庫的絕大多數(shù)寫操作都是相當(dāng)定期的,經(jīng)常變化的數(shù)據(jù)(例如用戶通訊錄信息)只占總數(shù)據(jù)量的一小部分,相比起我們檢查數(shù)據(jù),遷移它們花費(fèi)的時(shí)間相當(dāng)?shù)男 ?/p>

該部分的基礎(chǔ)工作流是:

  1. 遷移諸如用戶、聯(lián)系人之類的關(guān)鍵數(shù)據(jù),基本上所有我們無論如何都無法賠償損失的數(shù)據(jù)
  2. 遷移不太重要的數(shù)據(jù)(如我們可以再抓取、再計(jì)算獲得的數(shù)據(jù)等)
  3. 測試正常運(yùn)行在一組獨(dú)立服務(wù)器的一切
  4. 切換產(chǎn)品環(huán)境到新的服務(wù)器

再遷移步驟1的數(shù)據(jù),確保在平均故障時(shí)間內(nèi)創(chuàng)建數(shù)據(jù)不會丟失。

第2步是目前最耗時(shí)的,大約需要24小時(shí)。相反的是,步驟1和5中提到的數(shù)據(jù)遷移只需要45分鐘。

#p#

結(jié)論

我們遷移完成并且直到非常滿意大概過去了一個(gè)月。到現(xiàn)在為止除了那些積極的影響,還曾在各種情況中讓應(yīng)用的性能大幅提高。舉例來說,我們的 酒店評論數(shù)據(jù)API(Hotel Review Data API)(在Sinatra運(yùn)行)相比遷移之前交互延遲變低了許多:

遷移是在1月21日開始的,高峰表示應(yīng)用性能的硬重啟(在處理期間導(dǎo)致交互時(shí)間輕微變慢)。在21日之后交互的平均時(shí)間大致是原來的一半。

 

在另外一種被我們稱作“評論持久化”(譯者注:即存儲評論)的過程中,我們發(fā)現(xiàn)了性能上巨大的提升。后臺程序目標(biāo)很簡單:保存評論數(shù)據(jù)(評論內(nèi)容,評論分?jǐn)?shù)等等)。當(dāng)我們最終完成了為遷移工作做的很多大的更改后,結(jié)果令人振奮:

 

抓取器也變的更快了:

抓取器性能提升沒有評論存儲的過程那樣大,因?yàn)樽ト∑髦挥脭?shù)據(jù)庫來查詢某個(gè)評論是否存在(一個(gè)相對很快的操作),所以這樣的結(jié)果并不很令人吃驚。

 

最后來到程序里用來調(diào)度抓取過程的進(jìn)程(簡單稱之為“調(diào)度器”):

 

因?yàn)檎{(diào)度器只是以固定頻度運(yùn)行,這個(gè)圖可能有點(diǎn)難以理解,但是不管怎樣,在遷移之后有一個(gè)很清晰的平均處理時(shí)間的下降。

最后,我們已經(jīng)對現(xiàn)在的結(jié)果非常的滿意,而且我們肯定不會懷念MongoDB了。它的性能非常好,它的處理方案使其它數(shù)據(jù)庫相比之下黯然失色,并且查詢數(shù)據(jù)的過程與MongoDB相比實(shí)在太令人滿意了(尤其是對于non開發(fā)者而言)。盡管我們?nèi)匀贿€有一個(gè)服務(wù)(Olery Feedback)仍舊使用MongoDB(盡管這運(yùn)行在一個(gè)獨(dú)立的,相對小的集群上),我們?nèi)匀淮蛩銓戆阉浦驳絇ostgreSQL上。

原文鏈接:http://www.oschina.net/translate/goodbye-mongodb-hello-postgresql

責(zé)任編輯:Ophira 來源: oschina
相關(guān)推薦

2021-01-13 11:13:46

ExcelPandas代碼

2012-08-01 09:50:09

HotmailOutlook微軟

2021-04-23 09:09:19

GraphQLREST查詢

2019-02-01 10:35:33

PythonGo語言編程語言

2021-07-27 05:56:53

CrocFTPSFTP

2020-09-27 11:15:37

可視化PandasPython

2009-03-30 08:44:22

微軟Windows 7操作系統(tǒng)

2018-01-02 08:40:19

云安全云遷移數(shù)據(jù)泄露

2023-11-28 17:24:45

2011-01-07 18:05:37

QQ騰訊移動互聯(lián)網(wǎng)

2021-06-02 22:25:26

2G5G運(yùn)營商

2014-11-27 14:26:46

蘋果iPhone停產(chǎn)

2011-09-23 14:24:58

惠普云計(jì)算李艾科

2020-10-29 10:44:59

斗魚騰訊虎牙

2021-01-21 07:16:03

RocketMQKafka中間件

2021-05-04 22:31:15

零信任網(wǎng)絡(luò)安全網(wǎng)絡(luò)攻擊

2020-02-17 15:17:57

釘釘

2019-07-30 07:10:11

容器Docker軟件

2023-10-17 08:17:38

Jenkins開發(fā)

2021-02-20 09:01:05

網(wǎng)游仙劍姚壯憲
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號