Django:加速数百个用户上传
Django: speed up hundreds of users upload
问题
我目前正在开发一个应用程序并将其部署到 Heroku 上。一个重要的功能是管理员应该能够通过上传 .csv
文件一次 上传数百个用户 。问题是,由于预算限制,使用 Heroku 的免费计划花费的时间太长,请求时间 运行 超出 。我需要一个更好的解决方案来处理这个问题。
我的尝试
我现在的做法是使用create_user()
向数据库注册新用户(代码附在下面)。
def register_user(upload_file, file_type):
if file_type == 'csv':
reader = csv.reader(StringIO(upload_file), delimiter=',')
for i, row in enumerate(reader):
if i == 0:
continue
else:
username = row[0]
password = row[1]
if username.isdigit():
is_staff = False
else:
is_staff = True
try:
User.objects.create_user(username=username, password=password, is_staff=is_staff)
except:
continue
我不使用 bulk_create()
的原因是我必须跟踪 skipped/not 添加到数据库的用户数量。 (尽管这可能不是最佳做法。)
一些发现
我的尝试在 localhost
中完全正常。约 300 个用户,大约需要 10 秒。
从这个thread,我已经了解到create_user()
由于制作密码的过程花费了太长时间。所以我决定使用 Argon2 哈希器而不是默认的哈希器。性能已经大幅提升,但仍然不够。
bulk_create()
确实加快了进程,但在 localhost
.
中几乎微不足道
有什么方法可以防止请求超时吗?
我假设 create_user
正在内部调用 QuerySet.create。此函数在内部使用对象构造函数创建实例,然后使用对象的 save
方法来持久化它。
即这些片段很可能是等价的
User.objects.create_user(username=username, password=password, is_staff=is_staff)
u = User(username=username, password=password, is_staff=is_staff)
u.save()
您可以测量每次调用所花费的时间。我敢打赌,节省占了最大的部分,因为它必须建立(或获得一个缓存的)数据库连接,创建一个 SQL 查询,执行它并 return 结果。
Django 的 ORM 提供了一个方便的功能,可以将所有这些查询合并为一个:bulk_create。如果您的数据库 运行 在不同的主机上,切换到此功能将产生重大影响。
有了这个,您的代码将如下所示。我也冒昧地清理了你的代码。
def register_users(upload_file, file_type):
if file_type != 'csv':
# TODO raise error here
return
reader = csv.DictReader(upload_file)
users = []
for row in reader:
is_staff = not row['username'].isdigit()
users.append(User(username=row['username'], password=row['password'], is_staff=is_staff))
try:
User.objects.bulk_create(users)
except:
# TODO: Catch specific expected exceptions and log them
continue
如果此优化没有在一秒内获得查询,我将调试 bulk_create
的内部以查看是否存在一些惰性计算函数(如您提到的哈希)。
请注意,我没有测试以上任何一项。
除了这个小改进之外,这种过程通常最好在后台异步完成。您可以直接 return 给请求者,并可选择将上传状态作为单独的 API 终点提供。这样可以降低对请求时序的要求,释放一个请求线程。
可能值得使用队列 and/or 一项单独的批量上传服务,具体取决于您的应用程序的配置文件。
问题
我目前正在开发一个应用程序并将其部署到 Heroku 上。一个重要的功能是管理员应该能够通过上传 .csv
文件一次 上传数百个用户 。问题是,由于预算限制,使用 Heroku 的免费计划花费的时间太长,请求时间 运行 超出 。我需要一个更好的解决方案来处理这个问题。
我的尝试
我现在的做法是使用create_user()
向数据库注册新用户(代码附在下面)。
def register_user(upload_file, file_type):
if file_type == 'csv':
reader = csv.reader(StringIO(upload_file), delimiter=',')
for i, row in enumerate(reader):
if i == 0:
continue
else:
username = row[0]
password = row[1]
if username.isdigit():
is_staff = False
else:
is_staff = True
try:
User.objects.create_user(username=username, password=password, is_staff=is_staff)
except:
continue
我不使用 bulk_create()
的原因是我必须跟踪 skipped/not 添加到数据库的用户数量。 (尽管这可能不是最佳做法。)
一些发现
我的尝试在
localhost
中完全正常。约 300 个用户,大约需要 10 秒。从这个thread,我已经了解到
create_user()
由于制作密码的过程花费了太长时间。所以我决定使用 Argon2 哈希器而不是默认的哈希器。性能已经大幅提升,但仍然不够。
中几乎微不足道bulk_create()
确实加快了进程,但在localhost
.
有什么方法可以防止请求超时吗?
我假设 create_user
正在内部调用 QuerySet.create。此函数在内部使用对象构造函数创建实例,然后使用对象的 save
方法来持久化它。
即这些片段很可能是等价的
User.objects.create_user(username=username, password=password, is_staff=is_staff)
u = User(username=username, password=password, is_staff=is_staff)
u.save()
您可以测量每次调用所花费的时间。我敢打赌,节省占了最大的部分,因为它必须建立(或获得一个缓存的)数据库连接,创建一个 SQL 查询,执行它并 return 结果。
Django 的 ORM 提供了一个方便的功能,可以将所有这些查询合并为一个:bulk_create。如果您的数据库 运行 在不同的主机上,切换到此功能将产生重大影响。
有了这个,您的代码将如下所示。我也冒昧地清理了你的代码。
def register_users(upload_file, file_type):
if file_type != 'csv':
# TODO raise error here
return
reader = csv.DictReader(upload_file)
users = []
for row in reader:
is_staff = not row['username'].isdigit()
users.append(User(username=row['username'], password=row['password'], is_staff=is_staff))
try:
User.objects.bulk_create(users)
except:
# TODO: Catch specific expected exceptions and log them
continue
如果此优化没有在一秒内获得查询,我将调试 bulk_create
的内部以查看是否存在一些惰性计算函数(如您提到的哈希)。
请注意,我没有测试以上任何一项。
除了这个小改进之外,这种过程通常最好在后台异步完成。您可以直接 return 给请求者,并可选择将上传状态作为单独的 API 终点提供。这样可以降低对请求时序的要求,释放一个请求线程。
可能值得使用队列 and/or 一项单独的批量上传服务,具体取决于您的应用程序的配置文件。