我试图了解SSI在Postgres中的实际行为。我的理解是,如果有两个事务与同一个表交互,但这些事务没有与表中的相同行交互,那么也不会出现任何例外。
但是,我正在运行以下测试,其中事务一执行以下操作:
cur = engine.cursor()
cur.execute('SELECT SUM(value) FROM mytab WHERE class = 1')
s = cur.fetchall()[0][0]
print('retrieved sum is...')
print(s)
print('sleeping....')
time.sleep(10)
cur.execute('INSERT INTO mytab (class, value) VALUES (%s, %s)', (1, s))
engine.commit()当上面的第一个事务处于休眠状态时,我运行第二个事务:
cur = engine.cursor()
cur.execute('SELECT SUM(value) FROM mytab WHERE class = 2')
s = cur.fetchall()[0][0]
print('retrieved sum is...')
print(s)
cur.execute('INSERT INTO mytab (class, value) VALUES (%s, %s)', (2, s))
engine.commit()在这种情况下,第二个事务只接触类= 2的行,而第一个事务只接触类= 1的行。但是,这会导致第一个事务失败,但有以下例外:
could not serialize access due to read/write dependencies among transactions
DETAIL: Reason code: Canceled on identification as a pivot, during write.
HINT: The transaction might succeed if retried.作为参考,mytab非常简单,如下所示:
class value
1 10
1 20
2 100
2 200除了标准的engine = psycopg2.connect设置之外,在运行上面的代码之前,我还将使用这一行设置事务隔离级别:
engine.set_isolation_level(psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)发布于 2019-09-05 01:23:46
您的理解几乎是正确的,但是SSI算法并不完美,因此总是存在一些错误的风险(例如,正如文档中所指出的,行锁可能被组合成一个页面锁,从而以牺牲精度为代价优化内存)。
这里的行为是谓词锁定实现的一个限制,即:
对于表扫描,整个关系将被锁定。
基本上,在运行第一个查询WHERE class = 1之后,需要检查来自其他事务的未来插入,以确定如果它们是可见的,它们是否满足此条件。实际上,除了最简单的条件外,执行此检查是不实际的或不可能的,因此为了谨慎起见,应该在整个表上使用谓词锁。
细粒度的谓词锁实现是基于索引,因为从B树范围的角度来考虑关系的受影响子集比使用任意WHERE约束要容易得多。
换句话说,如果您在class列上有一个索引--并且您的表中有足够的记录供规划者实际使用--您应该得到您期望的行为。
https://stackoverflow.com/questions/57776922
复制相似问题