Reputation: 5881
I'm looking to speed up a query that needs to use distinct because it has a M2M field it selects on. At this point I'm not sure if my speed issues are related to how I have my DB server configured or if it's related to my queryset.
My question: What is the fastest queryset and can I also improve the speed by changing my Postgresql settings?
Instance: EC2 m1.xlarge
Postgresql Version: 9.1
Article Records: 240,695
Total Memory: 14980 MB
shared_buffers: 3617MB
effective_cache_size: 8000MB
work_mem: 40MB
checkpoint_segments: 10
maintenance_work_mem: 64MB
class AuthorsModelMixin(models.Model):
authors = models.ManyToManyField('people.Person', blank=True)
nonstaff_authors = models.CharField(
verbose_name='Non-staff authors', max_length=255, blank=True,
help_text="Used for the name of the author for non-staff members.")
byline_title = models.CharField(
max_length=255, blank=True,
help_text="Often contains an organization. Title of the person, or " \
"entity associated with the byline and a specified person " \
"(i.e. Associated Press).")
class Meta:
abstract = True
class TaxonomyModelMixin(models.Model):
sections = models.ManyToManyField(Section, blank=True)
tags = TaggableManager(
blank=True, help_text='A comma-separated list of tags (i.e. ' \
'Outdoors, Election, My Great News Topic).')
class Meta:
abstract = True
class PublishModelMixin(models.Model):
status_choices = (
('D', 'Draft'),
('P', 'Published'),
('T', 'Trash'),
)
comment_choices = (
('enabled', 'Enabled'),
('disabled', 'Disabled'),
)
sites = models.ManyToManyField(Site, default=[1])
status = models.CharField(
max_length=1, default='P', db_index=True, choices=status_choices,
help_text='Only published items will appear on the site')
published = models.DateTimeField(
default=timezone.now, db_index=True,
help_text='Select the date you want the content to be published.')
is_premium = models.BooleanField(
choices=((True, 'Yes'), (False, 'No')),
verbose_name='Premium Content', default=True)
comments = models.CharField(
max_length=30, default='enabled',
choices=comment_choices, help_text='Enable or disable comments.')
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
objects = PublishedManager()
class Meta:
abstract = True
class Article(AuthorsModelMixin, TaxonomyModelMixin, PublishModelMixin):
title = models.CharField(max_length=255)
slug = SlugModelField(max_length=255)
lead_photo = models.ForeignKey('media.Photo', blank=True, null=True)
summary = models.TextField(blank=True)
body = models.TextField()
Query time: (76 ms)
Pros: Fast and no chance published articles won't be displayed
Cons: If a higher id has an older pub date then the article list will be out of order
queryset = Article.objects \
.published() \
.filter(sections__full_slug__startswith=section.full_slug) \
.prefetch_related('lead_photo', 'authors') \
.order_by('-id') \
.distinct('id')
Query time: (76 ms)
Pros: Articles are in order all the time
Cons: If two articles have the same pub date and time, only one will be listed
queryset = Article.objects \
.published() \
.filter(sections__full_slug__startswith=section.full_slug) \
.prefetch_related('lead_photo', 'authors') \
.order_by('-published') \
.distinct('published')
Query time: (1007 ms)
Pros: Articles are in order all the time and no chance of articles not being listed
Cons: Much slower!
queryset = Article.objects \
.published() \
.filter(sections__full_slug__startswith=section.full_slug) \
.prefetch_related('lead_photo', 'authors') \
.order_by('-id', '-published') \
.distinct('id')
Query time: (4797.85 ms)
Pros: Not much, however not using DISTINCT ON
means it works on other databases like SQLite for tests
Cons: Much slower!!!
queryset = Article.objects \
.published() \
.filter(sections__full_slug__startswith=section.full_slug) \
.prefetch_related('lead_photo', 'authors') \
.order_by('-published') \
.distinct()
Upvotes: 3
Views: 2509
Reputation: 9749
Can you try a performance test on this query? As you haven't posted your models, please adapt any field names.
The idea is to break it into two: one that will return all Article ids looking at the intermediary table.
queryset = Article.objects \
.published() \
.filter(id__in=Article.sections.through.objects
.filter(section__full_slug__startswith=section.full_slug)
.values_list('article_id', flat=True)) \
.prefetch_related('lead_photo', 'authors') \
.order_by('-published', '-id')
Upvotes: 3