TL; DR :我有一个包含数百万个实例的表,我想知道如何索引它。

我有一个使用SQL Server作为数据库后端的Django项目。

在生产环境中建立具有大约1400万个实例的模型后,我意识到我遇到了性能问题:

class UserEvent(models.Model)

    A_EVENT = 'A'
    B_EVENT = 'B'

    types = (
        (A_EVENT, 'Event A'),
        (B_EVENT, 'Event B')
    )

    event_type = models.CharField(max_length=1, choices=types)

    contract = models.ForeignKey(Contract)

    # field_x = (...)
    # field_y = (...)

我在此字段中使用了很多查询,由于该字段未编制索引,因此效率非常低。仅通过此字段过滤模型大约需要7秒钟,而通过索引的外键查询不会带来性能问题:
UserEvent.objects.filter(event_type=UserEvent.B_EVENT).count()
# elapsed time: 0:00:06.921287

UserEvent.objects.filter(contract_id=62).count()
# elapsed time: 0:00:00.344261

当我意识到这一点时,我也对自己提出了一个问题:“该字段不应该是SmallIntegerField吗?因为我只有很少的选择集,所以基于整数字段的查询比基于文本/varchar的查询效率更高。”

因此,据我了解,我有两个选择*:


  • A)只需索引此字段,然后将其保留为CharField。
    A_EVENT = 'A'
    B_EVENT = 'B'
    
    types = (
        (A_EVENT, 'Event A'),
        (B_EVENT, 'Event B')
    )
    
    event_type = models.CharField(max_length=1, choices=types, db_index=True)
    
  • B)执行迁移以在SmallIntegerField中转换此字段(我不希望它成为BooleanField,因为可能向该字段添加更多选项),然后对该字段建立索引。
    A_EVENT = 1
    B_EVENT = 2
    
    types = (
        (A_EVENT, 'Event A'),
        (B_EVENT, 'Event B')
    )
    
    event_type = models.SmallIntegerField(choices=types, db_index=True)
    

    选项A

    优点:简单

    缺点:基于CharField的索引比基于整数的索引的效率低

    选项B

    优点:基于整数的索引比基于CharField的索引更有效

    缺点:我必须执行一个复杂的操作:
  • 架构迁移以创建新的SmallIntegerField
  • 数据迁移将数百万个实例从旧字段复制(和转换)到新字段。
  • 更新项目代码以使用新字段,或执行另一种模式迁移以将新字段重命名为上一个字段。
  • 删除旧字段。


  • 总结一下,这里的真正问题是:

    从将字段迁移到SmallIntegerField所获得的性能提高值得冒险吗?

    我倾向于尝试选项A,并检查性能改进是否足够。

    我还提出了这个问题到StackOverflow,因为出现了一个更通用的问题:
  • 在任何情况下,与Boolean/Integer/SmallIntegerField一起使用时,在Django选项中使用CharFields是更好的选择吗?

  • 之所以出现这种情况,是因为在定义项目模型时,我受到Django documentation code snippet的启发:
    YEAR_IN_SCHOOL_CHOICES = (
         ('FR', 'Freshman'),
         ('SO', 'Sophomore'),
         ('JR', 'Junior'),
         ('SR', 'Senior'),
    )
    
    year_in_school = models.CharField(max_length=2,
                                      choices=YEAR_IN_SCHOOL_CHOICES,
                                      default=FRESHMAN)
    

    为什么在使用整数时会使用chars,因为它只是一个永远都不会显示的值表示形式?

    最佳答案

    计数速度查询。

    UserEvent.objects.filter(event_type=UserEvent.B_EVENT).count()
    # elapsed time: 0:00:06.921287
    

    不幸的是,当表具有大量条目时,这种性质的查询在数据库中总是很慢。

    Mysql通过查看索引provided the indexed columns are numeric优化计数查询。因此,如果您使用的是mysql,则这是使用SmallIntegeField而不是Charfield的一个很好的理由,但显然您没有。您的里程因其他数据库而异。我不是SQL Server方面的专家,但我的理解是,它是COUNT(*)个查询的particularly poor at using indexes

    分区

    您可以通过对数据进行分区来提高涉及event_type的查询的整体性能。由于当前索引的基数很差,因此计划人员进行全表扫描通常会更好。如果数据已分区,则仅需要扫描该特定分区。

    字符或Smallint

    哪个占用更多的空间char(2)或small int?答案是,这取决于您的字符集。如果字符集每个字符仅需要一个字节,则小整数和char(2)将占用相同的空间量。由于该字段将具有非常低的基数,因此在这种情况下使用char或smallint不会有任何显着差异。

    关于sql-server - Django模型选择: IntegerField vs CharField,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36702839/

    10-11 23:00