PostgreSQL中CREATE INDEX CONCURRENTLY长期未完成的原因
CREATE INDEX CONCURRENTLY会挂起24小时? 结合你描述的场景——750万行的表、通过psql提交索引创建、pg_stat_activity中无相关记录,且此前曾成功创建过该索引——我来梳理下可能的原因:
前端会话意外终止,但后台索引创建仍在隐式执行
CREATE INDEX CONCURRENTLY分两个阶段扫描表,过程中如果你的psql会话因为网络中断、终端崩溃等意外退出,前端会看不到进程,但PostgreSQL可能还在后台默默推进剩下的步骤。你可以通过查询pg_stat_progress_create_index(PostgreSQL 12及以上版本支持)查看索引创建的实时进度,或者检查pg_locks中与目标表/索引相关的锁记录,确认是否有后台进程在持续工作。存在长期运行的只读事务
你提到没发现Idle in Transaction进程,但长时间运行的只读事务也会阻碍CREATE INDEX CONCURRENTLY的第二阶段——它需要等待所有在索引创建启动前开启的事务结束,才能确保没有旧快照会读取到未完成的索引。建议排查pg_stat_activity中事务启动时间较早的会话,哪怕这些会话处于活跃读状态,也可能拖慢索引创建。系统资源被耗尽
750万行的索引创建需要大量IO和内存资源:- 如果磁盘IO被其他任务(如批量写入、备份、大查询)占满,索引写入的速度会慢到近乎停滞;
- 若
work_mem配置不足,导致扫描或排序时频繁刷磁盘,也会大幅拖慢进程。
可以通过系统工具(如iostat、vmstat)查看IO和内存使用率,或者检查PostgreSQL日志中是否有资源相关的警告。
静默错误导致进程卡住
有时候索引创建过程中遇到非致命错误,但因为是后台执行,不会反馈到你的psql会话。比如磁盘空间不足但未触发崩溃、临时目录权限不足等。建议查看PostgreSQL的日志文件,搜索类似out of disk space、permission denied的关键词,排查是否有这类静默问题。PostgreSQL版本的已知bug
如果你使用的是较旧的PostgreSQL版本,某些特定场景下CREATE INDEX CONCURRENTLY存在挂起的bug——比如处理GIN/GiST索引、分区表(若你的表是分区表)时的并发问题。可以对比之前成功创建索引时的版本,或者查阅对应版本的Release Notes,确认是否有相关的bug修复记录。锁等待未被正确识别
虽然CREATE INDEX CONCURRENTLY仅需要SHARE UPDATE EXCLUSIVE锁,但如果有其他进程持有ACCESS EXCLUSIVE锁(比如ALTER TABLE、DROP TABLE操作),它会进入等待状态。有时候pg_stat_activity的等待事件可能未被清晰展示,建议查询pg_locks,过滤目标表和索引的锁记录,查看是否有长时间持有的冲突锁。
内容的提问来源于stack exchange,提问作者David A. Ventimiglia




