《今天你買到票了嗎?——從鐵道部12306.cn網站漫談電子商務網站的“海量事務高速處理”系統》

 

今天你買到票了嗎?

12306.cn是中國鐵路客戶服務中心網站。作爲鐵道部唯一授權的火車票訂票網站,在2012年的春運前夕幾乎成爲了一個家喻戶曉的網站。因爲從今年起,每個人都可以通過這個網站以實名制的方式預訂火車票,和家人團聚,共度一個祥和幸福美滿團圓的春節。與此同時,12306.cn網站也被輿論推到了風口浪尖,這並不是因爲12306.cn網站滿足了每一位用戶回家的願望而獲得了讚譽。而是在互聯網已經開始進入每個人日常生活的時代,12306.cn網站的界面醜陋、UI(用戶界面)粗糙,不誇張的說,比十年前的網站還遜色。當然,作爲一個功能性網站,用戶也不會苛求界面的美觀,UI的精緻,但是讓每一位用戶都不可接受的是,12306.cn網站的服務器不穩定,屢屢癱瘓。筆者很榮幸應CSDN之邀撰文對本次事件做一些分析。

作爲政府部門唯一授權的網站,用戶沒有用腳投票選擇離開的權力。剛剛過去的2011年是中國電子商務網站遍地開花的一年,中國的每家電子商務網站都在比拼燒錢打廣告,期望獲得更高的全球Alexa排名,期望獲得更高的訂單數量、銷售數量、用戶轉化率。按着筆者瞭解的情況,在2011年,電子商務網站每IP的廣告費用平均約爲0.4元,轉化爲註冊用戶時的每註冊用戶的廣告費用平均約爲4元,轉化爲實際銷售的每個首次銷售訂單的廣告費用平均約爲40元,也就是說一位通過電子商務網站廣告而訪問電子商務網站產生的首次購買,如果利潤低於40元的話,那麼就是在賠錢。

如果從電子商務的角度來看12306.cn網站的話,情況正好相反,沒做一分錢廣告,僅僅幾天的時間Alexa的排名飆升至260,這不是中國網站的排名,是全球網站的排名,而且還有不斷上升的趨勢,日訂單量很可能成爲全國第一,銷售額都是可預知的,因爲每列火車中每節車廂的每個座位都會賣出去,轉化率達到了前無古人後無來者的100%,因爲所有的註冊用戶,都是想買車票的,只能是他買不到,不可能是他不買。媒體普遍戲稱12306.cn網站是中國、仍至全球有史以來最牛的電子商務網站。

作爲12306.cn主管部門——鐵道部,從建國之初就有一個衆所周知的外號“鐵老大”。但是作爲12306.cn網站的製作單位——中國鐵道科學研究院(簡稱鐵科研),從一家僅爲業內人所熟知的科研單位,僅僅幾天的時間就受到了廣泛的關注,因爲每一位在訂票時屢屢遇到服務器不穩定、甚至癱瘓的用戶,特別是前一秒中還看到有票,因爲進不去訂票界面,後一秒鐘就看到無票的用戶,都會對這個網站的製作者產生好奇,究竟是誰做出這麼一個網站?瀏覽到12306.cn網站頁面的頁腳就會看到“中國鐵道科學研究院”的名字。接下來筆者將分析研發一下類似於12306.cn網站將會遇到哪些問題,以及如何通過現有的技術手段解決這些問題,並探討在一般的電子商務網站中如何處理同類問題。

在線處理的四種類型

互聯網站除了瀏覽器端的展示外,在服務器端的程序歸屬於“在線處理”的範疇。一般來說“在線處理”可以分成“在線事務處理”(OLTP,OnLine Transaction Process)和“在線分析處理”(OLAP,OnLine Analytics Process)兩類處理方式。事務(Transaction)這個概念來源於數據庫,在數據庫中事務(Transaction)是訪問並可能更新數據庫中各種數據項的一個程序執行單元(unit)。

