SQL 命令 SELECT 从 Postgresql 数据库中获取未提交的数据

SQL command SELECT fetches uncommitted data from Postgresql database

简而言之: 我有 Postgresql 数据库,我通过 Python 的 psycopg2 模块连接到该数据库。这样的脚本可能如下所示:

import psycopg2

# connect to my database
conn = psycopg2.connect(dbname="<my-dbname>",
                        user="postgres",
                        password="<password>",
                        host="localhost",
                        port="5432")

cur = conn.cursor()

ins  = "insert into testtable (age, name) values (%s,%s);"
data = ("90", "George")

sel = "select * from testtable;"

cur.execute(sel)
print(cur.fetchall())
# prints out
# [(100, 'Paul')]
# 
# db looks like this
# age | name
# ----+-----
# 100 | Paul

# insert new data - no commit!
cur.execute(ins, data)
# perform the same select again
cur.execute(sel)
print(cur.fetchall())
# prints out
# [(100, 'Paul'),(90, 'George')]
#
#  db still looks the same
# age | name
# ----+-----
# 100 | Paul
cur.close()
conn.close()

也就是说,我连接到脚本开头的数据库如下所示:

age | name
----+-----
100 | Paul

我执行 SQL select 并仅检索 Paul 数据。然后我做 SQL 插入,但是没有任何提交,但是第二个 SQL select 仍然获取 PaulGeorge - 我不想要那个.我查看了 psycopg 和 Postgresql 文档,发现了 ISOLATION LEVEL (see Postgresql and see psycopg2)。在 Postgresql 文档中(在 13.2.1.Read Committed Isolation Level 下)它明确表示:

However, SELECT does see the effects of previous updates executed within its own transaction, even though they are not yet committed.

我已经尝试过不同的隔离级别,据我所知,Read CommittedRepeatable Read 不起作用,我认为 Serializable 可能有效,但它没有 - - 这意味着我仍然可以使用 select 获取未提交的数据。

我可以做 conn.set_isolation_level(0),其中 0 表示 psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT,或者我可以将 execute 命令包装在 with 语句中(see).

毕竟,我有点困惑,我是否了解事务和隔离(select 没有 commit 的行为是完全正常的)。有人能给我讲讲这个话题吗?

您的两个 SELECT 语句使用相同的连接,因此使用相同的事务。来自 the psycopg manual you linked:

By default, the first time a command is sent to the database ... a new transaction is created. The following database commands will be executed in the context of the same transaction.

因此,您的代码等同于以下内容:

BEGIN TRANSACTION;
select * from testtable;
insert into testtable (age, name) values (90, 'George');
select * from testtable;
ROLLBACK TRANSACTION;

隔离级别控制事务如何与其他事务交互。在事务中,您始终可以看到命令在该事务中的效果。

如果您想隔离代码的两个不同部分,您需要打开两个数据库连接,每个连接(除非您启用自动提交)都会创建一个单独的事务。

请注意,根据已链接的文档,创建新游标是不够的:

...not only the commands issued by the first cursor, but the ones issued by all the cursors created by the same connection

使用自动提交不会解决您的问题。当自动提交为 1 时,每次插入和更新都会自动提交到数据库,所有后续读取都将看到该数据。

不想看到您已写入数据库的数据是最不寻常的。但如果那是你想要的,你需要两个单独的连接,你必须确保你的 select 在提交之前执行。