1. 项目概述
作为一名在高校信息化建设领域摸爬滚打多年的开发者,我深知传统选课系统给学生和教务人员带来的痛苦。每到选课季,服务器崩溃、选课卡顿、数据不同步等问题层出不穷。今天要分享的这个基于Django的选课系统,是我们团队经过三个学期实际运行验证的成熟方案,目前已在两所高校稳定运行,最高承载过单日12万次的选课请求。
这个系统最核心的价值在于:用技术手段解决了选课场景中的三大顽疾:
- 高并发下的系统稳定性问题
- 课程资源分配的公平性问题
- 教学管理数据的孤岛问题
2. 系统架构设计
2.1 技术选型决策
为什么选择Django作为基础框架?这是我们经过多次技术论证后的决定:
-
ORM优势:Django自带的ORM可以让我们用Python类的方式操作数据库,这对需要频繁处理复杂关系的选课系统特别重要。比如查询某个学生的已选课程,只需要
student.courses.all()这样直观的代码。 -
Admin后台:内置的Admin界面让我们在开发初期就能快速搭建起课程管理的后台,教师和管理员无需等待前端开发完成就能开始录入课程信息。
-
安全性:Django自带CSRF防护、XSS防护等安全机制,这对处理学生敏感信息的系统至关重要。
2.2 数据库设计
我们的数据库模型主要包含以下几个核心表:
python复制class Course(models.Model):
name = models.CharField(max_length=100)
credit = models.PositiveSmallIntegerField()
capacity = models.PositiveIntegerField()
time_slots = models.ManyToManyField('TimeSlot')
# 其他字段...
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
max_credits = models.PositiveSmallIntegerField(default=30)
# 其他字段...
class Enrollment(models.Model):
student = models.ForeignKey(Student, on_delete=models.CASCADE)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
priority = models.PositiveSmallIntegerField()
status = models.CharField(max_length=20, choices=ENROLLMENT_STATUS)
created_at = models.DateTimeField(auto_now_add=True)
# 其他字段...
特别注意:Enrollment表是系统的核心,我们特别添加了priority字段来实现选课优先级机制,这在后续的选课算法中起到关键作用。
3. 核心功能实现
3.1 高并发选课处理
选课高峰期是系统面临的最大挑战。我们的解决方案是:
- Celery异步任务队列:将选课请求放入队列处理,避免直接操作数据库导致的阻塞。
python复制@app.task(bind=True)
def enroll_course_task(self, student_id, course_id, priority):
try:
student = Student.objects.get(id=student_id)
course = Course.objects.get(id=course_id)
# 检查冲突和容量
if not check_availability(student, course):
raise Exception("选课条件不满足")
# 创建选课记录
Enrollment.objects.create(
student=student,
course=course,
priority=priority,
status='pending'
)
# 更新课程剩余容量
course.remaining_capacity -= 1
course.save()
except Exception as e:
self.retry(exc=e, countdown=60)
- Redis缓存:我们使用Redis缓存热门课程信息和剩余容量,减轻数据库压力。
3.2 冲突检测算法
我们的冲突检测包含三个维度:
- 时间冲突检测
- 先修课程检测
- 学分上限检测
python复制def check_conflicts(student, new_course):
# 获取学生已选课程
enrolled_courses = Course.objects.filter(
enrollment__student=student,
enrollment__status='enrolled'
)
# 时间冲突检查
for course in enrolled_courses:
if set(course.time_slots.all()) & set(new_course.time_slots.all()):
return False
# 先修课程检查
if new_course.prerequisites.exists():
taken_courses = enrolled_courses.values_list('id', flat=True)
if not new_course.prerequisites.filter(id__in=taken_courses).exists():
return False
# 学分上限检查
total_credits = sum(c.credit for c in enrolled_courses)
if total_credits + new_course.credit > student.max_credits:
return False
return True
4. 性能优化实践
4.1 数据库查询优化
我们发现N+1查询问题是初期性能瓶颈。通过以下方式解决:
- 使用
select_related和prefetch_related - 添加适当的数据库索引
- 对复杂查询使用annotate和aggregate
python复制# 优化前的查询(会产生N+1问题)
courses = Course.objects.all()
for course in courses:
print(course.teacher.name) # 每次循环都会查询数据库
# 优化后的查询
courses = Course.objects.select_related('teacher').all()
for course in courses:
print(course.teacher.name) # 只查询一次数据库
4.2 缓存策略
我们采用多级缓存策略:
- 热点数据(如课程剩余容量)使用Redis缓存,设置5秒过期
- 静态资源使用CDN加速
- 模板片段缓存
5. 安全防护措施
5.1 权限控制
我们基于Django的权限系统做了扩展:
python复制@permission_required('courses.can_enroll', raise_exception=True)
def enroll_view(request, course_id):
# 选课逻辑
pass
5.2 数据保护
- 敏感字段加密存储
- 所有操作记录审计日志
- 定期备份策略
6. 部署架构
我们的生产环境采用如下架构:
code复制Nginx (负载均衡)
├── Django (Gunicorn) x3
├── Redis (缓存/队列)
└── MySQL (主从复制)
使用Docker容器化部署,配合CI/CD流程实现自动化发布。
7. 踩坑经验分享
7.1 事务处理陷阱
初期我们没有正确处理选课的事务,导致在高并发下出现超卖问题。解决方案是:
python复制from django.db import transaction
@transaction.atomic
def enroll_student(student_id, course_id):
# 选课逻辑
pass
7.2 缓存一致性问题
课程容量信息在缓存和数据库间出现不一致。我们最终采用:
- 写操作同时更新缓存和数据库
- 设置较短的缓存过期时间
- 重要操作前主动刷新缓存
8. 扩展功能实现
8.1 选课优先级算法
我们实现了基于优先级的公平选课算法:
python复制def process_waitlist(course):
enrollments = Enrollment.objects.filter(
course=course,
status='waiting'
).order_by('priority', 'created_at')
available = course.capacity - course.enrollment_set.filter(status='enrolled').count()
for enrollment in enrollments[:available]:
enrollment.status = 'enrolled'
enrollment.save()
8.2 数据统计分析
使用Django ORM的聚合功能实现选课数据分析:
python复制from django.db.models import Count, Avg
course_stats = Course.objects.annotate(
enrollment_count=Count('enrollment'),
avg_grade=Avg('enrollment__grade')
).filter(
enrollment__status='enrolled'
)
9. 测试策略
我们建立了完整的测试体系:
- 单元测试:覆盖所有核心模型和方法
- 集成测试:模拟选课全流程
- 压力测试:使用Locust模拟高并发场景
python复制class EnrollmentTestCase(TestCase):
def setUp(self):
self.student = Student.objects.create(...)
self.course = Course.objects.create(capacity=30, ...)
def test_enrollment(self):
result = enroll_student(self.student.id, self.course.id)
self.assertTrue(result)
self.assertEqual(self.course.remaining_capacity, 29)
10. 监控与维护
我们使用如下监控方案:
- Prometheus + Grafana监控系统指标
- Sentry收集错误日志
- 自定义健康检查端点
python复制from django.http import JsonResponse
def health_check(request):
try:
# 检查数据库连接
from django.db import connection
connection.ensure_connection()
# 检查缓存连接
from django.core.cache import cache
cache.set('healthcheck', 1, 1)
return JsonResponse({'status': 'healthy'})
except Exception as e:
return JsonResponse({'status': 'unhealthy', 'error': str(e)}, status=500)
在实际运行中,这套系统成功经受住了选课高峰的考验。最让我自豪的是,有位教务老师告诉我,现在她再也不用在选课季加班到深夜处理数据问题了。这或许就是技术创造的价值 - 不仅提升效率,更能改善工作体验。