MySQL 索引
索引
- 索引的一个主要目的就是加快检索表中数据
- 是经过某种算法优化过的,因而查找次数要少的多
- 由此可见,索引是用来定位的
分类
- 普通索引
- 唯一索引
- 全文索引
- 联合索引
QuerySet
可切片
- 使用 Python 的切片语法来限制查询集记录的数目
- 它等同于 SQL 的 LIMIT 和 OFFSET 子句。
- 不支持负的索引(例如 Entry.objects.all ()[-1])
- 通常,查询集 的切片返回一个新的查询集 —— 它不会执行查询。
### 可迭代
惰性查询
- 查询集 是惰性执行的 —— 创建查询集不会带来任何数据库的访问
- 你可以将过滤器保持一整天,直到查询集 需要求值时,Django 才会真正运行这个查询。
- 一般来说,只有在 “请求” 查询集 的结果时才会到数据库中去获取它们。
- 当你确实需要结果时,查询集 通过访问数据库来求值。
缓存机制
- 每个查询集都包含一个缓存来最小化对数据库的访问
- 理解:
- 在一个新创建的查询集中,缓存为空。
- 首次对查询集进行求值, 同时查询数据库,Django 将保存查询的结果到查询集的缓存中并返回明确请求的结果
- 例如,如果正在迭代查询集,则返回下一个结果
- 接下来对该查询集 的求值将重用缓存的结果。
Model 建立索引
ORM 查询优化
减少连接
only()
只查某些字段
可以通过链式调用,不过等于又重新执行了查询该字段的sql,相当于在重新查了一遍
defer()
除了某些字段,其他的都查
values()
后续不能再点出其他字段了
自定义查询
总结
- 1、数据库技术进行优化,包括给字段加索引,设置唯一性约束等等;
- 2、查询过滤工作在数据库语句中做,不要放在代码中完成(看情况);
- 3、如果要一次查询出集合的数量,使用 count 函数,而不是 len 函数,但是如果后面还需要到集合,那就用 len,因为 count 还需要进行一次数据库的操作;
- 4、避免过多的使用 count 和 exists 函数;
- 5、如果需要查询对象的外键,则使用外键字段而不是使用关联的外键的对象的主键;
- 6、在通过 all 语句查询时,不要做跨表查询,只查询当前表中有的数据,否则查询语句的性能会下降很多;
比如:a 表存在外键 b 表: a.b.all () # 错误 - 7、如果想要查询其他表的数据,则加上 select_related (ForeignKey 字段名,其实就是主动联表查询,性能也会下降),如果有多个,则在括号中加上;
- 8、加 only 参数是从查询结果中只取某个字段,而另外一个 defer 方法则是从查询结果中排除某个字段;
- 9、不要获取你不需要的东西,可以通过 values 和 value_list 实现;
values 返回的是字典数组,比如:[{‘key1’: value1, ‘key2’: value2}, {‘key1’: value3, ‘key2’: value4}]
value_list 返回的是 tuple 数组 [(‘value1’, ‘value2’), (‘value3’, ‘value4’)]
value_list+flat=True 返回的是数组 [‘value1’, …] - 10、如果想知道是否存在至少一个结果,使用 exists,而不是使用 if QuerySet;但是如果后面需要用到前面的 QuerySet,那就可以使用 if 判断;
- 11、在任何位置使用 QuerySet.exists () 或者 QuerySet.count () 都会导致额外的查询;
- 12、不要做无所谓的排序,排序并非没有代价,每个排序的字段都是数据库必须执行的操作;
- 13、如果要插入多条数据,则使用 bulk_create 来批量插入,减少 sql 查询的数量;
- 14、对于缓存的 QuerySet 对象使用 with 标签,可以让数据被缓存起来使用;
- 15、使用 QuerySet.extra 明确的指出要查询的字段;
- 16、批量的更新和删除则使用 Queryset.update 和 delete 函数,但是更新操作注意对象的缓存;
- 17、使用 QuerySet.Iterator 迭代大数据;
当你获得一个 queryset 的时候,django 会缓存下来,保存在内存中
如果需要对 queryset 进行多次的循环,那么这种缓存无可厚非;
但是如果你只需要进行一次的循环,那么其实并不需要缓存,这个使用就可以使用 iterator;
- 18、如果想判断是否存在外键,只需要判断外键的 id 即可;
- 19、不要在循环中查询,而是提前取出,并且做好映射关系,这样在循环中直接通过字典的形式获取到;
- 20、当计算出一个 QuerySet 的时候,如果还需要进行多次循环的话,则可以先保留着这个缓存,但是如果只是使用一次的话,没有必要使用到缓存;
- 21、python 优化:排序尽量使用 .sort (), 其中使用 key 比 cmp 效率更高
- 利用
queryset lazy的特性
去优化代码,尽可能的减少连接数据库的次数. - 如果查出的 queryset 只用一次,可以使用 iterator () 去来防止占用太多的内存
- e.g.for star in star_set.iterator(): print(star.name).
- 感兴趣可以看看 ModelIterable 中重写的 iter 方法.
- 尽可能把一些数据库层级的工作放到数据库
- 使用 filter/exclude, F, annotate, aggregate (可以理解为 groupby), etc.
- aggregate: https://docs.djangoproject.com/en/1.11/topics/db/aggregation/#cheat-sheet
- F: getting the database, rather than Python, to do work
- 一次性拿出所有你要的数据,不去取那些你不需要的数据.
- 巧用 select_related (), prefetch_related () 和 values_list (), values (),
- 如果只需要 id 字段的话,用 values_list (‘id’, flat=True) 也能节约很多资源
- 或者使用 defer () 和 only () 方法:不加载某个字段 (用到这个方法就要反思表设计的问题了) / 只加载某些字段.
- 如果不用 select_related 的话,去取外键的属性就会连数据再去查找.
- bulk (批量) 地去操作数据,比如 bulk_create
- 查找一条数据时,尽量用有索引的字段去查询,O (1) 或 O (log n) 和 O (n) 差别还是很大的
- 用 count () 代替 len (queryset), 用 exists () 代替 if queryset:
评论区