Sql server Django模型选择:IntegerField vs CharField
TL;DR:我有一个包含数百万个实例的表,我想知道应该如何为它编制索引 我有一个Django项目,它使用SQL Server作为数据库后端 在生产环境中拥有大约1400万个实例的模型后,我意识到我遇到了性能问题:Sql server Django模型选择:IntegerField vs CharField,sql-server,django,indexing,django-models,Sql Server,Django,Indexing,Django Models,TL;DR:我有一个包含数百万个实例的表,我想知道应该如何为它编制索引 我有一个Django项目,它使用SQL Server作为数据库后端 在生产环境中拥有大约1400万个实例的模型后,我意识到我遇到了性能问题: class UserEvent(models.Model) A_EVENT = 'A' B_EVENT = 'B' types = ( (A_EVENT, 'Event A'), (B_EVENT, 'Event B')
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 = (...)
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
我在这个字段中使用了很多查询,而且效率很低,因为该字段没有索引。仅使用此字段筛选模型几乎需要7秒,而使用索引外键查询不会带来性能问题:
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 = (...)
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的查询效率更高。”
所以,据我所知,我有两个选择*:
*我意识到第三个选项可能存在,因为,但是因为我的值有[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(我不希望它是Boolean字段,因为可以向该字段添加更多选项),然后为该字段编制索引
A_EVENT = 1 B_EVENT = 2 types = ( (A_EVENT, 'Event A'), (B_EVENT, 'Event B') ) event_type = models.SmallIntegerField(choices=types, db_index=True)
总而言之,这里真正的问题是: 将字段迁移到SmallIntegerField所带来的性能改进值得冒险吗? 我倾向于尝试选项A,并检查性能改进是否充分
我还向StackOverflow提出了这个问题,因为出现了一个更一般的问题:
- 在Django选项中使用CharFields是否比使用Boolean/Integer/SmallIntegerField更好
既然它们可以使用整数,为什么还要使用字符,因为它只是一个永远不应该显示的值表示形式?计数查询的速度。
UserEvent.objects.filter(event_type=UserEvent.B_EVENT).count()
# elapsed time: 0:00:06.921287
不幸的是,当表有大量条目时,这种性质的查询在数据库中总是很慢
Mysql通过查看索引优化计数查询。因此,如果您使用的是mysql,那么这是使用SmallIntegeField而不是Charfield的一个很好的理由,但显然您不是。您的里程数因其他数据库而异。我不是SQL server方面的专家,但我的理解是它是基于COUNT(*)查询的
分区
通过对数据进行分区,可以提高涉及事件类型的查询的总体性能。因为当前索引的基数很差,所以规划者最好进行一次完整的表扫描。如果数据已分区,则只需扫描该特定分区
Char或Smallint
哪个字符(2)或小整数占用更多空间?答案是它取决于你的角色集。如果字符集每个字符只需要一个字节,则小整数和字符(2)将占用相同的空间量。由于该字段的基数将非常低,因此在这种情况下使用char或smallint不会产生任何显著差异。您遇到性能问题的实际查询将是一个有用的补充。@e4c5我已在问题中添加了查询示例:)我认为它们使用字符是因为可读性。您仍然可以在额外逻辑中使用值表示。如果您有一个测试服务器/db,我建议您尝试一下,看看通过迁移生产是否有任何好处。我认为在使用的存储空间方面不会有太大的差异。我已经更新了我的答案。谢谢你的输入。实际上,我用count查询来证明一个例子。最常见的情况是沿着布尔字段按事件类型进行筛选,以获得实例列表。您和我们已经陷入了一场白费力气的追逐!无法从计数查询判断性能。感谢您的输入。实际上,我用count查询来证明一个例子。最常见的情况是沿布尔字段按事件类型进行筛选,以获得实例列表。我的想法是,索引性能与存储空间无关,而是与数据类型有关。即使一个小的Int占用更多的空间,它不是比Char更有效吗?同时,由于99%的数据没有被应用程序使用,并且我们能够将其存储在该数据库之外,因此我们决定停止存储所有信息,只存储应用程序所需的数据。好的,你是对的,这是一个误导性的示例。对此我深表歉意。但性能问题是实实在在的。我认为关于指数表现和影响的问题是值得的。