Concurrent / Online Index Build
Build a new index without taking an exclusive lock on the table, allowing reads and writes to continue during index creation.
CREATE INDEX CONCURRENTLY CONCURRENTLY online index build non-blocking index ONLINE = ON ALGORITHM=INPLACE
index concurrent online non-blocking lock maintenance
| Database | Status | Syntax Family | Min Version |
|---|---|---|---|
| PostgreSQL | full | concurrently | 8.2 |
| MySQL | partial | online-ddl | 5.6 |
| MariaDB | partial | online-ddl | - |
| Oracle | partial | online-index | - |
| SQL Server | partial | online-index | 2005 |
| SQLite | none | - | - |
| Redshift | none | - | - |
| DB2 | partial | online-index | - |
| Snowflake | full | - | - |
| BigQuery | full | - | - |
Compare Databases
Database Details
PostgreSQL
full
-- Build an index without locking writes
CREATE INDEX CONCURRENTLY idx_users_email ON users (email);
-- Concurrent unique index
CREATE UNIQUE INDEX CONCURRENTLY idx_users_unique_email ON users (email);
-- Concurrent partial index
CREATE INDEX CONCURRENTLY idx_active_users ON users (email) WHERE is_active = true;
-- Rebuild an existing index concurrently (PostgreSQL 12+)
REINDEX INDEX CONCURRENTLY idx_users_email;
-- If a concurrent build is aborted, an INVALID index remains - drop it:
SELECT indexname, indexdef
FROM pg_indexes
JOIN pg_index ON pg_index.indexrelid = (SELECT oid FROM pg_class WHERE relname = indexname)
WHERE tablename = 'users'
AND NOT pg_index.indisvalid;
-- Drop the invalid index and retry
DROP INDEX CONCURRENTLY idx_users_email;- CREATE INDEX CONCURRENTLY performs two table scans and waits for existing transactions to finish between passes, making it slower than a regular CREATE INDEX.
- Cannot be run inside a transaction block - it must be a standalone statement.
- If the build is aborted (e.g., due to a unique constraint violation or cancelled), an INVALID index is left in the catalog and must be dropped manually before retrying.
- REINDEX CONCURRENTLY (PostgreSQL 12+) rebuilds an existing index non-disruptively.
- On a physical replica, concurrent index builds require wal_level = logical on the primary.
MySQL
partial
-- Online DDL: create index with no DML lock (default for most operations)
CREATE INDEX idx_email ON users (email)
ALGORITHM=INPLACE, LOCK=NONE;
-- Explicit online index creation
ALTER TABLE users ADD INDEX idx_email (email),
ALGORITHM=INPLACE, LOCK=NONE;
-- Check what lock level an operation will require:
ALTER TABLE users ADD INDEX idx_email (email),
ALGORITHM=INPLACE, LOCK=NONE; -- fails if LOCK=NONE is not supported
-- Monitor online DDL progress
SELECT event_name, work_completed, work_estimated
FROM performance_schema.events_stages_current
WHERE event_name LIKE 'stage/innodb/alter%';- InnoDB online DDL (introduced in MySQL 5.6) allows most index creation operations with ALGORITHM=INPLACE, LOCK=NONE.
- ALGORITHM=INPLACE rebuilds the index in-place without copying the table; LOCK=NONE allows concurrent DML.
- Some operations still require LOCK=SHARED or LOCK=EXCLUSIVE (e.g., adding a primary key to a table without one).
- MySQL does not have an explicit CONCURRENTLY keyword; online behavior is controlled by ALGORITHM and LOCK clauses.
- Performance Schema events_stages_current tracks in-progress online DDL operations.
MariaDB
partial
-- Online index creation (InnoDB)
ALTER TABLE users ADD INDEX idx_email (email),
ALGORITHM=INPLACE, LOCK=NONE;
-- Create index statement form
CREATE INDEX idx_email ON users (email)
ALGORITHM=INPLACE, LOCK=NONE;
-- Check if the operation supports LOCK=NONE before running
-- (omit it to let MariaDB choose, or use LOCK=DEFAULT)- InnoDB online DDL works the same as MySQL - most index creation operations support ALGORITHM=INPLACE, LOCK=NONE.
- Aria storage engine tables require metadata locks during index operations.
- Some operations (e.g., adding a primary key) may still require a table rebuild.
- MariaDB 10.0+ supports most online DDL operations that InnoDB in MySQL 5.6+ supports.
Docs: InnoDB Online DDL, ALTER TABLE
Oracle
partial
-- Build an index online (Enterprise Edition)
CREATE INDEX idx_email ON users (email) ONLINE;
-- Rebuild an existing index online
ALTER INDEX idx_email REBUILD ONLINE;
-- Monitor index build progress
SELECT opname, target, sofar, totalwork, units,
ROUND(sofar/NULLIF(totalwork,0)*100, 1) AS pct_done
FROM v$session_longops
WHERE opname LIKE 'Create Index%'
AND sofar < totalwork;- The ONLINE keyword allows DML operations to continue during index creation on Oracle Enterprise Edition.
- Available since Oracle 8i for regular B-Tree indexes; not available for bitmap indexes, domain indexes (Text, Spatial), or some clustered tables.
- Online index build is slower than offline because it must track DML changes during the build.
- Not available in Oracle Standard Edition.
- ALTER INDEX ... REBUILD ONLINE rebuilds an existing index without downtime.
Docs: Creating Indexes, CREATE INDEX
SQL Server
partial
-- Online index creation (Enterprise Edition)
CREATE INDEX idx_email ON users (email)
WITH (ONLINE = ON);
-- Online index rebuild
ALTER INDEX idx_email ON users REBUILD WITH (ONLINE = ON);
-- Resumable online index build (SQL Server 2017+)
CREATE INDEX idx_email ON users (email)
WITH (ONLINE = ON, RESUMABLE = ON, MAX_DURATION = 60 MINUTES);
-- Pause a resumable index operation
ALTER INDEX idx_email ON users PAUSE;
-- Resume a paused index operation
ALTER INDEX idx_email ON users RESUME WITH (MAX_DURATION = 120 MINUTES);
-- Check progress of online index operations
SELECT * FROM sys.index_resumable_operations;- ONLINE = ON requires Enterprise Edition or Developer Edition (for testing).
- ONLINE = ON allows reads and writes to continue during index creation and rebuild, using a versioning mechanism.
- RESUMABLE = ON (SQL Server 2017+) allows pausing and resuming long-running index operations, useful for maintenance windows.
- Online index builds are slower than offline builds and use more resources.
- sys.index_resumable_operations shows paused resumable index builds.
- Azure SQL Database supports ONLINE = ON for all service tiers.
SQLite
none
-- SQLite requires a write lock during index creation.
-- In WAL mode, readers are not blocked but writers are:
PRAGMA journal_mode=WAL;
-- CREATE INDEX acquires a write transaction:
CREATE INDEX idx_email ON users (email);
-- There is no CONCURRENTLY or ONLINE keyword available.- SQLite acquires a database-level write lock during CREATE INDEX, blocking concurrent writers.
- In WAL (Write-Ahead Logging) mode, readers are not blocked by a write transaction, but concurrent writers are still serialized.
- SQLite is designed for embedded use cases; long-running concurrent workloads are not its primary target.
- For production systems with heavy write concurrency, a client-server database is recommended.
Redshift
none
-- Redshift has no user-defined indexes to build.
-- Sort key changes require a table rebuild (VACUUM FULL or ALTER TABLE):
ALTER TABLE orders ALTER SORTKEY (customer_id, order_date);
-- VACUUM SORT ONLY re-sorts data to match the sort key:
VACUUM SORT ONLY orders;- Redshift has no user-managed B-Tree indexes, so concurrent index building is not applicable.
- Changing sort keys on a large table requires a full table re-sort via VACUUM or ALTER TABLE, which can be disruptive.
- Automatic VACUUM in Redshift handles sort and delete reclaim in the background.
Docs: VACUUM
DB2
partial
-- Allow write access during index creation
CREATE INDEX idx_email ON users (email)
ALLOW WRITE ACCESS;
-- Allow read-only access during index creation (more restrictive)
CREATE INDEX idx_email ON users (email)
ALLOW READ ACCESS;
-- Rebuild index online
REORG INDEXES ALL FOR TABLE users ALLOW WRITE ACCESS;- ALLOW WRITE ACCESS permits DML operations to continue during index creation.
- ALLOW READ ACCESS permits read-only queries during index creation but blocks writes.
- Online index builds are supported in DB2 LUW but availability of specific options may vary by edition.
- REORG INDEXES rebuilds all indexes on a table and supports ALLOW WRITE ACCESS for online operation.
Snowflake
full
-- Snowflake has no user-managed index builds.
-- Cluster key changes are applied asynchronously in the background:
ALTER TABLE orders CLUSTER BY (customer_id, order_date);
-- Clustering is applied asynchronously without blocking DML:
SELECT SYSTEM$CLUSTERING_INFORMATION('orders', '(customer_id, order_date)');
-- Search Optimization is also a background operation:
ALTER TABLE orders ADD SEARCH OPTIMIZATION;- Snowflake has no traditional index build operations that could block reads or writes.
- Cluster key application and Search Optimization indexing are background processes that do not block DML.
- All Snowflake DDL operations are metadata-only and instantaneous from the user's perspective.
- Multi-cluster warehouses handle concurrent queries without locking.
BigQuery
full
-- BigQuery DDL is metadata-only and non-blocking.
-- Search Index creation is a background managed operation:
CREATE SEARCH INDEX idx ON mydataset.orders (ALL COLUMNS);
-- Check Search Index status
SELECT * FROM mydataset.INFORMATION_SCHEMA.SEARCH_INDEXES
WHERE table_name = 'orders';- BigQuery is serverless - there are no table-level locks from the user's perspective.
- Search Index creation is managed by BigQuery in the background and does not block queries or DML.
- Table creation (CREATE TABLE AS SELECT) runs as a background job; queries against the table can proceed once it completes.
- Clustering and partitioning are applied at write time and do not require a separate index build step.