隨着技術的發展,事務已經不僅僅侷限在數據庫領域。現階段,一般用兩種角度來描述一個事務,一個是從外部的角度,事務是恢復和併發控制的基本單位。另一利是內部的角度:事務,是由一系列不可分割並且不可錯序的動作組成。雖然理解事務的角度隨着技術的發展不斷演講,但是用於描述並評估事務的屬性保持不變,一個事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱爲ACID特性。

  • 原子性(Atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。
  • 一致性(Consistency)。事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
  • 隔離性(Isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。
  • 持久性(Durability)。持續性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

以12306.cn爲例,爲避免“黃牛”,購票系統有一個業務邏輯:一個有效身份證件同一乘車日期同一車次限購一張車票。因此購買一張車票可以簡化爲包含四個操作:

  1. 判斷同一乘車日期同一車次是否有未預訂的空餘座位
  2. 判斷這個有效身份證是否已購買過同一乘車日期同一車次的車票
  3. 車票上標註的座位標記爲已預訂
  4. 如果沒有購買過,則該身份證預訂一張車票

原子性是指用戶提交一次購買的時候,要麼該車票上標註的座位標記爲已預訂並且該身份證預訂一張車票;要麼沒有標記該車票上標註的座位已預訂並且該身份證預訂失敗。也就是第3個操作和第4個操作要麼都做,要麼都不做。不允許沒有標記該車票上標註的座位已預訂,但是該身份證預訂了一張標註爲這個座位的車票,或者有某一個座位被標記爲預訂但是該身份證預訂失敗。一致性也是指類似的含義。

隔離性是指如果有兩個用戶同時預訂同一乘車日期同一車次的車票時,假設只剩一張票了,那麼第一個用戶判斷出有未預訂的空餘座位,那麼他就可以成功預訂車票。因此既便是第二個用戶在第一個用戶標記該座位已預訂之前判斷是否有未預訂的空餘座位,返回給他的結果也是沒有空餘座位。

持久性比較好理解,如果一個用戶已成功預訂了一個座位,那麼其他用戶就不能預訂同一乘車日期同一車次的同一個座位了。

對於一般的電子商務網站來說,一次購買行爲可以簡化爲四個操作:

  1. 判斷該商品有庫存
  2. 判斷該用戶的賬戶餘額
  3. 減少該用戶的賬戶餘額
  4. 減少該商品的庫存

這樣的四個操作組成了一次購買行爲,也需要滿足ACID屬性,當然實際的場景要複雜得多。

一般來說,不具備ACID屬性的在線處理都可以稱之爲OLAP,這不意味着OLAP比OLTP簡單。通常所說的OLAP是指對一定數量的數據進行統計分析的操作,需到強大的計算資源支持。OLTP和OLAP是評估“在線處理”的一個角度,另一個角度就是對於處理速度,也就是響應速度的要求,速度一般和數據量緊密關聯,相對於海量高速處理而言,海量低速處理,或者少量高速處理就容易得多。通常把海量低速處理或者少量高速處理統稱爲低速處理。

這樣對於在線處理的兩個角度就構成了四種情況:海量事務高速處理,海量分析高速處理,事務低速處理,分析低速處理四種情況。

 

OLTP

OLAP

海量高速

海量事務高速處理

海量分析高速處理

低速

事務低速處理

分析低速處理

“海量事務高速處理”與其他在線處理的區別與聯繫

在這四種情況中,先講一下分析低速處理。雖然分析低速處理看起來不要求速度,也不要求事務,但是在在線處理中被納入到分析低速處理領域的問題一般是指複雜算法方面的處理,常見的有聚類分析,在本文中不再展開討論。下面分別針對海量高速中的OLTP和OLAP的區別展開探討,以及OLTP中的海量高速和低速展開探討。

對於海量高速而言,因爲涉及到相當規模的運算量,遠遠超過單臺計算機的處理能力,需要多臺機算機組成一個系統,對於外界來說就像是一個系統,這樣的方式被稱爲集羣計算。集羣計算除了用於解決運算量之外,同時也用於提高系統的可用性,當集羣中的一臺計算機出現故障時,另一臺計算機可以接替它的計算任務,從外界來看,集羣相對單臺計算機而言,可用性提高了。

在OLAP中,因爲每個在線處理過程之間都是相對獨立的,不具有OLTP的ACID屬性,因此使用集羣計算處理分析任務時,一般的過程是把任務分發到多臺計算機上,所有計算機返回計算結果後,再彙總結果返回給用戶,這種方式一般被稱爲高速並行計算。從字面意義上看,所有的計算任務都是並行處理的,之間沒有太多的依賴關係。對於高速並行計算而言,只要有足夠的傳輸帶寬、服務器夠快、夠貴、夠多就可以解決所需要的計算量的要求。

這也是目前“海量事務高速處理”混同於“海量分析高速處理”時常見的一種誤解,認爲使用“高速並行計算”解決“海量分析高速處理”的方式可以用於解決“海理事務高速處理”,比如說使用單臺服務器一秒鐘可以處理一個交易,那麼使用一萬臺服務器就可以一秒種處理一萬個交易。或者說是只要有足夠的傳輸帶寬、服務器夠快、夠貴、夠多就可以解決“海量事務高速處理“的問題,這也是本文要澄清的關鍵問題之一。這也是相當一部分電子商務網站從初期的低速處理時期向“海量高速”發展時遇到的最大技術瓶頸。

當然這並不意味着“海量分析高速處理”比“海量事務高速處理”容易,這是兩個不同的方向,一個典型的例子就是搜索引擎,大型搜索引擎中的一次搜索行爲並不會涉及ACID等等屬性,但是仍需要將一次搜索任務分發給數百臺或者更多的服務器,然後再合併這些服務器的搜索結果後返回給用戶。在這方面典型的技術就是Google的MapReduce編程模型,目前有Hadoop這個開源項目實現了Google的MapReduce模型。

“海量事務高速處理”從量變到質變

由於事務具有ACID等屬性,因此“海量事務高速處理”和“海量分析高速處理”需要完全不同的技術路線,在國內,12306.cn並不是第一個遭遇這個問題的網站,2008年奧運會的訂票系統也遭遇過同樣的窘境,只是因爲用戶量相對少些,和大多數人的關係不密切而沒有得到廣泛的關注。當然如果數據量沒有那麼多,速度要求也沒有那麼高的話,也就是在“事務低速處理”領域中,目前有着豐富的解決方案,目前主流的數據庫,特別是商業數據庫都重點關注這一領域。

特別是從具體的需求來看,“事務低速處理”和“海量事務高速處理”只是“量”上的區別,作爲需求的“事務”本身並沒有任何變化,因此一個簡單的思維就是開發一個“事務低速處理”的網站,然後用“海量分析高速處理”的高速並行計算解決方案來解決“量”問題,但是結果並不如意,在辯證法中的三大定律之一——量變引起質變,也同樣適用於“海理事務高速處理”。近些年來,已經有很多先行者嘗試過努力,也積累了一定的經驗和教訓。

在通用領域中,既便是作爲領頭羊的IBM、HP等公司碰到這種系統,都是隻賣硬件,絕不爲軟件系統承擔任何風險和責任。說明白了,我掙硬件的錢,軟件,你們誰不怕死的話誰就向前衝吧。多年來在通用領域毫無建樹不意味着“海量事務高速處理”是不可解決的難題,在專用領域“海量事務高速處理”的發展就非常引人注目,其中最典型的案例就是IBM爲大型金融機構提供的IT解決方案。分析這個案例有助於撥開“海量事務高速處理”的重重迷霧。

在IBM的解決方案中,其核心就是S390大型機,筆者雖然沒有接觸過S390大型機,但是曾經在十年前學習過AIX小型機,並取得了中級管理員證書。在這裏不展開講解具體的小型機、大型機技術,總體而言,這些設備所表現出來的卓越運算能力,都是侷限在某個特定的領域。也就是說,IBM爲金融領域的特定場景“定製”了一套系統,這套系統在硬件、操作系統、數據庫、中間件、應用程序整個流程的每個環節都做了優化,消除了所有的瓶頸。

換句話說,雖然在這套系統中仍然可以區分出硬件、操作系統、數據庫、中間件、應用程序。但已經和通用領域中的含義不同了。比如說S390上運行的數據庫是DB2,事務中間件是CICS,在普通PC上也有着對應的DB2和CICS產品,但並不是簡單的移植,在S390上的這些軟件通過和硬件、操作系統緊密結合從而表現出卓越的性能,換句話說,在普通PC上的DB2和CICS等產品僅僅是提供給開發者便於學習和開發而已,脫離了IBM的專用硬件,這些軟件的表現明顯欠佳。

通過S390的例子可以看出,在專用領域中,“海量事務高速處理”需要按實際的需求“定製”,目前以及未來相當長的一段時期間不會出現通用的“海量事務高速處理”的解決方案,更不會出現相應的通用軟件。當然一個現實的問題是,在很多領域中,並不會都像金融業那麼有錢來支撐硬件的研發,比如說電子商務領域,既不能像金融業那樣來支撐硬件的研發,同時業務量又相當於一些小規模的銀行。此時就需要考慮如何在通用硬件上,藉助於“定製”操作系統、數據庫、中間件、應用程序來解決問題。

淺淡事務的隔離級別與鎖

前面談到了解決“海量事務高速處理”需要定製,那麼接下來就需要考慮這個定製過程的核心問題是什麼?要“海量高速”,就要能並行處理,而且是多臺計算機並行處理,此時與事務的ACID屬性關係最密切的就是隔離性。也就是說當多事務同時進行時,兩個事務之間的影響情況,一般來說隔離性需要處理三個問題:髒讀、不可重複讀、幻讀。

  • 髒讀:一事務對數據進行了增刪改,但未提交,有可能回滾,另一事務卻讀取了未提交的數據
  • 不可重複讀:一事務對數據進行了更新或刪除操作,另一事務兩次查詢的數據不一致
  • 幻讀:一事務對數據進行了新增操作,另一事務兩次查詢的數據不一致

以預訂車票爲例,髒讀就是當一個預訂車票的操作中,標記了某個座位已預定,但是沒有爲該身份證預訂一張車票時,其他預訂車票的操作讀取該座位的狀態時,不會立刻獲得該座位已預定狀態,而是當第一個預訂車票的操作完成後,獲得該座位已預定狀態,或者當第一個預訂車票的操作回滾後,獲得該座位未預定的狀態。讀者可以參照不可重複讀的解釋構照相應的場景。

一般來說事務都需要禁止髒讀和不可重複讀。而對於幻讀就需要依賴於實際的需求展開分析,以預訂車票爲例,當你查詢餘額的時候,看到車票有餘額,但是進入預訂的時候,發現沒有餘額,如果把你的整個操作視同一個事務的話,這就是幻讀,當然一般對於用戶來講是不禁止幻讀的,但是在交易系統內部則常常要禁止幻讀。

在現代的數據庫系統中,一般可以爲事務直接設置隔離級別,以期避免相應的問題。最高的隔離級別就是串行化,也就是相當於“一個一個執行”。在單機測試中,串行化的性能低至禁止不可重複讀但允許幻讀時的30分之一,這也就是禁止幻讀的代價。對於多臺計算機而言“一個一個執行”相當於單機的效果,沒能體現多臺計算機的計算能力,是不可接受的,此時就需要採取其他的辦法。目前主要有兩個策略來解決這個問題。

以預訂車票爲例,串行化就是指所有的預訂車票操作一個一個處理,看上去這個方式很笨拙,其實在現實生活中的排隊就是典型的串行化。在現實生活中,經常會有通過預分配資源的方式,來創建多個排隊來提升速度,例如在火車票還是一個小硬紙片的時代,火車站的售票大廳中每個窗口銷售不同車次的火車票,這就是預分配資源,買票需要先按照車次選擇窗口,然後排隊購買。有過經歷的讀者馬上就會發現,雖然預分配資源可以通過排多個隊來提升效率,但是如果預分配的方案不合理,則會造成一個隊排得很長,而另一個隊沒有人的情況。進一步的,預分配資源的方案無法避免一個人排了一個隊之後,再排一遍,或者排另一個隊。

一種是通過建立“快照”的方式,在需要避免“幻讀”時,就創建一個快照,此後這個事務中的所有讀取的操作都是針對於這個快照進行操作,因此就不會有“幻讀”發生,當然在數據庫中實現快照的方式並不是真的複製一遍數據庫,而是通過記錄數據的時間戳的方式來實現,對於單機來說,需要額外的存儲和計算來記錄並處理時間戳,對於多機來說,保持時間戳的同步也需要相當的代價,因此一般僅用於特定的場合。

通行的設置隔離級別的方式是加鎖,鎖的情況有很多種,而且不同的數據庫的產品中的實現方式也不盡相同,在此不展開講解,一般分成三種情況,數據庫按照語句加鎖,帶有加鎖命令的語句,在聲明事務的同時聲明隔離級別。需要注意是鎖會導致死鎖。此時應用中兩個或多個事務發生永久阻塞(等待),每個事務都在等待其他事務佔用並阻塞了的資源。例如,如果事務A鎖住了數據1並等待數據2,而事務B鎖住了數據2並等待數據1,這樣兩個數據就發生了死鎖現象。

以預訂車票爲例,當查詢該身份證是否已預訂過相同乘車日期相同車次的車票時,就相當於給這個身份證加上了鎖,直到預訂車票的操作成功或者失敗才解鎖,在此期間其他查詢該身份證是否已預訂過相同乘車日期相同車次的車票的操作都被阻塞(等待)。當查詢該車次是否有空餘座位時,也就相當於給這個車次加上了鎖,直到預訂車票的操作成功或者失敗才解鎖,在此期間,其他查詢該車次是否有空餘座位的操作都被阻塞。

當發生死鎖後,就需要解除死鎖狀態,否則更多事務在請求處於死鎖狀態的數據時,就會發生連鎖反應,解除死鎖狀態的主要方式是選擇回滾其中的某一個事務。解除死鎖畢竟是一種消極的處理方式,積級的處理方式應當是預防死鎖、避免死鎖,同時輔助以檢測死鎖避免出現連鎖反應。在現代的數據庫系統中,一般都提供各種加鎖的方式以及比較智能的處理鎖。對於支持多臺計算機部署的場景中,也會把鎖延伸至多臺計算機中,也就是說,一臺計算機上的數據庫加鎖的時候,同時會影響到其他計算機上的數據庫,從而保證在多臺計算機上配置成一套數據庫系統時,運行在不同計算機上的事務的隔離級別和運行在單臺計算機上的隔離級別表現一致。

電子商務中事務的核心算法問題

在電子商務的場景中,因爲涉及到財務(錢)的操作,就需要保證事務的隔離級別足夠高不會導致出錯。在實際需求中,無論是錢的餘額,還是待售商品的存量,都會涉及到在一個表中查詢記錄並求和這樣的操作。如果應用將一個操作直接請求到數據庫時,爲避免在併發操作時出現兩次查詢的數據不一致的情況,就需要隔離級別中禁止幻讀。而隔離級別禁止幻讀時在多臺計算機上的的性能嚴重下降,難以滿足要求。這種現象說明不適合藉助於數據庫本身的鎖機制來實現電子商務中“海量事務高速處理”的請求(對於數據庫本身支持一定的中間件特性的情況按照中間件來討論)。

既然應用直接訪問數據庫不適合“海量事務高速處理”的場景,就有必要在中間增加中間件,把事務的協調處理交給中間件處理,由於計算量大,這個中間件需要部署在多臺計算機上,共同構成一個分佈式事務中間件。目前市場上有很多支持分佈式事務的中間件容器,例如EJB,可以通過簡單的配置和編程就可以獲得所需要的事務隔離級別。但是在電子商務的實際場景中,如果使用容器提供的分佈式事務將會遭遇和在數據庫中直接實現時相同的困境,如果不依賴於容器本身的分佈式事務,而是按照需求自行管理鎖的話,容器本身不僅沒有起到積極的作用,而且因爲引入了額外的處理過程而大大降低性能,因此目前世界上上處理速度最高的海量事務處理系統中,基本上都是自行開發。

自行開發分佈式事務中間件,可以按照實際需求最大程度的優化,自行開發也不意味着沒有規律可循,目前在“海量事務高速處理”中自行開發分佈式事務大多基於Paxos算法。 http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95 Paxos算法的基本內容可以參考維基百科上的介紹。本文不重複理論知識,僅探討一些在實踐中可能涉及到的問題。對於沒有接觸過Paxos算法的朋友,首先要理解Paxos是一個怎麼樣的算法,Paxos算法並不像JPEG、PNG、MPEG等等算法,Paxos算法提供了一個爲分佈式系統如何就某個值(決議)達成一致的模型。Paxos算法證明了一個符合Paxos模型的實現必然能保證分佈式一致性,不需要針對於每次的具體實現再次證明,當然,如果有時間和精力的情況下,證明一次Paxos算法有助於加深對於該算法的理解。其次要知道Paxos算法是目前解決分佈式系統一致性算法中最有效的算法。

剛纔提到Paxos算法解決的是分佈式系統一致性,而實際場景中從分佈式事務到分佈式系統一致性還需要按照具體的情況拆分事務。這個拆分過程需要反覆論證,任何一個環節的設計缺陷都將導致整個分佈式系統一致性失效。如果您剛纔已經看到了Paxos算法的簡要介紹,您可能會注意到兩個細節,在Paxos算法中值是保存在某一個分佈式節點上,每個節點中保存的值允許是不同的,因此對於獲取值這樣的操作也是針對於某個節點的,放在實際的計算機環境中也就意味着,數據庫被綁定在中間件上,其他的應用和中間件都從相關的中間件上獲取數據,而不是訪問數據庫。第二個細節就是提案依賴於編號,因此在實際的計算機環境中需要保證一個能滿足海量事務的編號機制。

安全問題與優化思想

除了算法問題外,還需要特別關注安全問題,“海量事務高速處理”的安全問題主要是在系統負荷暴增的情況下,或者有若干臺服務器宕機時,如何保證整個系統只是慢下來,而不是崩潰。一個針對類似問題的研究曾經引起美國政界的軒然大波,一位來自中國的留學生髮表了一篇《如何對美國電網的缺陷進行梯級式攻擊》的論文,其核心思想就是如何用最少的代價引起一個局部系統故障,而局部系統的故障可能導致更大範圍內的系統故障。在分佈式系統中普遍存在這個問題,而對於基於事務的“海量事務高速處理”則更應重視這個問題。再舉一個計算機的例子,Linux操作系統有一個Load值,這個值代表着一段時間內平均需要處理的進程數,保持計算機能及時處理進程的話,Load值應爲總內核數的70%,而Load值超過總內核數的100%的話,計算機的性能不會獲得提升,相反因爲CPU需要花費更多的時間在進程調度上。

以預定車票爲例,如果一個系統每秒種能處理一千次交易的話,那麼一般來說,如果有700次交易隨機分佈在這一秒鐘提交到系統的話,有一半以上的交易提交到系統的時候,系統正處於空閒狀態,可以立刻處理。如果有1000次交易隨機分佈在這一秒種提交到系統的話,那麼所有的交易都要先等待之前的交易完成後才能被系統處理。從系統的角度來說,不僅需要在處理交易的同時,阻塞後續的交易,同時還需要在完成一個交易後,選擇一個沒有和目前正在處理的其他交易有衝突的交易,這種判斷自然要比系統空閒時不需要判斷麻煩得多。如果負荷繼續攀升的話,系統需要不斷的阻塞後續的交易,反而減慢了正在處理的交易。目前12306.cn網站可以簡單按照這種思路來理解。

剛纔提到了在“海量事務高速處理”系統中,數據庫已經被融入了中間件中。而中間件的設計基於Paxos算法,與傳統意義上基於中間件容器的設計不同,一些在中間件容器的模型中放在中間件的功能在“海量事務高速處理”系統中可能放在應用中實現,也有可能正好相反,一些在中間件容器的模型中放在應用中的功能在“海量事務高速處理”系統中可能放在中間件中實現。更進一步,操作系統也需要按照實際的應用和中間件調優。這意味着在“海量事務高速處理”系統中,從應用到中間件到數據存儲到操作系統都是一體化考慮的,唯有如此,才能達到高速。

形象的說,“海量事務高速處理”系統最後往往分不出哪一塊是應用,哪一塊是數據庫。換言之,如果能像通用系統一有明顯的應用、數據庫、操作系統、中間件的界限的話,其實就失敗了,因爲沒有優化到極致,用武俠小說的話講以無招勝有招,通用系統就是“有招”,而“海量事務高速處理”系統必須做到“無招”,當然了,也可以稱之爲“屠龍之技”,因爲國內應用場景很少,在國外也主要是大型企業可能涉足這個領域。

不必重蹈覆轍

既然整個系統是一體化定製,就必然會導致牽一髮動全身,在實際環境中,特別是互聯網瞬息萬變,需求不可能一成不變,這時在需求環節中就需要精準分析需求,同時在前期要考慮前面提到的算法問題和安全問題。因爲在整個後面的過程中,核心需求是定死的,不可能有任何變化的,而且部署和運維也納入到研發中的全部定製。這麼多因素一起考慮,要定製中間件、操作系統和數據庫,可想而知開發難度有多大了。

在實踐中,特別是在沒有遇到瓶頸的時候,一個通用系統的解決方案,比如說Linux + Oracle + J2EE + Tomcat 更容易獲得認可。而在遭遇瓶頸後,因爲整個系統的架構已經定型,全面提升爲“海量事務高速處理”系統不僅有研發的投入,還有數據遺留資產的升級等等問題。最後往往採用犧牲局部的方式不停的打補丁來解決。這也是目前國內很少見到“海量事務高速處理”系統的原因。毫不誇張的說,國內多數發展迅猛的電子商務企業都正在面臨這樣的困境,可以預見是否有決心定製“海量事務高速處理”系統將成爲這些企業能否勝出的決定因素。

EJB方案之所以不適合,是因爲所用的Oracle + J2EE + Tomcat都是通用的工具,裏面有大量的爲了符合通用標準而具有的模塊,這些模塊雖然讓這些工具可以適合在多種場景下應用,但在特定場景下卻成了影響系統性能的垃圾,當這些“垃圾”需要清除的時候,目前的軟件公司基本上束手無措。

當然,不採用EJB方案不代表沒有中間件,前面提到的Paxos算法,在實踐中也是以中間件的形式體現,當然,在這種場景中的定製開發的中間件已經和通用方案中藉助於中間件容器實現的中間件已經大不相同,但是有一點要值得注意,所有的“寫”操作,都是藉助於中間件來完成的,而不是藉助於數據庫中的事務操作。

從大量的經驗教訓來看,對於已經預見到“海量”的電子商務網站,不要採用EJB等通用中間件容器方案;而對於有着“海量”發展預期,但是目前還沒有達到“海量”的電子商務網站,核心業務的所有的“寫”操作要藉助於中間件而不是數據庫。通常“海量事務高速處理系統”造價昂貴,應用範圍不廣,除了電子商務公司自行研發的系統外,目前涉足該領域的研究機構也非常少,清華大學web與軟件技術研究中心是中國極少的研究此技術的權威機構,有寶貴的成功經驗。

“海量事務高速處理”的演進道路

前面講了一些經驗教訓,在實踐中很少有像12306.cn這樣的可以預見到“海量”的電子商務網站,更多的有着“海量”發展預期,但是目前還沒有達到“海量”的電子商務網站,這些電子商務網站一方面受客觀因素制約不可能一步到位設計“海量事務高速處理”系統,另一方面發展期間同時也是業務探索期間,也不太可能明確哪一塊業務將發展成爲“海量”。此時就需要在實際建設之前規劃一個演講的道路,避免陷入誤區。下面講一下“海量事務高速處理”的演進道路。

首先要特別重視需求,既然核心系統將是全部定製開發,因此需求變更如果影響到了核心系統的話,那麼代價將是非常昂貴的。所以要儘可能的精確調研需求,避免模糊需求影響到核心系統,下圖就是廣爲流傳的由於需求理解的偏差,導致最終結果的嚴重偏差。


如果定性的話,曾經有一個研究,一個缺陷如果在需求階段修正要花1美元的話,那麼在設計與撰寫階段要花2美元,如果是在完成後修正的話,平均會花費69美元,如下圖所示。這還是針對項目而言。由於互聯網網站的升級必須要繼承所有的數據遺留資產,因此互聯網網站修正的代價將更加高昂,而對於處於核心的“海量事務高速處理”系統來說,嚴重缺陷的修正代價可能是不可想象的。


電子商務網站產品設計基礎

既然需求對於電子商務網站是如此重要,那麼該如何精確的分析需求並避免錯誤呢,一般來說,可以將一個網站分成如下圖所示的七種產品型態。


這七種產品形態可以用如下的經驗方式界定。


對於基本的電子商務網站來說,主要包含CMS、MIS、OA、ERP這四種產品形態,這四種產品形態在電子商務網站中的常見體現形式如下圖所示。


從圖中可以看出,核心交易系統的需求是ERP中的一部分。下面繼續分析ERP產品形態。一般來說ERP系統有三個層面——Data、Process、Action,大多數的ERP系統實現了Data和Process二個層面,Action目前尚處於探索階段。如果用一種簡單的方式理解的話,Data就是與核心交易系統的MIS,而OA就是與核心交易系統的OA,如下圖所示。


通過上面的介紹,讀者可以看到,電子商務網站產品設計的核心之處在於如何分析出核心交易系統的需求,換句話說,如果在產品設計階段就不重視核心交易系統的話,那麼在未來也將投入巨大的成本在修正缺陷上。

初期核心交易系統程序設計示例

前面提到,爲了長期的發展,從一開始所有的核心交易的“寫”操作都應當通過一個交易中間件,從架構模式上來看,屬於Blackboard“黑板”模式。在實踐中,一開始由於需求變化頻繁,在沒有特殊要求的場景中,“讀”操作可以直接讀取數據庫,爲了緩解壓力,也可以藉助於數據庫的複製功能,下圖就是一個核心交易系統中間件的部署示意圖。


由於需求隨着發展將不斷變化,因此一開始很難有清晰核心交易系統的邊界,所以在程序設計上採用接口設計方式,設計接口需要遵循三個標準:

  • 按照不同來源劃分接口
  • 接口都體現價值點。在本例中,用戶的價值點除了擁有虛擬貨幣外,還擁有“票”(具體業務相關,不展開討論),因此每一個接口或者和價值點有關,或者和待售物品有關
  • 接口應當具有明確的業務意義

接口設計示例如下圖所示。


交易系統的外部接口在處了了一些業務邏輯後,最終都將調用核心交易系統的處理過程,這個過程應當遵循如下的3個標準:

  • 初期可以採用“一個一個處理”的串行化方式將涉及到同一個價值點的多個處理過程排隊,以保證ACID,例如在Java中可以直接使用Synchronized關鍵字
  • 每個處理過程應儘量爲更多的接口服務,也就是說盡量把外部接口轉換成最少的核心交易處理過程
  • 每個處理過程應涉及兩個價值點,例如虛擬貨幣和物品,最多不超過3個

核心交易系統處理過程的序列圖示例如下圖所示。


在實際的生產環境中,該系統的日最高曾經處理約10萬筆交易。當然,這個數值和具體的項目需求密切相關,隨着複雜程度的提高,處理能力可能會直線下降,既便如此,也可以滿足起步電子商務網站的需求了。從公開的消息可以看到一些參考數據,在2011年11月11日的“雙十一”促銷期間,京東商城日訂單量超過了40萬單。

以預訂車票爲例,核心交易系統要處理的就是身份證、指定日期的指定車次這樣一個交易。連錢都不需要處理,因爲錢是在預訂成功之後支付的。通過本例可以看出,傳統的中間件容器方案中,爲了避免負荷被中間件容器放大,一般都會僅把核心業務放在中間件容器中,而在自行研發交易中間件時,所有程序按需開發,同時爲了應對未來必將出現的需求變更,將一部分需求在交易中間件中實現,也就是模糊了應用和中間件的界限。

發展期核心交易系統程序設計探討

當電子商務網站繼續發展,上面講到的單機的核心交易系統已經無法滿足需求時,就需要進一步提升核心交易系統的處理能力。一般有下面兩種策略,一種是資源預分配、另一種是離線鎖。

前面提到了資源預分配,也講到了資源預分配的缺點,但是資源預分配仍是一個非常有效的提升核心交易系統處理能力的方法。以預訂車票爲例,既然火車的座位數都是可以預知的,那麼完全可以預先爲每一天的每一個車次的每一個座位準備好空位,就差填寫身份證了。如果一臺計算機無法處理所有的交易,可以按照每一天的每一個車次來分配在不同的計算機上,這樣針對於特定日期特定車次的交易就可以定位在特定的計算機上,而該計算機只需要保證在本機上事務成功提交即可,因爲按照業務邏輯,這種預分配資源的方式可以保證計算機之間不會衝突。

針對於春運期間火車票的特殊場境,資源預分配可以進一步優化,比如說對於當天有票釋放出的車次,因爲訪問量巨大,所以儘量拆分開分配分配到獨立的計算機上處理,例如每種類型的車次一個計算機集羣,對於昨天有票釋放出的車次,在春運期間可以認爲基本上已經滿了,可以把幾種類型的車次合併爲一個計算機集羣,而對於更早釋放出的車票,可以認爲只剩下退票的情況了,可以把所有類型的車次合併爲一個計算機集羣,示意圖如下所示,當然實際情況要複雜得多。


資源預分配策略可以在一定時期內有效緩解核心交易系統的壓力。對於某些負荷變化大的應用,也可以採用資源預分配的策略,例如團購,每一件商品的數量是有限的,很有可能被瞬間搶購一空,此時就應該預先爲每一件商品分配空位。類似的,對於秒殺來說,商品數量很少,系統瞬間負荷很大,可以爲每一件商品分配一臺計算機處理。當然實際情況要複雜得多。

除了資源預分配之外,還有一種處理策略是離線鎖,在架構模式中,離線鎖包含了樂觀離線鎖和悲觀離線鎖兩種情況。離線鎖設計的初衷是用來解決當客戶端長期操作一個數據而鎖定數據的時候,服務器端無法同時處理大量的在線鎖的場景。在覈心交易系統的場景中,一臺響應用戶請求的計算機替代了客戶端的角色,每個需要保證事務的數據(通常是一組數據)對應一個鎖記錄,在每次請求的過程中,先在鎖記錄上通過一個原子操作將狀態從未鎖定狀態置爲鎖定狀態,然後操作數據(通常是一組數據),在操作完成後再在鎖記錄上通過一個原子操作將鎖定狀態置爲未鎖定狀態。這就是典型悲觀離線鎖的使用方式。如果把鎖記錄獨立爲一個服務的話,就稱爲鎖服務。

在實際場景中,一般是按照資源預分配策略來劃分離線鎖中鎖定的數據粒度,比如說在電子商務網站中,一個用戶所持有的所有價值點對應於一個鎖,這些價值點包括他的賬戶、各種優惠券等等,某一個品種的普通商品對應於一個鎖,當然也要考慮到前面提到的每一件商品對應於一個鎖的情況。而且一般一個事務會鎖定二個到三個數據,例如在一次消費過程中,先鎖定用戶,鎖定商品,然後操作用戶、操作商品,最後釋放用戶鎖、釋放商品鎖。如果在操作過程中發現異常,則在釋放鎖之前恢復數據,保證一致性。

“海量事務高速處理”期核心交易系統分析探討

當系統演講到海量事務高速處理階段,如前面所述,整個交易系統是完全按照需求定製的,下面僅就一些共性的問題做簡要的探討。主要涉及三方面的問題,系統的可用性,鎖的粒度,以及事務的劃分。

首先看系統的可用性,任何系統隨着規模的增長,設備故障的頻率也將越來越高,此時就需要有相應的容災策略。需要消除單點、甚至於單集羣故障。在前面提到的鎖服務也就不能採用通用的數據庫來設計。目前流行的做法是採用基於Paxos算法的鎖服務,通過五臺計算機(最少是三臺,一般是奇數個,考慮容災的需要一般用五臺,更多的計算機會造成之間通訊量的快速增長)組成一個針對某類資源的鎖服務集羣。在操作某一項資源時,先在鎖服務上對這一項加鎖,操作之後解鎖。這和悲觀離線鎖機制相同,當然在實際操作中,因爲還要消除保存資源數據的計算機的單點故障,加鎖和解鎖的過程要和所有數據的一致性保持同步。

既然鎖是以鎖服務的形式存在,那麼如何劃分鎖的粒度就成爲效率的關鍵,鎖的粒度太細的話,一個事務中操作鎖的次數就太多了,鎖的粒度太粗的話,碰撞的可能性就大得多。另外,既然鎖已經獨立於數據,因此一組鎖服務可以爲幾類數據量不大的數據服務,對於數據量很大的數據可以分成幾組鎖服務。

鎖的粒度劃分又和事務緊密聯繫在一起,一般來說,一個事務中持有的鎖越多,碰撞的機會就越多,而碰撞的結果就是回滾。從定性的分析來看,可以用N^2來評價,比如說一個事務中持有兩個鎖,那麼碰撞的機會就是4,持有三個鎖的話,碰撞的機會就是9。也就是說,如果把一個需要持有三個鎖的事務劃分成兩個持有兩個鎖的階段的話,碰撞的機會就是2*4=8<9。從這個簡單的定性分析可以看出,事務應以2個鎖爲主,不超過3個鎖。4個鎖以上的事務一定要分解成多個小事務。當然經過分解之後,複雜程度也隨之提升,但是因爲減少了碰撞,系統的總體性能獲得了提升。也就在另外的一些環節中減少了系統的複雜度。

系統的可用性、鎖的粒度以及事務的劃分這三個問題既相互獨立,又相互聯繫。對於核心交易系統來說,合理的事務劃分可以提升性能,從而減少計算機的數量,間接提升了可用性。

另外一些誤區

在前面提到了採用類似於EJB的中間件容器或者將事務交給數據庫處理是兩個主要的誤區,下面談一談其他一些認識上的誤區。

加強CDN建設能提升核心交易系統性能。答案是沒有直接聯繫,對於電子商務網站來說,CDN的價植更多體現在CMS產品上,也就是貨架陳列產品上,對於涉及到事務的MIS、OA、ERP來說,不可能使用CDN的緩存,最多使用CDN的路由加速功能。這些對於核心交易系統沒有直接聯繫。

將核心交易系統建設成開放平臺有助於簡化核心交易系統,提升交易性能。答案正好相反。內部系統縱然有種種弊端,但是對於任何一個細節都是可控的,而對於開放平臺來說,難以控制外部的操作,爲了應外可能出現的負荷變化可能要做更多的準備。換句說話,在內部系統還沒有完善的時候,開放平臺不僅沒有幫助,反而可能放大現在的缺陷引發連鎖崩潰。

對於其他的誤區歡迎各位聯繫,我將繼續補充。

發佈了37 篇原創文章 · 獲贊 3 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章