1. 程式人生 > >論騎車穿越美國與實現Postgres的相通之處

論騎車穿越美國與實現Postgres的相通之處

來源:http://www.postgres.cn/news/viewone/1/106

論騎車穿越美國與實現Postgres的相通之處


原作者:斯通布雷克  創作時間:2015-10-22 09:18:37+08
doudou586 釋出於2015-12-23 09:18:37   評論: 0   瀏覽: 19   頂: 3  踩: 3 

2015年10月22~24日,中國計算機學會在合肥召開了中國計算機大會(CNCC 2015),現發表圖靈獎得主斯通布雷克等8位專家的大會特邀報告,以饗讀者。

原文來源:《中國計算機學會通訊》2015年第12期《CNCC2015特別報道》作者:邁克爾·斯通布雷克(Michael Stonebraker) 

我今天並行交錯地講兩個故事。一個是我和我夫人在1988年夏天騎車穿越美國的旅行;另一個是從1984年到1995年,Postgres的商業版Illustra的設計、實現、商業化以及最後被收購的故事。

Postgres的設計

1984年,商業化的Ingres已經4歲了,它的實現程式碼比學院版的Ingres要好太多,繼續在學院版的Ingres上實現我們的一些想法和原型系統已經不現實了,因為它很難比得過商業版的Ingres。於是,我們做了一個痛苦的決定:拋棄所有的Ingres程式碼,重新設計並實現一個新的資料庫系統Postgres。那麼,Postgres應該是什麼樣的呢?

抽象資料型別

Ingres和System R是20世紀80年代初最主要的兩個關係資料庫原型系統,它們都完全聚焦於對商業資料的處理。然而當時在很多論文中都有這樣的結論:“我們在關係資料庫模型上實現了X,但是它的效果很差,於是我們在關係模型上加上了Y”。我很清楚,如果在關係模型上加上這些互不相關的擴充,將導致關係模型的消亡。我們需要更好的策略來迎合非商業資料處理市場。

那個時候,有個地理資訊研究小組想要使用Ingres系統,我們以這個為例。假設有一個帶有位置範圍資訊的僱員資訊資料庫,如表1所示。一個常見的地理資訊查詢是:“找到位置範圍與矩形(14, 17, 0, 16)相交的所有僱員”。那麼我們需要的SQL查詢語句是這樣的:
select name where xmin>14 and xmax<17 and ymin>0 and ymax<16 。

然而,這樣的查詢語句既複雜又很難優化。為了更好地優化這個查詢,我們需要新的資料型別。然而當時的商業資料庫只有字串、浮點數、整數等基本資料型別,我的想法是:這些基本資料型別並不能很好地支援地理資訊查詢,如果使用一個“location”的屬性來替代表示位置範圍的4個屬性(如表2所示),那麼,我們的SQL語句就可以簡化為:
select name where location overlaps MakeBox(14,17,0,16) 。

為了支援這個功能,我們需要使用者指定的函式(user defined function)、使用者指定的型別(user defined type)和使用者指定的操作(user defined operator)。這就是Postgres想要做的事情之一:支援抽象資料型別。雖然這個概念很直觀,但是困難總是隱藏在細節中。為了支援抽象資料型別,我們需要新的索引結構來索引新的資料型別,例如R-tree;我們需要告訴查詢優化引擎如何處理新的型別;我們需要對“非”操作進行優化等。

在騎行的第三天,我們到達了華盛頓州的溫思羅普(Winthrop)。我們從早上5點開始騎行爬山,掙扎了15英里後,終於向上騎行了5000英尺,然而這個時候開始下雪了,我們穿上了所有的衣服依然感覺很冷。雖然如此,我們最終還是翻越了這座山,儘管後面還有很多很多的高山,但我依然非常樂觀和高興,因為翻過當前這座高山,證明我們有能力翻過其他高山。

用“always”關鍵字實現約束規則

1981年,克里斯·戴特(Chris Date)提出了完整性約束。完整性約束主要是處理主鍵約束和外來鍵約束的。例如,表3所示的僱員表和部門表,其中僱員的部門屬性是部門表的外來鍵。如果我們使用SQL語句刪除掉Candy部門,那麼會發生什麼呢?在Art的部門值上將會有個懸空的外來鍵指標,順著這個外來鍵指標走將會產生錯誤。為了避免這種情況,戴特提出了三個合理的解決方案。第一個是級聯刪除,它將刪除所有屬於Candy部門的僱員,從而保證資料庫的完整性;第二個是把指向Candy部門的外來鍵替換為“null”,這會保證資料庫的一致性;第三個是拒絕刪除操作,因為這會破壞資料庫的一致性。同樣,在插入操作中也有對應的三種方案。戴特提出的解決方案在當時被商業資料庫廣泛採納。

