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 的查询更有效。”
所以,据我了解,我有两个选择*:
*我意识到可能存在第三种选择,因为低基数索引字段可能不会带来显着的改进 https://dba.stackexchange.com/a/101096,但由于我的值具有 [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)
Option A
Pros:简单
Cons: 查菲尔德 https://docs.djangoproject.com/en/1.9/ref/models/fields/#charfield基于索引的效率低于基于整数的索引
Option B
Pros:基于整数的索引比查菲尔德 https://docs.djangoproject.com/en/1.9/ref/models/fields/#charfield基于索引
Cons:我必须执行一个复杂的操作:
- 架构迁移以创建新的小整数字段 https://docs.djangoproject.com/en/1.9/ref/models/fields/#smallintegerfield
- 数据迁移将数百万个实例从旧字段复制(并转换)到新字段。
- 更新项目代码以使用新字段或执行另一次架构迁移以将新字段重命名为前一个字段。
- 删除旧字段。
总而言之,这里真正的问题是:
将字段迁移到 SmallIntegerField 所带来的性能提升值得冒这个风险吗?
我倾向于尝试选项 A,并检查性能改进是否足够。
我还在 StackOverflow 上提出了这个问题,因为出现了一个更通用的问题:
- 在任何情况下,在 Django 选择中使用 CharFields 是否比使用 Boolean/Integer/SmallIntegerField 更好?
这种情况的产生是因为在定义项目模型时我受到了启发Django 文档代码片段 https://docs.djangoproject.com/en/1.9/ref/models/fields/#choices:
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)
为什么他们可以使用整数时却使用字符,因为它只是一种永远不应该显示的值表示形式?