Django — 提高 `startswith` 的性能

Posted

技术标签:

【中文标题】Django — 提高 `startswith` 的性能【英文标题】:Django — improve `startswith` performance 【发布时间】:2018-09-15 21:22:30 【问题描述】:

如何提高以下查询的性能?

class Worker(models.Model):
    name = models.CharField(max_length=32, db_index=True)

# This line is slow:
Worker.objects.filter(name__startswith='John')

我已经为模型添加了一个索引,但是......它根本没有被使用。 然而,当我在没有startswith 的情况下进行普通过滤时,索引确实会启动:

# This line is fast:
Worker.objects.filter(name='John')

为什么startswith 不使用索引?

【问题讨论】:

【参考方案1】:

问题在于startswith 表达式转换为包含LIKE 运算符的SQL 查询,它没有利用默认索引。

解决方案:添加一个带有特殊operator class的附加索引:

CREATE INDEX "appname_model_field_like_idx" 
ON "appname_model" ("fieldname" varchar_pattern_ops);

一步一步:

    首先,创建一个空迁移:

    python3 manage.py makemigrations appName --empty
    

    添加自定义 RunSQL 命令:

    class Migration(migrations.Migration):
    
        dependencies = [
            ('stats', '0002_auto_2010213_0159.py'),
        ]
    
        operations = [
            migrations.RunSQL(
                sql=r'''CREATE INDEX "appname_model_field_like_idx" 
                        ON "appname_model" ("fieldname" varchar_pattern_ops);''',
                reverse_sql=r'DROP INDEX "appname_model_field_like_idx";'
            ),
        ]
    

【讨论】:

reverse_sql 的附加样式点。丢失时总是令人沮丧。 似乎 Django 已经自动添加了这个索引(可能只有当引用字段上已经有索引时 - 我不确定)。【参考方案2】:

如果您的后端是 mysql,请尝试使用不区分大小写的 istartswith 来使用索引:

Worker.objects.filter(name__istartswith='John')

【讨论】:

以上是关于Django — 提高 `startswith` 的性能的主要内容,如果未能解决你的问题,请参考以下文章

AttributeError:“元组”对象没有属性“startswith”

在服务器上运行 collectstatic:AttributeError:“PosixPath”对象没有属性“startswith”

Django篇之F,Q

在Django中使用Q()对象

Django数据查询方法总结

Django中Q查询及Q()对象