PostgreSQL information_schema.tables 和事务隔离级别

PostgreSQL information_schema.tables and TRANSACTION ISOLATION LEVEL

在 PostgreSQL 中,我面临竞争条件。我的 table 和架构可能会被系统中的单独进程删除。使用成语 if schema and table exists, then read the contents however does not generally work, because the table may step exist exist in the middle of the statement .

有一件事我不明白为什么 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 没有帮助。我想我可能期望在我的交易过程中对模式和 tables 有一致的看法,但我没有。下面是我的 Java 代码:

pgConnection = DriverManager.getConnection(/* ... */);
pgConnection.setAutoCommit(false);

PreparedStatement statement = pgConnection.prepareStatement(
                                 "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;");
statement.execute();

statement = pgConnection.prepareStatement(
               "SELECT ('myschema','config') IN " +
               "(SELECT table_schema,table_name FROM information_schema.tables);");

ResultSet result = statement.executeQuery();
result.next();
if(result.getBoolean(1)) {
    statement = pgConnection.prepareStatement("SELECT key,value FROM myschema.config;");

    result = statement.executeQuery();  // here I'm often getting an exception

    /* ... */
}

我遇到的异常是:

org.postgresql.util.PSQLException: ERROR: relation "myschema.config" does not exist

这怎么可能?我以为 ISOLATION LEVEL SERIALIZABLE 会保护我免受这种情况的影响。那是因为删除模式的操作过于具体而无法保持隔离吗?还是我做错了什么?

SQL 语句 set transaction isolation level . . . 没有启动事务。 (无论如何,不​​是你感兴趣的那种感觉。)

按照您的代码,您可以像这样编写 SQL 语句

set transaction isolation level serializable;
begin transaction;
...

或者像这样。

begin transaction isolation level serializable;
...

Relevant PostgreSQL docs

但是您不需要为此使用可序列化的事务。您可以在两个终端会话中通过 运行 psql 进行测试。

sandbox=# begin transaction;
BEGIN
                                          sandbox=# begin transaction;
                                          BEGIN

sandbox=# select * from foo for update;
 foo_id 
--------
      1
(1 row)
                                          sandbox=# drop table foo;
                                          [waits . . .]
sandbox=# update foo set foo_id = 2;
UPDATE 1
sandbox=# select * from foo;
 foo_id 
--------
      2
(1 row)

sandbox=# commit;
COMMIT
                                          DROP TABLE
                                          sandbox=# commit;
                                          COMMIT

sandbox=# select * from foo;
ERROR:  relation "foo" does not exist
LINE 1: select * from foo;

话虽如此,设计一个表和模式可能 定期 被删除和(我想)创建的数据库似乎是个坏主意。