在PostgreSQL 9.6

时间:2019-05-29 14:04:31

标签: postgresql

我正在尝试通过带有PostgreSQL 9.6的“同时创建索引”语句通过hikari创建索引 create语句被正在另一个表上工作的另一个事务阻止,并且事务状态为IIT(事务中的空闲)

  1. 代码通过hikari连接动态创建索引 池
  2. 所有操作(例如选择/创建索引等)只有一个连接池
  3. 这两个SQL在异步模式下以不同的数据库连接在同一线程中运行
  4. 使用“创建索引”而不是“同时创建索引”时,一切正常
  5. postgresql显示“在B上同时创建索引”(活动)被“ select * from A”(事务中的空闲状态)阻止了
  6. 我试图通过命令行重现该问题,所有事情都运行良好,只需打开2个窗口,执行“开始;从*中选择*;”在第一个窗口中,并尝试执行“在B上同时创建索引”;在第二个窗口中,按预期方式创建了索引,没有发生任何阻塞(我检查了第一个处于“ IIT”状态)
  7. 要在游标上使用fetch_size,select语句将在从池中获取连接时禁用自动提交,并通过hikari自身使用池全局设置将其设置回值,默认池设置为autocommit = true
  8. 2条语句在不同的表上工作,这2个表之间没有关系
  9. 取消“ IIT”语句后,“创建”语句继续按预期工作
wait_event_type |  pid  |        state        |    query                                                                                                                                                                              
Lock            | 25707 | active              | CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS "idx_tr-parameters__id_json" ON "tr-parameters" ((info->'_id') ASC)
                | 25701 | idle in transaction | SELECT t.info FROM "configuration-profiles" t

05-29 21:22:53.458 [vert.x-worker-thread-11] DEBUG com.calix.sxa.VertxPGVertice - SELECT t.info FROM "organizations" t HikariProxyConnection@379242839 wrapping org.postgresql.jdbc.PgConnection@645bae4d
05-29 21:22:53.529 [vert.x-worker-thread-11] DEBUG com.zaxxer.hikari.pool.PoolBase - hikari-cp-threads - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@645bae4d
05-29 21:22:53.533 [vert.x-worker-thread-11] DEBUG com.calix.sxa.VertxPGVertice - SELECT t.info FROM "configuration-profiles" t HikariProxyConnection@358392671 wrapping org.postgresql.jdbc.PgConnection@645bae4d
05-29 21:22:53.693 [vert.x-worker-thread-11] DEBUG com.calix.sxa.VertxPGVertice - SELECT t.info FROM "groups" t HikariProxyConnection@269112314 wrapping org.postgresql.jdbc.PgConnection@63822471
05-29 21:22:53.701 [vert.x-worker-thread-11] DEBUG com.zaxxer.hikari.pool.PoolBase - hikari-cp-threads - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@63822471
05-29 21:22:53.701 [vert.x-worker-thread-11] DEBUG com.calix.sxa.VertxPGVertice - SELECT t.info FROM "configuration-profiles" t WHERE COALESCE((t.info->'configurations'->'parameterValues')::jsonb ?? 'OUI_FilterList', false) = true HikariProxyConnection@1431456353 wrapping org.postgresql.jdbc.PgConnection@63822471
05-29 21:22:53.704 [vert.x-worker-thread-11] DEBUG com.zaxxer.hikari.pool.PoolBase - hikari-cp-threads - Reset (autoCommit) on connection org.postgresql.jdbc.PgConnection@63822471
05-29 21:22:53.712 [vert.x-worker-thread-11] DEBUG com.calix.sxa.VertxPGVertice - CREATE INDEX CONCURRENTLY IF NOT EXISTS "idx_tr-parameters__id_json" ON "tr-parameters" ((info->>'timestamp') ASC) HikariProxyConnection@454316525 wrapping org.postgresql.jdbc.PgConnection@63822471

  1. 我不知道块为何会发生,因为表完全不同,并且当使用2个命令行窗口进行手动测试时,所有工作都很好
  2. 如何解决此问题?有任何解决方法吗?

感谢您的帮助


如@jjanes所述,在并发建立索引的两次扫描期间,它被其他事务(同一表上的事务或具有快照的任何事务)阻止

官方文档还提到了[1]:https://www.postgresql.org/docs/9.1/sql-createindex.html#SQL-CREATEINDEX-CONCURRENTLY

After the second scan, the index build must wait for any transactions 
that have a snapshot (see Chapter 13) predating the second scan to terminate

就我而言

wait_event_type |  pid  |        state        | backend_xid | backend_xmin | query
----------------+-------+---------------------+-------------+--------------+--------------------------------------------------
                |  5226 | idle in transaction |             |      7973432 | select * from "configuration-profiles"

backend_xmin是IIT的7973432,然后IIT使用快照阻止“并发创建索引”

顺便说一句,当使用隔离级别为“已读读”的命令行时,“并发创建索引”不会被阻止,但是对于Java代码,“ create”操作也会被相同的隔离级别阻止,

wait_event_type |  pid  |        state        | backend_xid | backend_xmin | query
----------------+-------+---------------------+-------------+--------------+--------------------------------------------------
                |  5226 | idle in transaction |             |      7973432 | select * from "configuration-profiles"
                |  5210 | idle in transaction |             |              | select * from "configuration-profiles";
                |  5455 | idle in transaction |             |      7973432 | declare cur cursor for select * from "configuration-profiles";

如上所示,

  1. 使用“从“配置文件”中选择*;”在命令行中,因为没有打开游标,所以没有backend_xmin,执行该语句后应返回所有记录
  2. 使用“从“配置文件”中声明用于选择*的当前光标;”在命令行中,自从游标打开并等待查询以来,backend_xmin就具有值
  3. 通过Java使用“从“配置文件”中选择*”,backend_xmin也具有价值,因为lib也使用游标

1 个答案:

答案 0 :(得分:1)

PostgreSQL无法知道其他连接将不希望使用在快照的将来某个时刻建立索引的表。仅仅因为它尚未使用过表,并不意味着它永远不会使用。肯定知道的方法是等待该事务(或快照)完成,这就是它的作用。

  

这两个SQL在异步模式下以不同的数据库连接在同一线程中运行

为什么另一个连接是IIT?还等什么呢? (它在代码中等待,不在数据库中)。由于它是异步的,因此它不应该等待CIC。它只是在等待您发出COMMIT吗?由于您关闭了自动提交,因此您有责任在适当的时候发出COMMIT。如果您使用的是更高的隔离级别,则即使仅SELECT语句也需要提交。

  

我试图通过命令行重现该问题,所有事情都运行良好,只需打开2个窗口,执行“开始;从A中选择*;”在第一个窗口中,并尝试执行“在B上同时创建索引”;在第二个窗口中,

您可以通过将第一个更改为begin isolation level repeatable read; select * from A;

解决方法:

  1. 不要在公开交易中闲逛
  2. 不要使用比您需要的更高的隔离级别。
  3. 不要使用CIC。