2012-08-07 79 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的同时用户将不被允许。