Python Innodb pop队列

Python Innodb pop队列,python,mysql,django,innodb,deadlock,Python,Mysql,Django,Innodb,Deadlock,在多进程环境中,我对以下Python/Django代码有一个问题。它尝试更新InnoDB表,就像它是一个队列一样,弹出最大值。它可以正常工作,直到负载达到某个级别,并且此代码中同时包含多个进程。当一个进程持有要删除的表,而另一个进程试图更新该表时,就会发生死锁 @classmethod @transaction.commit_manually def pop(cls, unique_id): """Retrieves the next item and removes it from t

在多进程环境中,我对以下Python/Django代码有一个问题。它尝试更新InnoDB表,就像它是一个队列一样,弹出最大值。它可以正常工作,直到负载达到某个级别,并且此代码中同时包含多个进程。当一个进程持有要删除的表,而另一个进程试图更新该表时,就会发生死锁

@classmethod
@transaction.commit_manually
def pop(cls, unique_id):
    """Retrieves the next item and removes it from the queue."""
    transaction.commit()    # This has the side effect of clearing the object cache

    if cls.objects.all().filter(unique_id__isnull=True).count() == 0:  return None

    sql = "UPDATE queue SET unique_id=%d" % unique_id
    sql += " WHERE unique_id IS NULL"
    sql += " ORDER BY id LIMIT 1"
    cursor = connection.cursor()
    try:
        cursor.execute(sql)
        row = cursor.fetchone()
    except OperationalError, oe:    # deadlock, retry later
        transaction.rollback()
        return None
    # If an item is available, it is now marked with our process_id.

    # Retrieve any items with this process id
    items = cls.objects.all().filter(process_id=process_id)
    if len(items) == 0:     # No items available
        transaction.rollback()
        return None
    top = items[0]      # there should only be one item
    # ... perform some other actions here ...
    top.delete()        # This item is deleted from the queue
    transaction.commit()
    return item
关于InnoDB,我以前不知道的是索引与表是分开的,因此一个进程可以锁定表并等待对索引的锁定,而另一个进程则相反,从而产生死锁。下面代码中的更新/删除死锁似乎就是这样

简而言之,代码首先更新表中的单行限制1,防止任何其他进程更新同一行。然后选择该行以检索其他内容,执行一些工作,然后从表中删除该行

@classmethod
@transaction.commit_manually
def pop(cls, unique_id):
    """Retrieves the next item and removes it from the queue."""
    transaction.commit()    # This has the side effect of clearing the object cache

    if cls.objects.all().filter(unique_id__isnull=True).count() == 0:  return None

    sql = "UPDATE queue SET unique_id=%d" % unique_id
    sql += " WHERE unique_id IS NULL"
    sql += " ORDER BY id LIMIT 1"
    cursor = connection.cursor()
    try:
        cursor.execute(sql)
        row = cursor.fetchone()
    except OperationalError, oe:    # deadlock, retry later
        transaction.rollback()
        return None
    # If an item is available, it is now marked with our process_id.

    # Retrieve any items with this process id
    items = cls.objects.all().filter(process_id=process_id)
    if len(items) == 0:     # No items available
        transaction.rollback()
        return None
    top = items[0]      # there should only be one item
    # ... perform some other actions here ...
    top.delete()        # This item is deleted from the queue
    transaction.commit()
    return item
如上所述,当两个进程同时运行相同的代码时,就会发生死锁。当另一个进程正在执行更新查询时,一个进程正在尝试执行top.delete。InnoDB行级锁定不会阻止第二个进程尝试更新此处的表。以下是显示引擎innodb状态的输出;:


我的问题是:在Python/Django和InnoDB中实现队列和执行pop操作的正确方法是什么?特别是,需要对此代码进行哪些更改?

不确定为什么会发生这种情况,但您是否尝试过使用更新队列q JOIN SELECT MINid AS id from queue WHERE unique\u id为NULL qi ON qi.id=q.id SET q.unique\u id=%d这样的方法从更新中删除限制;在唯一的id上有一个索引,所以锁定是最小的?还没有尝试过。目的是锁定表和索引中的一行,使其不与DELETE查询重叠吗?这是在更新中执行单行锁定的唯一方法吗?我不确定这是问题所在,还是您处理事务的方式以及更新在会话中的可见性。我建议您标记这个问题,并要求迁移到DBA.SE,在那里会有更多的人研究这个问题。