CREATE INDEX thường lấy khoá SHARE trên bảng — chặn mọi INSERT/UPDATE/DELETE cho tới khi index dựng xong. Với bảng lớn việc này kéo dài hàng chục phút, tương đương ngừng ghi.
Dùng CONCURRENTLY:
CREATE INDEX CONCURRENTLY idx_orders_created_at ON orders (created_at);Cách này quét bảng hai lượt và chờ các transaction cũ kết thúc, nên chậm hơn nhưng không chặn ghi.
Những ràng buộc phải nhớ:
- Không chạy được trong transaction block. Nhiều công cụ migration (Rails, Django, Prisma) mặc định bọc migration trong transaction — phải tắt tường minh cho bước này.
- Có thể thất bại giữa chừng và để lại index ở trạng thái INVALID. Index hỏng vẫn tốn chi phí ghi nhưng không được planner dùng. Kiểm tra rồi dọn:
SELECT indexrelid::regclass FROM pg_index WHERE NOT indisvalid;
DROP INDEX CONCURRENTLY idx_orders_created_at;- Bị chặn bởi transaction dài đang mở. Trước khi chạy nên kiểm tra
pg_stat_activityđể chắc không có transaction treo. DROP INDEXcũng có biến thểCONCURRENTLY, vàREINDEX CONCURRENTLYcó từ PostgreSQL 12.
Cùng nguyên tắc áp dụng cho unique constraint: dựng CREATE UNIQUE INDEX CONCURRENTLY trước, rồi ALTER TABLE ... ADD CONSTRAINT ... USING INDEX — bước sau chỉ khoá trong chốc lát.