【发布时间】:2016-08-10 17:08:07
【问题描述】:
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 吗?由于我只有一小部分选择,并且基于整数字段的查询比基于 text/varchar 的查询效率更高查询。”
所以,据我了解,我有两个选择*:
*我意识到可能存在第三个选项,因为 indexing fields with low cardinality may not cause severe improvements,但由于我的值具有 [1%-99%] 分布(我正在寻找 1% 部分),索引该字段似乎是一个有效的选项。
-
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 提出了这个问题,因为出现了一个更通用的问题:
- 在 Django 选项中使用 CharFields 是否比使用 Boolean/Integer/SmallIntegerField 更好?
之所以出现这种情况,是因为我在定义项目模型时受到了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)
为什么在可以使用整数的情况下使用字符,因为它只是一种不应该显示的值表示形式?
【问题讨论】:
-
您遇到性能问题的实际查询将是一个有用的补充。
-
@e4c5 我在问题中添加了查询示例:)
-
我会说他们使用字符是因为可读性。您仍然可以在额外的逻辑中使用值表示。如果您有一个测试服务器/数据库,我建议您尝试一下,看看迁移生产是否有任何好处。我认为在使用的存储空间方面不会有太大差异。
-
我已经更新了我的答案。
标签: sql-server django indexing django-models