【问题标题】:Prevent database from locking with multiple ajax posts in django and django-rest-framework防止数据库在 django 和 django-rest-framework 中使用多个 ajax 帖子锁定
【发布时间】:2015-02-20 14:05:51
【问题描述】:

我有以下型号

class Peri(models.Model):
    #fields

class Task(models.Model):
    #fields
    peri = models.ForeignKey(Peri)

我正在创建一个 javascript 客户端来创建新的 Peri 和任务。在创建新的 Peri 模型以预先创建必要的任务时,我想对自己轻松一点。每个 Peri 最多有 64 个任务。所以我想预先创建它们,然后让用户从 javascript 客户端/前端编辑它们。创建是使用 ajax 和 django-rest-framework 完成的。我使用 ajax 调用来创建 peri 模型,如果成功,我将创建任务

$.ajax({
    dataType: 'json',
    url: 'api/url',
    data: newPeriInstance,
    success: function (data, textStatus, jqXHR){
        for (var i = 0; i < 64; i++){
            $.ajax({
                dataType:'json',
                data: newTaskInstance,
                url: '/api/url/tasks/',
                success: function (data, textStatus, jqXHR){
                    //code for success
                },
                error: function (jqXHR, textStatus, errorThrown){
                    //code for error
                }
            });
        }
});

问题是我收到锁定数据库的错误。我在开发阶段使用 sqlit,但我不确定这不会在更大的生产规模中发生。我怎样才能通过这个问题?

Internal Server Error: /crm/api/peritasks/
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py", line 114, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/views/decorators/csrf.py", line 57, in wrapped_view
    return view_func(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/viewsets.py", line 79, in view
    return self.dispatch(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 403, in dispatch
    response = self.handle_exception(exc)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/views.py", line 400, in dispatch
    response = handler(request, *args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/mixins.py", line 54, increate
    self.object = serializer.save(force_insert=True)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py", line 595, in save
    self.save_object(self.object, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/rest_framework/serializers.py", line 1037, in save_object
    obj.save(**kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 545, in save
    force_update=force_update, update_fields=update_fields)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 573, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 654, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/base.py", line 687, in _do_insert
    using=using, raw=raw)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/manager.py", line 232, in _insert
    return insert_query(self.model, objs, fields, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/query.py", line 1511, in insert_query
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/usr/local/lib/python2.7/dist-packages/django/db/models/sql/compiler.py", line 898, in execute_sql
    cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 69, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/utils.py", line 99, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/util.py", line 53, in execute
    return self.cursor.execute(sql, params)
  File "/usr/local/lib/python2.7/dist-packages/django/db/backends/sqlite3/base.py", line 450, in execute
    return Database.Cursor.execute(self, query, params)
OperationalError: database is locked

【问题讨论】:

  • 需要更多细节...你能分享 django 错误和任何堆栈跟踪吗?
  • 请不要将堆栈轨道扔到外部链接中,它们往往会很快消失。
  • 很抱歉不知道...我会在这里发布

标签: python django sqlite django-rest-framework database-locking


【解决方案1】:

SQLite does not allow row-level locking 就像许多其他数据库系统一样,这也是它不是最好的开发或生产数据库的众多原因之一。它只支持写操作的数据库级锁,所以当你有多个写入请求时,只允许一个。最终,您肯定会遇到超时或锁定问题,这就是您在此处看到的。

因此,在您的情况下,您有几个选项可以防止锁定。

  1. 不要提前创建任务。就初始实现而言,这是最简单的方法,但它需要您的代码库的其余部分能够处理部分或不存在的任务列表。

  2. 在创建父对象时创建任务列表。这将需要修改Peri 模型视图的create 方法,但您可以避免客户需要这样做。您可以使用 Django 提供的the bulk_create method 在单个事务中创建所有任务,并且不会遇到很多速度问题。

【讨论】:

  • 是的,我正在考虑在创建 peri 模型时创建任务,就像上面建议的 @nima 一样。但是当尝试更新(放置)所有任务时呢?它不会引起同样的问题吗?
  • 仅在 SQLite 上(真的,你应该尝试匹配生产)。我推荐drf-bulk之类的东西,但可以通过API进行批量更新和创建。
  • 所以如果我使用更好的数据库系统(我使用 MySQL 进行生产)db 将不会被锁定?我在大型应用程序方面不是很有经验,所以任何帮助将不胜感激
  • 正如我在回答中提到的,SQLite 进行数据库级锁定(一次只对整个数据库进行一次写入),而大多数数据库都进行行或表级锁定。因此,您在 MySQL 上遇到锁定问题的可能性要小得多。
【解决方案2】:

在 for 循环中发送这么多 Ajax 调用并不是一个好主意。在 Peri 模型中创建任务:

class Peri(models.Model):

    # fields

    def save(self, *args, **kwargs):
        # Create tasks here
        super(Peri, self).save(*args, **kwargs)

【讨论】:

    【解决方案3】:

    虽然存在不预先创建任务的论据,但如果有此要求,您可以扩展当前的 API 视图,使其接受要创建的任务数量(或始终创建 64 个)并构建一个列出并使用 Django 的 bulk_create 方法。

    这取决于您从 DRF 使用的 APIView,但作为一个想法:

    class MyApiView(baseview):
        def create(*args):
            model = super(MyAPIView, self).create(*args)
            tasks = []
            for i in range(0, 64):
                tasks.append(Task(...))
    
            Task.objects.bulk_create(tasks)
            return model
    

    【讨论】:

    • 这并没有提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。
    • 你能解释为什么或如何它不能回答这个问题吗?是不是简单的“你不能延长……”可以改写为“你可以延长……”?
    • 你可以修改它,让它看起来更像是一个答案而不是评论
    猜你喜欢
    • 2019-02-22
    • 2018-09-19
    • 2016-02-28
    • 2021-09-01
    • 1970-01-01
    • 2017-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多