在我看來,首先,任何一個新的資料庫系統都必須實現外來鍵約束,當然也包括Postgres。其次,在當時,觸發器(trigger)引起了很多人的關注。如果使用者想確保Sam和Bill的工資是相同的,那麼對Sam工資的更新也必須級聯到Bill上,這就需要一個觸發器。觸發器雖然和引用約束不同,但它也只是更新操作的一個附屬操作——在更新操作之後需要完成的。第三是主鍵約束,例如確保工資不會出現負數,這也是更新操作的一個附屬操作。仔細想想就會發現,這僅僅是三個瑣碎的規則系統。我的想法是實現一個統一的規則系統,它能處理所有的(至少上述三個)規則。

思考了一年左右,我提出的解決方案是在命令中增加一個“always”關鍵字來處理這些規則。在更新操作中附上“always”關鍵字將會保證後面的條件一直成立。例如,為了讓Sam和Bill的工資保持一致,我們使用如下命令:
Update ALWAYS (set salary = E.salary) Where E.name = ‘Sam’ and name = ‘Bill’ 。

這是一個比If-Else規則系統更加清楚簡單的系統。

在第15天的時候,我們抵達了蒙大拿州的邊境。我們一路下坡,之前的高山和石子路都被我們甩在了背後,一條一直通往芝加哥的平坦馬路在我們面前延伸。

無重寫儲存

Postgres的第三個計劃是無重寫儲存。20世紀70年代,大家都認識到了處理宕機恢復的重要性。為了確保使用者絕對不會丟失他們的資料,我們必須非常認真地對待宕機恢復的問題。在當時,所有人採取的方式都是分開儲存資料和日誌。每次做更新操作的時候,需要把資料更新前和更新後的樣子都寫入到日誌裡。所以有兩個資料儲存地點,其中一個用來管理資料,另外一個用來管理日誌,如圖2所示。我們必須非常小心地維護這些資料和日誌以使它們保持同步。

當時我就意識到,處理日誌的程式碼十分複雜而且醜陋,同時還要確保它能正確工作。否則,如果一個非常重要的客戶的資料庫系統宕機了,而你的日誌並不能恢復他們丟失的資料,那你就徹底完蛋了。如果這位客戶足夠重要,你將會出現在《紐約時報》的頭版上。我們必須有更好的方法來解決宕機問題。因此,我們提出統一儲存日誌和資料(如圖3所示),並且在更新操作的時候,不覆蓋當前的資料,我們做的僅僅是插入一條更新後的資料,並加上時間戳,這樣就可以繞過宕機恢復,而且還能順便實現“時間旅行”的功能。這看起來非常有趣,但是困難總是隱藏在細節當中。傳統的資料庫對日誌的寫操作和資料的讀操作分別進行了優化,然而在我們的設計中,日誌和資料混在一塊,這些優化並不能直接套用,如何提高我們的資料庫的效能是一個很大的挑戰。

以上就是Postgres的設計藍圖。抽象資料型別,用“always”關鍵字來處理規則以及無重寫儲存。當然我們還有其他計劃,例如,複雜物件和繼承等,但是前面三個是最重要的。

Postgres的實現

騎行到第26天的時候,我們到達了蒙大拿州東側的北達科他州(North Dakota),這裡是一望無際的平原,一棵樹也看不到。美國中西部夏天的天氣很奇怪,呼嘯的狂風從東往西一直不停地吹。我們原本打算以16~18英里的時速輕鬆騎行,但事實上我們努力掙扎也只能是每小時7~8英里。我們當天只前進了50英里,比計劃少了25英里。距離丘陵地帶的明尼蘇達州的邊界線還有250英里,我們感到十分絕望,不知道如何才能走完這段路程。

1986年,C++語言還不成熟,所以最開始我們採用了Lisp語言來實現Postgres。可是事實證明,這是個災難性的決定,因為它的效能比其他語言要低1個數量級。所以我們拋棄了非常多的程式碼,把可以轉化的程式碼轉化了,並重寫了其他不能轉化的程式碼。採用新語言這個實驗造成了災難性後果。

抽象資料型別工作得很好。我是從Ingres的一個使用者那裡明白這一點的。當時我還是Ingres的首席技術官,我接到一個客戶的電話,他告訴我,我們在Ingres上實現的日期和時間的型別有錯誤。我覺得這不可能,我們是按照專家的指導實現的,即使不同的月份有不同的天數,但我們依然能夠正確地處理日期型別。其實這位客戶想要實現一個應用系統來計算金融債券利息,然而天知道為什麼,華爾街的那幫傢伙認為所有月份都是30天,每個月的利息都是一樣的,不管這個月實際有多少天。例如,在他們看來3月15日距離2月15日是30天。這位客戶想要的僅僅是跳過我們系統設定的日期減法,使用他們自己實現的日期減法。這對Postgres的抽象資料型別來說簡直是小菜一碟,然而當時所有的商業資料庫系統都不支援這個特性。在這之後,日期還有很多不同的理解方式,例如交易日和工作日等。

