2012-08-07 32 views
14

我期望在天青表存儲中實現頁面查看計數器。如果說兩個用戶同時訪問該頁面,並且PageViews上的當前值爲100,則確保更新操作後的PageViews = 102?天青表存儲中的原子操作

+1

我不知道這個問題,但爲什麼不直接添加一個100 MB的SQL數據庫,每月$ 5。 SQL處理鎖。 – Paparazzi 2012-08-08 19:56:17

回答

23

答案取決於你如何實現你的櫃檯。 :-)

表存儲沒有一個「增量」操作符,所以你需要閱讀的當前值(100),並將其更新爲新的值(101)。表存儲採用了樂觀併發性,所以如果您在使用.NET存儲客戶端庫時自然而然地做出了什麼,那麼當兩個進程試圖同時執行此操作時,您可能會看到一個異常。這將是流:

  1. 過程A讀取網頁瀏覽值,並接收100
  2. 方法B讀取網頁瀏覽值,並接收100
  3. 方法A使一個有條件更新與瀏覽量該裝置「只要當前爲100,就可以將PageView設爲101。」這成功了。
  4. 進程B執行相同的操作並失敗,因爲前提條件(PageViews == 100)爲false。

明顯的事情,當你收到此錯誤是重複的過程中做。 (讀取當前值,現在爲101,並更新爲102.)這將始終(最終)導致計數器具有正確的值。

還有其他的可能性,我們做了關於如何實現一個真正可擴展的計數器整個雲插曲:http://channel9.msdn.com/Shows/Cloud+Cover/Cloud-Cover-Episode-43-Scalable-Counters-with-Windows-Azure

如果發生碰撞的可能性不大,那麼該視頻中描述的內容可能會過度殺傷。也就是說,如果你的命中率是每秒一次,那麼正常的「讀,增,寫」模式將是安全和有效的。另一方面,如果你每秒獲得1000次點擊,你會想要做更聰明的事情。

編輯

只是想澄清誰閱讀理解樂觀併發人......有條件的操作是不是真的「只要設置瀏覽量,因此101,因爲它是目前100元。」這更像是「將頁面視圖設置爲101,只要自上次查看它以來沒有改變」。 (這是通過使用回來在HTTP請求中的ETag來完成的。)

+0

我會建議使用AutoRenewLease.DoOnce(由我們的好朋友smarx,http://blog.smarx.com/posts/managing-concurrency-in-windows-azure-with-leases :) :) – 2012-08-07 20:14:55

+0

儘可能多的我喜歡的人使用我的代碼:-),我不認爲我遵循這將如何幫助這裏? – smarx 2012-08-07 22:08:32

+0

糟糕,DoOnce錯了,但是我會在一個名爲PageView.PartitionKey +「_」+ PageView.RowKey的blob上使用AutoRenewLease。一旦它鎖定了blob,它將獲得該記錄並增加計數。通過這種方式,您可以確保每個頁面視圖都被佔據。所有這些都使用瞬態故障處理來確保 - 在出現問題的情況下 - 代碼會重試,直到它可以記錄綜合瀏覽量。 – 2012-08-07 22:17:24

8

你也可以重新思考「計數」的一部分。爲什麼不把它變成一個兩步過程?

第1步 - 錄製頁面訪問量

每次有人瀏覽網頁的記錄添加到表(我們稱之爲網頁瀏覽量)。您將添加在這些商店之一的信息將是如下:

  • PartitionKey =頁面名稱
  • RowKey =隨機GUID

後一些看法,你就會有這樣的事情:

  • MyPage。ASPX - someGuid
  • MyPage.aspx - someGuid
  • SomePage.aspx頁面 - someGuid
  • MyPage.aspx - someGuid

第2步 - 計數頁面訪問量

我們想要做什麼現在是獲取所有這些記錄,計數它們,增加計數器並刪除所有記錄。假設你有多個工作人員在運行。你的工人都會有一個循環,隨機運行1到10分鐘。每當工作人員的時間過去時,如果還沒有租約(這應該總是相同的blob,您可以使用AutoRenewLease),它將採用blob的租約。

第一個工人獲得鎖可以繼續執行計數:

  1. 獲取從PageViewRecordings表或從緩存中
  2. 計數每個網頁頁面視圖中的所有記錄
  3. 更新計數的地方
  4. 刪除計數時考慮到的記錄

這裏的問題是t這很難把它變成一個冪等過程。如果您的實例在計數和刪除之間崩潰,會發生什麼?您的頁數會增加,但由於這些項目未被刪除,因此下次處理它們時,它們將被添加到總計數中。

這就是爲什麼我會建議如下。在同一張表(PageViews)中,您還將在同一分區中記錄總頁面瀏覽量。但數據會有所不同一點(這將是該分區中的一個紀錄保持者的總數):

  • PartitionKey =頁面名稱
  • RowKey = Guid.Empty(只是不使用隨機GUID ,這樣我們就知道了記錄的頁面視圖和記錄了總計數的記錄之間的區別)。
  • 計數=當前網頁視圖計數

這是完全可能的,因爲表存儲是模式少。我們爲什麼要這樣做?因爲如果我們將自己限制在具有最多100個實體的同一個表+分區,我們確實有交易。我們可以用這個做什麼?

  1. 使用Take,我們從該表+分區中獲得100條記錄。
  2. 我們會得到的第一條記錄是'counter'記錄。爲什麼?由於其rowkey是Guid.Empty和排序是辭書
  3. 計數這些記錄(-1,因爲第一個記錄是不是一個頁面視圖,這只是我們的計數器佔位符)
  4. 更新計數器記錄的Count屬性
  5. 刪除99(或更少)其他記錄
  6. 使用批處理更改SaveChanges。
  7. 重複,直到只剩下1條記錄(計數器記錄)。

然後每工作X分鐘,您的工作人員就會看到是否沒有租約,獲得租約並重新啓動流程。

這個答案是否足夠清晰,還是應該添加一些代碼?

+0

現在我明白你對blob租約的看法。這是明智的,但我仍然喜歡分片之類的東西(如在雲端封面插曲中)。 – smarx 2012-08-07 22:55:35

+0

我喜歡這個想法。不需要代碼,我清楚地理解它。但你的意思是,「如果**不是**租賃,那麼工人需要租賃並重新啓動流程」是吧? – States 2012-08-07 22:59:09

+0

是的,我已經更新了答案。 – 2012-08-07 23:01:20

1

如果使用Azure的網站,那麼Azure的隊列和WebJobs是另一種選擇。 在我的一個場景中,雖然我實際上將採取分片方法,並讓WebJobs定期更新聚合。具有PartitionKey = User和RowKey = Page的UserPageViews的Azure表存儲表。兩個具有相同用戶ID的同時用戶將不被允許。