面试题答案
一键面试数据库设计优化
- 合理的表结构设计
- 避免冗余:确保数据库表结构遵循范式原则,减少数据冗余,防止在更新操作时出现数据不一致问题。例如,商品的基本信息(如名称、描述等)在商品表中存储一份,避免在其他关联表中重复存储。
- 拆分大表:如果某些表数据量非常大,可以考虑垂直拆分或水平拆分。对于商品详情页涉及的表,若商品表字段过多,可以将不常用字段拆分到单独的扩展表中。水平拆分可按一定规则(如按时间、地区等)将数据分布到不同的表中,减轻单表查询压力。
- 添加索引
- 为关联字段添加索引:对于商品详情页关联查询涉及的外键字段,如商品分类表与商品表关联的分类ID字段,在商品表的分类ID字段上添加索引。这样在执行JOIN操作时,数据库可以更快地定位匹配的数据行,提高查询效率。
- 复合索引:如果查询经常涉及多个字段的条件筛选,可创建复合索引。例如,如果经常根据商品类别和价格范围查询商品详情,可在商品表的类别字段和价格字段上创建复合索引
(category_id, price)
,注意索引字段顺序应根据查询条件的选择性来确定,选择性高的字段在前。
查询优化
- 减少JOIN操作
- 使用数据库视图:对于复杂的多表关联查询,可以创建数据库视图。视图将多个表的关联逻辑封装起来,查询视图时就像查询单表一样,减少了在应用层编写复杂JOIN语句的工作量,同时数据库可以对视图查询进行优化。例如,将商品表、商品详情表、商品库存表等关联查询封装成一个视图,用于商品详情页的展示。
- 尽量避免子查询:子查询在性能上通常不如JOIN操作。如果有嵌套子查询的情况,尝试将其改写为JOIN查询。例如,原本通过子查询获取符合条件的商品ID,再根据ID查询商品详情,可以改写为直接通过JOIN将多个表关联起来进行查询。
- 优化查询语句
- 使用
select_related
和prefetch_related
:在Django的ORM中,对于外键关联的查询,使用select_related
进行“深度优先”查询,它通过SQL的JOIN语句一次性获取关联对象的数据。例如,商品表与商品品牌表通过外键关联,查询商品详情时可以使用Product.objects.select_related('brand').get(pk = product_id)
,这样只需要一次数据库查询就可以获取商品及其品牌信息。对于多对多关系或反向一对多关系,使用prefetch_related
,它会执行多个查询,但会在Python层面合并结果,避免N + 1问题。例如,商品表与商品标签表是多对多关系,可使用Product.objects.prefetch_related('tags').get(pk = product_id)
。 - 使用原始SQL:在某些复杂情况下,Django的ORM可能无法生成最优的SQL语句。此时,可以使用原始SQL查询。但要注意防止SQL注入问题,通过参数化查询方式传递变量。例如,
from django.db import connection; with connection.cursor() as cursor: cursor.execute("SELECT * FROM product JOIN product_detail ON product.id = product_detail.product_id WHERE product.id = %s", [product_id]); result = cursor.fetchone()
。
- 使用
缓存机制优化
- 页面缓存
- 使用Django内置的缓存中间件:在Django的
settings.py
中配置缓存中间件,如django.middleware.cache.UpdateCacheMiddleware
和django.middleware.cache.FetchFromCacheMiddleware
。可以根据商品详情页的URL设置缓存时间,例如,对于大部分商品详情页设置缓存时间为30分钟(1800秒)。这样在缓存时间内,相同URL的请求直接从缓存中获取页面内容,无需经过复杂的数据库查询和视图渲染过程。 - CDN缓存:将商品详情页中的静态资源(如图片、CSS、JavaScript文件)分发到CDN网络。CDN节点会根据用户的地理位置缓存和提供这些资源,大大加快资源的加载速度,减轻服务器压力。同时,CDN还可以缓存部分HTML页面片段,进一步提高页面加载效率。
- 使用Django内置的缓存中间件:在Django的
- 数据缓存
- 使用Django的缓存API缓存查询结果:在视图函数中,可以使用
cache
模块缓存商品详情查询结果。例如,from django.core.cache import cache; def product_detail_view(request, product_id): cache_key = f'product_detail_{product_id}' product_detail = cache.get(cache_key) if not product_detail: product_detail = Product.objects.select_related('brand').prefetch_related('tags').get(pk = product_id) cache.set(cache_key, product_detail, 60 * 10) # 缓存10分钟 return render(request, 'product_detail.html', {'product': product_detail})
。这样在缓存有效期内,再次请求相同商品详情时,直接从缓存中获取数据,减少数据库查询次数。 - 使用分布式缓存(如Redis):对于高并发场景,分布式缓存可以提供更好的扩展性和性能。可以将商品详情数据缓存到Redis中,Redis支持多种数据结构,可根据需求选择合适的数据结构存储缓存数据。例如,使用哈希表结构存储商品详情信息,以商品ID作为哈希表的键,商品详情的各个字段作为哈希表的字段和值。同时,Redis具有较高的读写性能和数据过期机制,可以有效管理缓存数据。
- 使用Django的缓存API缓存查询结果:在视图函数中,可以使用