I have a table on PostgresSQL with millions of rows. The table has columns a and b. I already have a unique index on a and b:
CONSTRAINT a_b UNIQUE (a, b)
I want to add a composite primary key on columns a and b.
Will there be any downtime if I do a
ALTER TABLE table_name ADD PRIMARY KEY (a, b);
-
There is no real difference between your existing unique constraint and a primary key. So why do you think you need the primary key?user1822– user18222023年03月15日 21:40:11 +00:00Commented Mar 15, 2023 at 21:40
-
@a_horse_with_no_name I need it due to pg_logical replicationgkatzioura– gkatzioura2023年03月15日 21:58:55 +00:00Commented Mar 15, 2023 at 21:58
-
Hi, and welcome to dba.se! You could set up a little test to check times with a few thousand records and see what happens then? Don't forget to drop your constraint after you create the primary key!Vérace– Vérace2023年03月15日 22:38:58 +00:00Commented Mar 15, 2023 at 22:38
1 Answer 1
Will there be any downtime?
While CREATE INDEX
only blocks concurrent writes and still allows reads, you need ALTER TABLE
to add the PRIMARY KEY
constraint, and that takes an ACCESS EXCLUSIVE
lock on the table, blocking reads, too. So, yes, if the table is essential, I would call that "downtime".
However, there are options to alleviate the pain.
First, both index columns must be NOT NULL
. While a UNIQUE
constraint / index allows null
values, the PK does not. If that's not the case, fix it first. Else, the later ALTER TABLE
will do it for you, extending the downtime. The manual:
If [...] the index's columns are not already marked
NOT NULL
, then this command will attempt to doALTER COLUMN SET NOT NULL
against each such column. That requires a full table scan to verify the column(s) contain no nulls. In all other cases, this is a fast operation.
So, first (after fixing any null
values):
ALTER TABLE public.demo
ALTER COLUMN a SET NOT NULL
, ALTER COLUMN b SET NOT NULL;
Then, the question is: unique index or constraint? An index can be repurposed in a very fast "metadata only" operation. The same is not possible for a constraint, unfortunately. One might think it should be: it's still the same index. But it's just not implemented. Too rare a use-case that anyone would have bothered. You can probably hack the system catalogs. But I am not going there. One false move and you can break the entire database. There was a similar request in the past, and I didn't go there, either:
So, if it's a constraint, build a second, otherwise identical, unique index first. Use a temporary name, it will later be thrown out the window. We have the non-blocking CREATE INDEX CONCURRENTLY
for the task since Postgres 8.2 (almost forever now).
CREATE UNIQUE INDEX CONCURRENTLY a_b_temp ON demo (a,b);
Finally, drop the constraint, and add the PK building on the existing or new index:
ALTER TABLE public.demo
DROP CONSTRAINT a_b -- if it was a constraint
, ADD CONSTRAINT demo_pkey PRIMARY KEY USING INDEX a_b_temp; -- "a_b" if it was an index
This takes said ACCESS EXCLUSIVE
lock. But all the hard work has already been done, so it should be a matter of milliseconds - as soon as concurrent transactions let it take the lock.
Explore related questions
See similar questions with these tags.