Use Unique Indexes on AO Tables
You can add a unique index to an Append-Optimized (AO) or AOCS table in SynxDB. With a unique index, SynxDB enforces uniqueness constraints when inserting data into an AO table, thereby ensuring data uniqueness. This also allows the optimizer to optimize specific queries, improving database query performance. However, this comes with some overhead for maintaining the unique index, especially during data insertion.
How it works
In scenarios where a unique index exists, when inserting data into an AO table, SynxDB inserts a placeholder into the BlockDirectory, which is an auxiliary index structure of the AO table. This blocks the insertion of the same key, thus implementing the unique index.
How to add a unique index to an AO table
There are two ways to add a unique index to an AO table. You can choose based on your business needs:
Specify a unique key or constraint when creating the table:
CREATE TABLE foo(i int UNIQUE) USING ao_row; CREATE TABLE bar(i int UNIQUE) USING ao_column; CREATE TABLE foo2(i int, CONSTRAINT iuniq UNIQUE(i)) USING ao_row;
Add a unique index or constraint separately after creating the table:
CREATE TABLE baz(i int) with (appendonly=true); CREATE UNIQUE INDEX on baz(i); CREATE TABLE foobar(i int) USING ao_row; ALTER TABLE foobar ADD CONSTRAINT unique_i UNIQUE (i);
Usage examples
Create an example AO table with a unique constraint.
postgres=# CREATE TABLE foo(i int UNIQUE) USING ao_row; CREATE TABLE postgres=# \d foo Table "public.foo" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- i | integer | | | Compression Type: None Compression Level: 0 Block Size: 32768 Checksum: t Indexes: "foo_i_key" UNIQUE CONSTRAINT, btree (i) Distributed by: (i)
Inserting the same key value into the table will return a
duplicate keyerror.postgres=# INSERT INTO foo VALUES(1); INSERT 0 1 postgres=# INSERT INTO foo VALUES(1); ERROR: duplicate key value violates unique constraint "foo_i_key" (seg1 127.0.1.1:8003 pid=557) DETAIL: Key (i)=(1) already exists.
Example of concurrent data insertion: At the READ COMMITTED transaction level, two transactions concurrently insert the same value into the same AO table. One succeeds, and the other is blocked.
The transaction that starts later does not fail immediately. Its INSERT waits until the earlier transaction commits or rolls back before returning a result. The error that session 2 reports in the following example therefore appears only after session 1 has finished.
If one transaction succeeds, the other insertion fails.
-- Session 1. postgres=# BEGIN; BEGIN postgres=*# INSERT INTO foo VALUES(1); INSERT 0 1 -- Insertion successful postgres=*# COMMIT; COMMIT
-- Session 2. postgres=# BEGIN; BEGIN postgres=*# INSERT INTO foo VALUES(1); ERROR: duplicate key value violates unique constraint "foo_i_key" (seg1 127.0.1.1:8003 pid=2726) DETAIL: Key (i)=(1) already exists. -- Insertion failed postgres=!# END; ROLLBACK
If one transaction fails, the other transaction will succeed.
postgres=# BEGIN; BEGIN postgres=*# INSERT INTO foo VALUES(1); INSERT 0 1 postgres=*# ROLLBACK; ROLLBACK
postgres=# BEGIN; BEGIN postgres=*# INSERT INTO foo VALUES(1); INSERT 0 1 postgres=*# COMMIT; COMMIT
Usage restrictions
Note the following restrictions when using a unique index on an AO table:
A unique index or constraint must contain all the distribution key columns of the table. Creating an index that omits the distribution key fails:
CREATE TABLE u1(a int, b int) USING ao_row DISTRIBUTED BY (a); CREATE UNIQUE INDEX ON u1(b); ERROR: UNIQUE index must contain all columns in the table's distribution key DETAIL: Distribution key column "a" is not included in the constraint.
Including the distribution key in the index resolves it:
CREATE UNIQUE INDEX ON u1(a, b);
When a
UNIQUEconstraint and aDISTRIBUTED BYclause are both given at table creation, the distribution key must be a subset of the unique constraint columns:CREATE TABLE u2(a int, b int UNIQUE) USING ao_row DISTRIBUTED BY (a); ERROR: UNIQUE constraint and DISTRIBUTED BY definitions are incompatible HINT: When there is both a UNIQUE constraint and a DISTRIBUTED BY clause, the DISTRIBUTED BY clause must be a subset of the UNIQUE constraint.
A randomly distributed table (
DISTRIBUTED RANDOMLY) cannot have a unique index:CREATE TABLE u3(a int) USING ao_row DISTRIBUTED RANDOMLY; CREATE UNIQUE INDEX ON u3(a); ERROR: UNIQUE and DISTRIBUTED RANDOMLY are incompatible
Uniqueness is enforced on
UPDATEas well. Changing the unique column of a row to an existing value raises the same error:UPDATE bar SET i = 2 WHERE i = 1; ERROR: duplicate key value violates unique constraint "bar_i_key" DETAIL: Key (i)=(2) already exists.