在10个并发的uwsgi工作者上锁定Django DB - 怎么样?

时间:2015-01-19 17:40:16

标签: django concurrency sqlite locking

最优雅的方式是什么?

锁定Django DB ,同时我进行复杂交易(读取,决定,写入)

...在此期间,没有其他uwsgi工作者应该访问该表(或者至少没有访问权限)到该表?

我正在使用Django + db.sqlite3 + uwsgi(+ nginx)。

非常感谢!


编辑1:

太棒了,当然Django本身解决了这个问题。比我想象的容易得多。好的,我问了一下!

这是我的黄色标记突出显示的版本http://marker.to/W0CbtZ 关于交易的手册页https://docs.djangoproject.com/en/1.7/topics/db/transactions/

谢谢,IRC: - )


编辑2:

我实际上正在寻找一个数据库LOCK ,所有进程都会等到轮到他们。

我今天学到了什么,现在我实施了:

try: 
   with transaction.atomic():
      foo(obj)
      obj.save()     # (*)
except IntegrityError:
   print "debug information"

所以我永远不会得到一个不一致的数据库。但是现在(*)抛出异常"操作错误:数据库被锁定" (而且this suggestion也没有帮助)。据我所知,因为有数十个此类交易正在尝试同时保存到数据库。

我更需要的是像

lock = threading.Lock()
...
with lock:
    foo(obj)
    obj.save()

但不是线程,因为它需要锁定所有uwsgi worker 进程

欢迎任何想法。这样做最优雅的方法是什么?现在要查看手册的哪一部分?我已经google了,并没有找到答案 - 这就是我在这里问的原因。

非常感谢。


编辑3:

因为我仍有一小部分"操作错误:数据库被锁定"失败(即使my threading.Lock),我实施了@ knbk的建议 - 但它没有用,这是输出:

up to here all is fine
ALERT! cannot start a transaction within a transaction
Out[]: False

这是我实施它的方式,关注@ knbk' code example

from django.db import connection

def foobar():
    cursor = connection.cursor()
    cursor.execute('BEGIN IMMEDIATE')  # also tried 'BEGIN EXCLUSIVE'
    try:
        myObjects = myModel.objects.filter(myFilter="myfilter")
        if myCondition(myObjects) > 0:
            obj = myObjects[0]
            print "up to here all is fine"
            obj.save()                                          
    except Exception as e:
        connection.rollback()
        print "ALERT!", e
        return False
    finally:
        connection.commit()
    return True

那么......现在怎么样?


编辑4:

现在自己解决了。我编写了一个完整的 lockbydir.DLock 类,它使用目录存在和年龄 - 用于跨进程锁定!现在我可以简单地锁定对Django DB的访问,无论使用哪个DB。很高兴。也许你想看看?这是GIT:

https://github.com/drandreaskrueger/lockbydir

请参阅自述文件。甚至还有3个GIT播放器可以在您的浏览器中观看代码示例的实时执行!好极了,不是吗? ; - )

感谢您的时间和关注!

2 个答案:

答案 0 :(得分:2)

获取锁定的正确陈述是BEGIN IMMEDIATEBEGIN EXCLUSIVE。第一个将获得一个锁,以防止其他进程获得写锁。第二个将获得一个锁,该锁也可以防止其他进程获得读锁定。

Django没有提供高级api来执行这些语句。相反,您必须使用数据库游标直接执行这些语句:

from django.db import connection

cursor = connection.cursor()
cursor.execute('BEGIN IMMEDIATE')
try:
    my_objs = Model.objects.filter(...)
    etc...
except:
    connection.rollback()
finally:
    connection.commit()

免责声明:请注意,此方法(尤其是COMMIT EXCLUSIVE)将获取整个数据库的锁定。 Sqlite不支持行级或表级锁定。在锁定期间,没有其他进程能够写入数据库的任何部分,并且使用独占锁定它们甚至无法读取。如果这是一个频繁的操作或者你有一个写入量很大的数据库,请考虑使用MySQL或PostgreSQL,它们都支持行/表级锁定。如果您的站点需要10个并发工作者,那么这可能是一个好主意。

答案 1 :(得分:-2)

现在,我可以得到(几乎!)所有问题都会消失,因为添加了一个threading.Lock() - 显然(大部分)问题是由同一个工作进程的并发线程试图访问数据库引起的不是为了那个 - sqlite3。

成功的解决方案现在看起来像这样:

from django.db import transaction, IntegrityError, OperationalError

import threading
lock = threading.Lock()

def foobar():
    with lock:  
        try:
            with transaction.atomic():
                obj = readFromDB() 
                if foo(obj): 
                    obj.save()                                          
        except (IntegrityError, OperationalError) as e:
            print "ALERT!", e
            return ABORTED
    return CORRECTO

即使用数十个并发请求锤击服务器,几乎总是返回CORRECTO,每秒造成数百次数据库访问。

然而,我很少见到“操作错误:数据库被锁定”。所以这些剩余的案例现在肯定是由进程(而不是线程)并发问题引起的。 我的Django数据库锁在哪里,我希望如此? :-)

非常感谢所有想帮助我的人!


我解决了。请参阅原始问题中的“编辑4”:Lock Django DB on 10 concurrent uwsgi workers - how to?