Django页面命中计数器并发
Posted
技术标签:
【中文标题】Django页面命中计数器并发【英文标题】:Django page hit counter concurrency 【发布时间】:2011-11-05 19:52:15 【问题描述】:我是 django 新手。我在 Uni 的网络编程课程中做一个粗略的计数器作为作业。我做了一个类 HitCount:
from django.db import models
# Create your models here.
class HitCount(models.Model):
count = models.IntegerField()
然后我在视图文件中使用此代码:
def index(request):
#try getting a hitcounter, if there is none, create one
try:
hc = HitCount.objects.get(pk=1)
except:
hc = HitCount(count=0)
hc.save()
pass
#get a queryset containing our counter
hc = HitCount.objects.filter(pk=1)
#increment its count and update it in db
hc.update(count=F('count')+1)
#ATM hc is a queryset, so hc.count will just return how many
#counters are in the queryset (1). So we have to get the
#actual counter object
hc = HitCount.objects.get(pk=1)
#and return its count
return render_to_response('hitcount/index.html', 'count': hc.count)
这是我的 index.html 文件:
<p>count</p>
这似乎工作得很好,但我想知道:
这是一种合理的做法吗?增量代码真的应该驻留在视图文件中吗?还是应该把它移到类中的方法中? 这种并发是安全的还是我需要使用某种锁?部分任务是使计数器并发安全。我使用 SQLite,它使用事务,所以我认为应该没问题,但我可能会遗漏一些东西。【问题讨论】:
交易不会被自动使用。尽管它确实消除了 ORM 的一些细节,但如果您使用的是 mysql,则可以在单个查询中完成:INSERT INTO hitcount(id,count) VALUES(1,1) ON DUPLICATE KEY UPDATE count=count+1
。这完全避免了交易的需要。 (您可以将MERGE INTO
用于 Oracle 或 SQL 服务器,并在 PostgreS 中使用 UDF)
为您的意见干杯。你介意扩大一点吗?例如。如何强制使用交易?以及如何使用手动查询消除对事务的需求?那么不应该存在同时执行两个查询导致错误的风险吗?
请参阅here 了解有关 Django 如何处理事务以及如何让 Django 自动使用事务的信息。至于我的替代建议,每次更新都在单个语句中执行,并被数据库服务器视为原子,因此没有事务。
【参考方案1】:
题外话,但您应该在 try/except 中捕获 HitCount.DoesNotExist
,因为您真的只想在 HitCount 对象尚不存在的情况下执行 except 中的代码。
如果可能,您可能希望查看类似 Redis(或其他密钥/val 存储)之类的内容来计算您的命中计数器。
Redis 提供了一个名为INCR 的方法,它会自动将一个值增加 1。它非常快,并且对于像这样的命中计数器来说是一个很好的解决方案。您需要做的就是创建一个与页面相关的键,然后您可以将其增加 +1。
使用middleware 类来跟踪页面点击可能更有意义。比将其添加到每个视图要容易得多。如果您需要在每个页面上显示此计数,您可以使用context processor (more info) 将页面的点击计数添加到上下文中。这样会减少代码重复。
编辑
我最初错过了这是针对 Uni 项目的,因此对于您的需要,这可能会严重过度工程化。但是,如果您要为生产环境构建命中计数器,我建议您这样做。您仍然可以使用中间件/上下文处理器以 DRY 方式进行命中计数/检索。
【讨论】:
这是一个很好的答案和很好的信息!虽然在这种特殊情况下 Redis 可能有点矫枉过正,但我肯定会考虑将一些代码移动到中间件类中,这感觉像是一个更好的设计。因为您向我提供的信息可以回答我的两个问题,所以我会将其标记为已接受的答案。【参考方案2】:在 python 中可以使用以下方法进行锁定:
lock = Lock()
lock.acquire()
try:
... access shared resource
finally:
lock.release() # release lock, no matter what
请记住,该方法在多服务器环境中并不安全。
您还可以创建一个更具扩展性的“日志记录”解决方案,将每个命中作为数据库中的一行进行跟踪,并提供相关信息,然后甚至可以在特定日期范围内进行计数/查询。
【讨论】:
【参考方案3】:您可以为每个命中创建一个新的数据库行并调用 HitCount.objects.count() 来获取计数。
【讨论】:
感谢您的贡献,但可以说简单地增加对象计数值会更加简洁和轻便。以上是关于Django页面命中计数器并发的主要内容,如果未能解决你的问题,请参考以下文章