JCJ*_*CJS 10 sql-server django indexing django-models
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 = (...)
Run Code Online (Sandbox Code Playgroud)
我使用了很多基于此字段的查询,并且效率非常低,因为该字段未编入索引.仅使用此字段过滤模型大约需要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
Run Code Online (Sandbox Code Playgroud)
当我意识到这一点,我也做了一个问题对自己说:"不应该在该领域是一个SmallIntegerField因为我只有一小部分的选择,以及基于在整数字段查询比基于文本/ 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)
Run Code Online (Sandbox Code Playgroud)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)
Run Code Online (Sandbox Code Playgroud)优点:简单
缺点: 基于CharField的索引的效率低于基于Integer的索引
优点:基于整数的索引比基于CharField的索引更有效
缺点:我必须执行复杂的操作:
总结一下,这里真正的问题是:
从将字段迁移到SmallIntegerField所获得的性能提升值得冒险吗?
我倾向于尝试选项A,并检查性能改进是否足够.
我还向StackOverflow提出了这个问题,因为出现了一个更通用的问题:
出现这种情况是因为在定义项目模型时,我受到Django文档代码片段的启发:
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)
Run Code Online (Sandbox Code Playgroud)
为什么他们在使用整数时使用字符,因为它只是一个不应该永远不会显示的值表示?
计数查询的速度。
UserEvent.objects.filter(event_type=UserEvent.B_EVENT).count()
# elapsed time: 0:00:06.921287
Run Code Online (Sandbox Code Playgroud)
不幸的是,当表中有大量条目时,这种性质的查询在数据库中总是很慢。
如果索引列是数字, Mysql 通过查看索引来优化计数查询。因此,如果您使用的是 mysql,但显然您不是,那么这是使用 SmallIntegeField 而不是 Charfield 的一个很好的理由。您的里程因其他数据库而异。我不是 SQL Server 方面的专家,但我的理解是它在 COUNT(*) 查询上使用索引特别差。
分区
您也许可以通过对数据进行分区来提高涉及 event_type 的查询的整体性能。由于当前索引的基数很差,因此规划器通常最好进行全表扫描。如果数据已分区,则仅需要扫描该特定分区。
Char 或 Smallint
char(2) 和小 int 哪个占用更多空间?答案是,这取决于您的字符集。如果字符集只需要每个字符一个字节,那么小整数和 char(2) 将占用相同的空间量。由于该字段的基数非常低,因此在这种情况下使用 char 或smallint 不会产生任何显着差异。
| 归档时间: |
|
| 查看次数: |
2102 次 |
| 最近记录: |