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)
优点:简洁
缺点: CharField的索引效率低于基于整数的索引
优点:基于整数的索引比基于CharField的索引更有效
缺点:我必须执行复杂的操作:
总结一下,真正的问题是:
我将字段迁移到SmallIntegerField所带来的性能提升值得冒险吗?
我倾向于尝试选项A,并检查性能改进是否足够。
我还向StackOverflow提出了这个问题,因为出现了一个更普遍的问题:
这种情况的起源是因为在定义项目模型时,我受到了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)
为什么他们在使用整数时会使用字符,因为它只是一个不应该显示的值表示?
答案 0 :(得分:3)
计数查询速度。
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服务器的专家,但我的理解是它在COUNT(*)查询上是particularly poor at using indexes。
<强>分区强>
您可以通过对数据进行分区来提高涉及event_type的查询的整体性能。由于当前索引的基数较差,因此规划人员进行全表扫描通常会更好。如果数据已分区,则只需要扫描该特定分区。
Char或Smallint
哪个占用更多空间char(2)或小int?答案是它取决于你的字符集。如果字符集每个字符只需要一个字节,那么小整数和char(2)将占用相同的空间量。由于该字段的基数非常低,因此在这种情况下使用char或smallint不会产生任何显着差异。