文章目录
一、orm中的锁
1.行级锁
mysql中,存储引擎是innodb时,是可以支持行级锁的,那么在orm中操作数据库时,为某一行数据开启行级锁只需要执行如下命令
select_for_update(nowait=False, skip_locked=False)
注意:
- 行级锁必须用在事务里面
- 所有匹配的行将被锁定,直到事务结束,这意味着可以通过锁防止数据被其它事务修改
返回一个锁住行直到事务结束的查询集,如果数据库支持,它将生成一个 SELECT … FOR UPDATE 语句。
举个例子:
entries = Entry.objects.select_for_update().filter(author=request.user) #加读取互斥锁。create、update、delete操作时,mysql自动加行级互斥锁
- mysql在查询时自动加的是共享锁,可以同时读,不能同时修改。
- 手动加读取互斥锁后,既不能同时读,也不能同时修改
一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。
如果这不想要使查询阻塞的话,使用select_for_update(nowait=True)。 如果其它事务持有冲突的锁,互斥锁, 那么查询将引发 DatabaseError 异常。你也可以使用select_for_update(skip_locked=True)忽略锁定的行。nowait和skip_locked是互斥的,同时设置会导致ValueError。
目前,postgresql,oracle和mysql数据库后端支持select_for_update()。 但是,MySQL不支持nowait和skip_locked参数。
使用不支持这些选项的数据库后端(如MySQL)将nowait=True或skip_locked=True转换为select_for_update()将导致抛出DatabaseError异常,这可以防止代码意外终止。
表锁(了解)
class LockingManager(models.Manager):
""" Add lock/unlock functionality to manager.
Example::
class Job(models.Model): #其实不用这么负载,直接在orm创建表的时候,给这个表定义一个lock和unlock方法,借助django提供的connection模块来发送锁表的原生sql语句和解锁的原生sql语句就可以了,不用外层的这个LckingManager(model.Manager)类
manager = LockingManager()
counter = models.IntegerField(null=True, default=0)
@staticmethod
def do_atomic_update(job_id)
''' Updates job integer, keeping it below 5 '''
try:
# Ensure only one HTTP request can do this update at once.
Job.objects.lock()
job = Job.object.get(id=job_id)
# If we don't lock the tables two simultanous
# requests might both increase the counter
# going over 5
if job.counter < 5:
job.counter += 1
job.save()
finally:
Job.objects.unlock()
"""
def lock(self):
""" Lock table.
Locks the object model table so that atomic update is possible.
Simulatenous database access request pend until the lock is unlock()'ed.
Note: If you need to lock multiple tables, you need to do lock them
all in one SQL clause and this function is not enough. To avoid
dead lock, all tables must be locked in the same order.
See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
"""
cursor = connection.cursor()
table = self.model._meta.db_table
logger.debug("Locking table %s" % table)
cursor.execute("LOCK TABLES %s WRITE" % table)
row = cursor.fetchone()
return row
def unlock(self):
""" Unlock the table. """
cursor = connection.cursor()
table = self.model._meta.db_table
cursor.execute("UNLOCK TABLES")
row = cursor.fetchone(