“時間旅行”很有趣,但是我們不知道如何才能提高它的效能,這一直困擾著我們。同時,“always”關鍵字並不能很好地工作。我們不知道如何用它來支援日期提出的全部6種約束,我們又拋棄了大量的程式碼,重新實現了傳統的規則系統。

1987年到1990年的三年時間裡,我們的進展非常緩慢。這一段時間我們就像是在泥潭和沼澤裡艱難地爬行。我們做了很多修修補補的工作,例如,我們一直想弄清楚複雜物件到底能有多複雜以及如何處理規則系統。這個階段過得很艱難,在Postgres的實現上和我們的騎行上都是。

Postgres的商業化

然而這個時候,奇蹟突然出現了。這個奇蹟和狂風無關,狂風依舊呼嘯著。這個奇蹟是我的兄弟出現了,他決定來陪我們騎行一個星期。他非常強壯,狂風並不能擊倒他。他幫助我們破風,就像在自行車競賽中看到的一樣,我們跟在我兄弟的自行車後輪後面大概3英寸的地方騎行。我們終於看到了離開北達科他州的希望。

1991年,Ingres公司對我的競業限制正好過期了,我們決定成立一個商業公司來對Postgres進行商業化。這樣做的原因是,在我看來,市場是檢驗一個好想法的最終裁決者。如果你想實現科技轉化,那麼你有兩種選擇。第一種是讓一個大公司採用你的科技,當時我們接觸了Ingres 公司,但是大公司有他們自己的日程安排。根據哈佛商學院的一位教授所寫的書《創新者的窘境》(The Innovators Dilemma),如果一個銷售舊科技的公司要採用新的技術,他們很難避免原有客戶的流失,所以對大公司來說,他們多少有些難以接受我的新技術。第二種科技轉化的最好方式是建立一個初創企業,這就是我們所做的。我們僱用了一批非常優秀的工程師,他們中有一部分是從學術界過來的。我的朋友出任了臨時執行長,我們通過融資獲得了一筆資金。我們當時的計劃是把查詢語言從QUEL轉化為當時的標準查詢語言SQL,優化程式碼以及提升效能。就像所有的初創企業一樣,我們當時非常興奮。

在第38天的時候,我們穿越了威斯康星州,抵達了密歇根州的拉丁頓(Ludington)。我們距離波士頓只有不到1000英里了,上中西部已經在背後,我們看到了完成這趟探險旅程的希望。

1993年,在幾次錯誤的嘗試之後,我們最終把公司命名為Illustra。和所有商業化公司一樣,我們需要做的第一件事情就是吸引使用者。

我們成功地吸引了幾個初始使用者並融到了更多資金。最重要的是,我們僱用了一個真正的管理團隊,包括執行長、主管市場的副總裁以及主管銷售的副總裁。我們的公司走上了正軌。

但是好景不長。在騎行第49天的時候,我們到達了位於紐約州和賓夕法尼亞州交界處的埃利科特維爾(Ellicottville)。那天很不幸,我們出發時在酒店的大理石地面上騎車滑倒,我的膝蓋磕破了,我倆只剩下3條好腿來完成剩下的旅途。但是最糟糕的是我們遇到了一座高山。從威斯康星到密歇根再到俄亥俄州一路平坦,我們的騎行非常輕鬆,但是這一切隨著這座高山的出現戛然而止。雖然這座山只有500英尺高,但是一路都是起起伏伏,我們感到筋疲力盡。

1994年,我們遇到了Illustra的第一個嚴重危機。我們的很多使用者想要使用大型應用商提供的一些抽象資料型別的庫,於是我們和那些大型應用商談判,看是否能把他們的第三方庫融合進我們的資料庫。然而他們並不關心這些,他們問我,你們現在有多少使用者?你們能幫我們賣出去多少許可證?原來他們想要的僅僅是一個銷售渠道,而不是根本的技術變革。這樣一來,我們陷入了一個死迴圈:我們希望抓住客戶,客戶想用大型應用商的庫,而大型應用商又要求我們給他帶來更多客戶。所以當時我們的產品銷量並不好,而這又直接導致了另外一個危機。

一個初創企業從零開始融資,每一輪都有個估值,風險投資商會以一定的價格獲得這個企業的一些股票。當初創企業的錢用光後需要再進行一輪融資,這時企業的估值通常會更高,股票的價格也會更高。但是如果新的一輪融資的估值變低了,這輪融資就叫做貶值融資。我們就經歷了一輪貶值融資,這是比企業破產更糟糕的事情。為什麼呢?因為融資合同裡通常會有這麼一個條款,如果企業遇到了貶值融資,那麼之前所有的融資合同裡股票的價格都要按照這輪貶值融資的股票價格重新計算。而對於員工來說,他們手上的股票遭到了雙重稀釋,他們可能會選擇跳槽。這是件令人非常尷尬的事情,我絕不想再經歷一次。我需要和公司所有的股東進行談判,這花了我3個月的時間,直接導致了我們3個月毫無進展。在這之後,我們終於獲得了一筆錢,使得Illustra能夠繼續存活一段時間。

