带缓存的 Django 模型 count()

标签 django database python-2.7 caching prometheus

我有一个带有 Apache Prometheus 监控和模型的 Django 应用程序,名为 Sample

我想监控 Sample.objects.count() 指标 并在具体时间间隔内缓存此值 避免在数据库中进行昂贵的 COUNT(*) 查询。

来自本教程 https://github.com/prometheus/client_python#custom-collectors 我读到我需要编写自定义收集器。

实现此目标的最佳方法是什么? 在 django 中有什么方法可以 获取 Sample.objects.count() 缓存值并在 K 秒后更新它? 我也在我的应用程序中使用 Redis。我应该把这个值存储在那里吗? 我应该创建单独的线程来更新 Sample.objects.count() 缓存值吗?

最佳答案

首先要注意的是,您实际上并不需要缓存 count(*) 查询的结果。

虽然不同的 RDBMS 处理计数操作的方式不同,但对于大型表,它们的速度普遍较慢。但它们的一个共同点是 RDBMS 提供了一个替代 SELECT COUNT(*) 的方法,它实际上是一个缓存结果。好吧。

您还没有提到您的 RDBMS 是什么,所以让我们看看它在与 Django 一起使用的流行的 RDBMS 中如何

mysql

假设您的表上有一个主键并且您使用的是 MyISAM。 SELECT COUNT() 在 mysql 上非常快并且扩展性很好。但很可能您正在使用 Innodb。由于各种原因,这是正确的存储引擎。 Innodb 是事务感知的,不能像处理 MyISAM 那样处理 COUNT(),并且查询会随着表的增长而变慢。

对具有 2M 记录的表的计数查询花费了 0.2317 秒。以下查询耗时 0.0015 秒

SELECT table_rows FROM information_schema.tables 
WHERE table_name='for_count';

但它报告的值是 1997289 而不是 200 万,但足够接近了!

因此您不需要自己的缓存系统。

数据库

Sqlite COUNT(*) 查询并不是真的很慢,但它也不能扩展。随着表大小的增长,计数查询的速度会减慢。使用类似于 mysql 中使用的表,SELECT COUNT(*) FROM for_count 需要 0.042 秒才能完成。

没有捷径可走。 sqlite_master 表不提供行数。 pragma table_info

也没有

你需要自己的系统来缓存SELECT COUNT(*)的结果

PostgreSQL

尽管是功能最丰富的开源 RDBMS,但 postgresql 不擅长处理计数 (*),它速度慢且扩展性不佳。也就是说,和穷亲戚没什么区别!

count 查询在 postgreql 上用了 0.194 秒。另一方面,以下查询花费了 0.003 秒。

SELECT reltuples FROM pg_class WHERE relname = 'for_count'

您不需要自己的缓存系统。

SQL服务器

SQL Server 上的 COUNT 查询平均耗时 0.160 秒,但波动相当大。对于此处讨论的所有数据库,第一个 count(*) 查询相当慢,但后续查询速度更快,因为文件已由操作系统缓存。

我不是 SQL Server 方面的专家,所以在回答这个问题之前,我不知道如何使用模式信息查找行数。我找到了这个 Q&A有帮助。我试过的其中一个在 0.004 秒内产生了结果

SELECT t.name, s.row_count from sys.tables t
JOIN sys.dm_db_partition_stats s
ON t.object_id = s.object_id
AND t.type_desc = 'USER_TABLE'
AND t.name ='for_count'
AND s.index_id = 1

您不需要自己的缓存系统。

集成到Django

可以看出,除 sqlite 之外的所有数据库都提供了内置的“缓存查询计数”,我们不需要创建自己的数据库。创建客户经理以利用此功能是一件简单的事情。

class CustomManager(models.Manager):

    def quick_count(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""SELECT table_rows FROM information_schema.tables 
    WHERE table_name='for_count'""")

         row = cursor.fetchone()
         return row[0]

class Sample(models.Model):
    ....
    objects = CustomManager()

上面的例子是针对 postgresql 的,但同样的事情可以用于 mysql 或 sql server,只需将查询更改为上面列出的查询之一。

普罗米修斯

如何将其插入 django prometheus?我把它留作练习。

关于带缓存的 Django 模型 count(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41397052/

相关文章:

python - Django 1.8 自动登录

Javascript 每次循环 JSON 时只获取第一个元素?

java - 如何在从数据库获取值的地方制作可靠的下拉菜单

python - 使用递归查找数字是否为 2 的幂

python - 将训练和测试数据保存在文件中

Python子进程bash命令输出文件被阻止

django - 如果用户没有帐户,如何在十小时后删除数据?

python - 带有 wsgi 的 Django 偶尔失败并显示 "Premature end of script headers:"

python - 使用 Python 的分配算法

PHP 使用 session 登录