在2010年4月,Google的網頁索引更新實現了實時更新,在今年的OSDI大會上,Google首次公佈了有關這一技術的論文。 html
在此以前,Google的索引更新,採用的的批處理的方式(map/reduce),也就是當增量數據達到必定規模以後,把增量數據和全量索引庫Join,獲得最新的索引數據。採用新的索引更新系統以後,數據的生命週期縮短了50%,所謂的數據生命週期是指,數據從網頁上爬下來,到展示在搜索結果中這段時間間隔,可是正如Google所強調的,這一系統僅僅是爲增量更新所創建的,並無取代map/reduce的批量做業處理模式。 sql
Google的新一代增量索引更新 – Percolator,是創建在Bigtable之上,提供的API也儘可能接近Bigtable的方式,因此整個架構大體是以下的樣子: 數據庫
事務(Transaction)和鎖(Lock)有區別嗎? 架構
在關係數據庫領域,兩者仍是有很大區別的,可是對Percolator而言,Transaction = Lock,因此咱們這裏討論的分佈式鎖,也能夠說是分佈式事務,因此下面提到的鎖或者事務,指的都是同一件事。 oracle
Percolator利用Bigtable原有的行鎖,再加上本身的一些巧妙的作法,實現了分佈式鎖服務,這就意味着,Google能夠實時的更新PB級別的索引庫。最近咱們發現Google的搜索結果時效性很好,剛寫好的文章,幾分鐘以後,Google就能夠檢索到,緣由就在Google的Crawler在抓到新的網頁以後,不用再等待必定的時間批量更新索引,而是實時的更新,數據生命週期大大縮短。 nosql
Percolator支持跨行,跨表的事務,充分利用了Bigtable自己已經有的行事務、備份機制。 分佈式
在分析Percolator的細節以前,先看一個簡單的例子,對Percolator有一個大概的認識,有利於後面的理解。 google
下面的這個例子是把UserA的人氣分減掉10,加到UserB的人氣分上,key表示每一行的key,data,lock,write是列名字,data存儲數據,lock存儲鎖狀態,write表示事務提交後的數據位置引用. spa
初始狀態:UserA有100我的氣分,UserB有50我的氣分 .net
最終狀態:UserA有90我的氣分,UserB有60我的氣分
Step0(初始狀態)
Key | Data | Lock | Write |
UserA | 100:t1 | ||
UserB | 50:t2 |
Step1(從UserA中拿出10我的氣分)
Key | Data | Lock | Write |
UserA | 90:t2100:t1 | Primary Lock:t2 | t2 |
UserB | 50:t2 |
Step2(把UserB的人氣分加10)
Key | Data | Lock | Write |
UserA | 90:t2100:t1 | primary_lock:t2 | t2 |
UserB | 60:t350:t2 | Primary_lock:UserA@data | t3 |
Step3(事務提交)
A:先提交primary(移除鎖,寫入新的timestamp,在write列寫入新數據的位置引用)
Key | Data | Lock | Write |
UserA | t390:t2 100:t1 |
t3:data:t2t2 | |
UserB | 60:t350:t2 | Primary_lock@UserA.data | t3 |
B:再提交非primary(步驟同上)
Key | Data | Lock | Write |
UserA | t390:t2 100:t1 |
t3:data:t2t2 | |
UserB | t460:t3 50:t2 |
t4:data:t3t3 |
事務結束了,UserA有90我的氣分,timestamp是t3,Userb有60我的氣分,timestamp是t4。(至於鎖的寫法和write列爲何那樣寫,後面再詳細解釋)
Percolator鎖分爲兩種,primary和non-primary,在事務提交的過程當中,先提交primary鎖,不管是跨行仍是跨表,primary鎖都是沒有區別的。
事務的提交
事務的提交的過程分兩步,以UserA爲例:
首先,在write列寫入新數據的位置引用,注意不是數據,是引用(理解成指針會更形象),上面step3A 中t3:data:t2表示在t3時刻提交的數據,最新的數據在data列的t2 timestamp
而後,移除lock列的內容。
由於Bigtable支持行鎖定,因此上述兩步都是在一個Bigtable事務內完成的。
讀操做
當一個client在發起讀操做以後,首先會向oracle server申請time stamp,接下來Percolator會檢查lock列,若是lock列不空,那麼讀操做試圖移除(修復)這個lock或者等待,在後續鎖衝突處理詳細介紹如何修復。
補充:oracle發放time stamp是嚴格遞增的,並且不是一次發放一個,而是採起批量的方式。
寫操做
當一個client發起寫操做以後,首先會向oracle server申請time stamp,Percolator會檢查write列,若是write列的timestamp大於當前client的timestamp,那麼寫失敗(不能覆蓋新的數據 write-write conflict);若是lock列有鎖存在,說明當前行正在被另外的client鎖定,client要麼寫失敗,要麼試圖修復(lock conflict)!
Notify機制
Percolator定義了一系列的Observer(相似於數據庫的trigger),位於Bigtable的tablet server上,Observer會監視某一列或者某幾列,當數據發生變化就會觸發Observer,Observer執行完以後,又會建立或者通知後續的Observer,從而造成一個通知的傳遞。
當一個client在事務提交階段,crash掉了,那麼鎖還保留,這樣後續的client訪問就會被阻止,這種狀況叫作鎖衝突,Percolator提供了一種簡單的機制來解決這個問題。
每一個client按期向Chubby Server寫入token,代表本身還活着,當某個client發現鎖衝突,那麼會檢查持有鎖的client是否還活着,若是client是working狀態,那麼client等待鎖釋放。不然client要清除掉當前鎖。
Roll forward & roll back:
Client先檢查primary lock是否存在,由於事務提交先從primary開始,若是primary不存在,那麼說明前面的client已經提交了數據,因此client執行roll forward操做:把non-primary對應的數據提交,而且清除non-primary lock;若是primary存在,說明前面的client尚未提交數據就crash了,此時client執行roll back操做:把primary和non-primary的數據清除掉,而且清除lock。
Google的分佈式鎖服務很好了支持了增量索引的實時更新,縮短了數據的生命週期。本文對notify機制介紹的比較簡單,感興趣的請參考論文原文。