Postgres被收購

在第56天的時候,我們抵達了馬薩諸塞州的邊境,我們繞過了那座連綿起伏的大山,橫在我們面前的是抵達目的地前的最後一座山,再往後的路都十分好走。

1995年,奇蹟突然出現。網際網路蓬勃發展,網路公司受到大家的熱烈追捧。此時,我們的產品不再僅僅是一個擁有擴充套件資料型別的資料庫,而是一個真正意義上的網際網路資料庫,可以儲存網際網路上所有型別的東西,例如文字、影象和音訊。當時一個網際網路巨擎考慮的是把我們的產品融入到他們的系統中,這會促成一筆交易。他們首先需要測試我們的產品,很遺憾他們並沒有測試我們的產品檢索文字或者地理資訊的效能,他們要在一個測試事務處理效能的標準資料集上測試我們的產品,因為他們認為網際網路上的每一個操作都對應一個事務,所以我們的產品需要高效的事務處理能力。然而事務處理並不是我們的系統所擅長的,如果要大幅度提升事務處理的效能,我們需要重寫大部分的程式碼。

在第59天的時候,我們抵達了波士頓,我們把雙人車騎行到大西洋邊的海灘來結束我們的旅程。

1996年2月,奇蹟突然出現了。我們的一個競爭對手同時也是一家大公司,提出要收購Illustra。這直接解決了我們前面的兩個問題,首先解決了我們的一些客戶需要大型應用商的第三方庫的問題,因為大型應用商很樂意和這家使用者量巨大的大公司合作。其次,這家大公司有非常高效的事務處理引擎,這解決了我們的產品事務處理的效能問題。被收購之後,我們成功地把 Illustra 的很多特性移植到了這家大公司的系統中。

結論

我為什麼要講關於自行車騎行的故事呢?首先,我要證明我是會寫程式碼的。這是騎行穿越美國的演算法虛擬碼:

直到(大西洋) {
  起床;
  向東騎行;
  克服遇到的任何困難;
}
我們稍微泛化一下這個虛擬碼:
直到(目標完成) {
  起床;
  行動;
  克服遇到的任何困難;
}

我們把這段程式碼定義為一個巨集:Make It Happen(動手實現)。

第二個要說明的是,為什麼一個正常的人想要騎車穿越美國?更別說我們兩個是擁有高等計算機學位的人。畢竟騎車穿越美國是件單調無聊,時常令人沮喪,偶爾讓人興奮,遠距離且困難的事情。如果你看看我的簡歷,你就能找到答案。我花了5年的時間獲得博士學位,這是一個“動手實現”的過程,因為要寫一篇能讓導師簽字通過的畢業論文並不容易,我甚至在博士資格考試中失敗過一次。之後我在加州大學伯克利分校做助理教授,花了5年時間獲得終身教職,也是“動手實現”的過程。另外,花兩個月的時間騎車穿越美國也是“動手實現”的過程。

第三是因為騎行故事是設計實現大型軟體系統的一個隱喻。要設計實現Postgres系統,首先你需要有幾個好主意,然後花費5年時間實現一個原型系統,這是一個“動手實現”的過程。在這期間別忘了,什麼時候扔掉所有程式碼重新來過都不晚。在這之後你就可以創立一家公司,僱用一些絕頂聰明的人,完成你的產品,這也是一個“動手實現”的過程。這期間就像在一個長長的沼澤裡爬行一樣。簡而言之,設計實現Postgres只需要一個好主意並“動手實現”。可是這花費了我10年的時間,期間起起落落,甚至比獲得終身教職還難。有人可能會問既然如此之難,那你為什麼還要設計實現Postgres呢?這和騎行穿越美國是一樣的,那是我要做的(That’s what I do)。

騎車穿越美國與設計和實現Postgres有什麼相通之處呢?第一,動手實現;第二,有突然出現的奇蹟。

(本文根據CNCC 2015特邀報告整理而成)

作 者:

邁克爾·斯通布雷克 (Michael Stonebraker):美國工程院院士,2014 ACM圖靈獎獲得者。美國麻省理工學院教授。主要研究方向為資料庫研究和開發。

整 理:

鄧 棟,清華大學博士生。主要研究方向為資料質量以及資料融合。

李國良,CCF高階會員、本刊編委,CCF青年科學家獎獲得者。清華大學副教授。主要研究方向為大資料管理、資料清洗與融合、移動資料管理等。