用Django获取随机记录集,是什么影响了性能

2024-10-04 01:22:48 发布

您现在位置:Python中文网/ 问答频道 /正文

上面说的是

Record.objects.order_by('?')[:n]

有性能问题,建议您这样做:(here

^{pr2}$

既然如此,为什么不直接这样做:

result = random.sample(Record.objects.all(),n)

我不知道这些代码什么时候在后台运行django。请告诉我最后一行代码是否更有效?为什么?在

=============编辑2013-5-12 23:21 UCT+8=======================

我整个下午都在做这个测试。
我的电脑:CPU Intel i5-3210M RAM 8G 系统:Win8.1 pro x64 Wampserver2.4-x64(带apache2.4.4 mysql5.6.12 php5.4.12)Python2.7.5 Django1.4.6

我所做的是:

  1. 创建应用程序。在
  2. 用索引和CharField内容构建一个简单模型,然后Syncdb。在
  3. 创建3个视图可以得到一个随机集20个记录以上3种不同的方式,并输出使用的时间。在
  4. 修改settings.py,使{}可以将日志输出到控制台。在
  5. 在表中插入行,直到行数是我想要的。在
  6. 访问3个视图,注意SQL查询语句、SQL时间和总时间
  7. 在表格中重复5、6行。(10k,200k,1m,5m)

这是views.py

def test1(request):  
    start = datetime.datetime.now()  
    result = Record.objects.order_by('?')[:20]  
    l = list(result) # Queryset是惰性的,强制将Queryset转为list  
    end = datetime.datetime.now()  
    return HttpResponse("time: <br/> %s" % (end-start).microseconds/1000))  

def test2(request):  
    start = datetime.datetime.now()  
    sample = random.sample(xrange(Record.objects.count()),20)  
    result = [Record.objects.all()[i] for i in sample]  
    l = list(result)  
    end = datetime.datetime.now()  
    return HttpResponse("time: <br/> %s" % (end-start)  

def test3(request):  
    start = datetime.datetime.now()  
    result = random.sample(Record.objects.all(),20)  
    l = list(result)  
    end = datetime.datetime.now()  
    return HttpResponse("time: <br/> %s" % (end-start) 

正如@Yeo所说,result = random.sample(Record.objects.all(),n)是垃圾。我不谈这个。
但有趣的是,Record.objects.order_by('?')[:n]总是比其他的好,尤其是小于1m行的表。数据如下: test data

以及图表: SQL costtotal cost

怎么了? 在上一个测试中,tatget table,result = random.sample(Record.objects.all(),n)中的5195536行实际上执行了以下操作:

(22.275) SELECT `randomrecords_record`.`id`, `randomrecords_record`.`content` 
FROM `randomrecords_record` ORDER BY RAND() LIMIT 20; args=()

每个人都是对的。用了22秒。以及

^{pr2}$

实际上是这样做的:

(1.393) SELECT COUNT(*) FROM `randomrecords_record`; args=()
(3.201) SELECT `randomrecords_record`.`id`, `randomrecords_record`.`content` 
FROM `randomrecords_record` LIMIT 1 OFFSET 4997880; args=()
...20 lines

如你所见,排一排,花费3秒。我发现索引越大,所需的时间就越长。
但是。。。为什么?在

我的想法是:
如果有什么方法可以加快大索引查询的速度

^{pr2}$

应该是最好的。除了(!)这张桌子比1米行还小。在


Tags: sampledatetimeobjects时间orderrandomresultall
2条回答

Record.objects.count()被转换成非常简单的SQL查询。在

SELECT COUNT(*) FROM TABLE

Record.objects.all()[0]也被转换成一个非常简单的SQL查询。在

^{pr2}$

Record.objects.all()通常将结果切分以提高性能

SELECT * FROM table LIMIT 20;  // or something similar

list(Record.objects.all())将查询所有数据并将其放入列表数据结构中。在

SELECT * FROM TABLE

因此,每当您将查询集转换为列表时,就会发生代价高昂的情况

在您的示例中,random.sample()将转换为一个列表。(如果我没有错的话)。在

因此,当您执行result = random.sample(Record.objects.all(),n)操作时,它将执行完整的查询集并转换为一个列表,然后随机选择该列表。在

想象一下,如果你有数百万的记录。您要查询并将其存储到一个包含数百万元素的列表中吗?或者您更愿意一个一个地查询

.order_by(?)的问题在于,它实际上是ORDER BY RAND()(或等效的,取决于DB),这基本上必须为每一行创建一个随机数并进行排序。这是一项繁重的工作,需要很多时间。在

另一方面,做Record.objects.all()会迫使你的应用程序下载所有对象,然后从中进行选择。它在数据库方面没有那么重(它将比排序更快),但它在网络和内存上很重。因此,它也会扼杀你的表演。在

所以这就是交易。在

现在情况好多了:

sample = random.sample(xrange(Record.objects.count()),n)
result = [Record.objects.all()[i] for i in sample]

因为它避免了上面提到的所有问题(注意,Record.objects.all()[i]被转换成{},这取决于DB)。在

但是,由于.count可能很慢(与通常一样:依赖于DB),所以它可能仍然是低效的。在

相关问题 更多 >