Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commiteddd71a

Browse files
committed
Merge branch 'main' into 'main'
Days 68-70See merge request postgres-ai/postgresql-consulting/postgres-howtos!26
2 parentsc726a7a +881ea43 commiteddd71a

File tree

4 files changed

+246
-0
lines changed

4 files changed

+246
-0
lines changed

‎0068_psql_shortcuts.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
Originally from:[tweet](https://twitter.com/samokhvalov/status/1731340441403215959),[LinkedIn post]().
2+
3+
---
4+
5+
#psql shortcuts
6+
7+
>I post a new PostgreSQL "howto" article every day. Join me in this
8+
>journey –[subscribe](https://twitter.com/samokhvalov/), provide feedback, share!
9+
10+
In my tool[postgres_dba](https://github.com/NikolayS/postgres_dba/), the setup instructions have this:
11+
12+
```bash
13+
printf"%s %s %s %s\n"\\set dba\'\\\\i$(pwd)/start.psql\'>>~/.psqlrc
14+
```
15+
16+
This provides a way to call`start.psql` by just typing`:dba` in psql, and this line works in both bash and zsh (which
17+
is good because macOS switched to zsh as the default shell a few years ago, while bash is usually the default shell on
18+
Linux distros).
19+
20+
One can easily add their own instructions to`.psqlrc`, defining various handy shortcuts. For example:
21+
22+
```sql
23+
\set pid'select pg_backend_pid() as pid;'
24+
\set prim'select not pg_is_in_recovery() as is_primary;'
25+
\set a'select state, count(*) from pg_stat_activity where pid <> pg_backend_pid() group by 1 order by 1;'
26+
```
27+
28+
👉 This adds simple shortcuts:
29+
30+
1) Current PID:
31+
32+
```sql
33+
nik=# :pid
34+
pid
35+
--------
36+
513553
37+
(1 row)
38+
```
39+
40+
2) Is this not a primary?
41+
42+
```sql
43+
nik=# :prim
44+
is_primary
45+
------------
46+
t
47+
(1 row)
48+
```
49+
50+
3) Simple activity summary
51+
52+
```sql
53+
nik=# :a
54+
state | count
55+
---------------------+-------
56+
active | 19
57+
idle | 193
58+
idle in transaction | 2
59+
| 7
60+
(4 rows)
61+
```
62+
63+
The value of a currentlyset psql variable can be passedin an SQL contextas a string using this interesting syntax:
64+
65+
```sql
66+
nik=# select :'pid';
67+
?column?
68+
---------------------------------
69+
select pg_backend_pid() as pid;
70+
(1 row)
71+
```
72+
73+
It is important not to forget to use the option`-X` (` --no-psqlrc`)in scripting, so nothing that you putin`.psqlrc`
74+
would affect the logic of your scripts.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
Originally from:[tweet](https://twitter.com/samokhvalov/status/1731707060193312953),[LinkedIn post]().
2+
3+
---
4+
5+
#How to add a CHECK constraint without downtime
6+
7+
>I post a new PostgreSQL "howto" article every day. Join me in this
8+
>journey –[subscribe](https://twitter.com/samokhvalov/), provide feedback, share!
9+
10+
Adding`CHECK` constraints can be helpful to:
11+
12+
- maintain better data quality
13+
- define a`NOT NULL` constraint without downtime in PG12+
14+
(more[here](0060_how_to_add_a_column.md?ref_type=heads#not-null))
15+
16+
To add a`CHECK` constraint without downtime, we need:
17+
18+
1. Quickly define a constraint with the flag`NOT VALID`
19+
2. In a separate transaction, "validate" the constraint for existing rows.
20+
21+
##Adding CHECK with NOT VALID
22+
23+
Example:
24+
25+
```sql
26+
altertable t
27+
addconstraint c_id_is_positive
28+
check (id>0) not valid;
29+
```
30+
31+
This requires a very brief`AccessExclusiveLock` lock, so on loaded systems, the command has to be executed with low
32+
`lock_timeout` and retries (read:
33+
[Zero-downtime Postgres schema migrations need this: lock_timeout and retries](https://postgres.ai/blog/20210923-zero-downtime-postgres-schema-migrations-lock-timeout-and-retries)).
34+
35+
🖋️**Important:** Once the constraint with`NOT VALID` is in place, the new writes are checked (while old rows have not
36+
been yet verified and some of them might violate the constraint):
37+
38+
```sql
39+
nik=# insert into t select -1;
40+
ERROR: new row for relation"t" violatescheckconstraint"c_id_is_positive"
41+
DETAIL: Failing row contains (-1).
42+
```
43+
44+
##Validation
45+
46+
To complete the process, we need to validate the old rows:
47+
48+
```sql
49+
altertable t
50+
validateconstraint c_id_is_positive;
51+
```
52+
53+
This scans whole table, so for a large table, it takes long time – but this query only
54+
acquires`ShareUpdateExclusiveLock` on the table, not blocking the sessions that run DML queries. However, a lock
55+
acquisition attempt is going to be blocked if there is`autovacuum` running in the transaction ID wraparound prevention
56+
mode and processing the table, or if there is another session that builds an index on this table or performs
57+
another`ALTER` – so we need to make sure none of these heavier operations are happening before we run our`ALTER`, to
58+
avoid excessive wait time.

‎0070_how_to_add_a_foreign_key.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
Originally from:[tweet](https://twitter.com/samokhvalov/status/1732056107483636188),[LinkedIn post]().
2+
3+
---
4+
5+
#How to add a foreign key
6+
7+
>I post a new PostgreSQL "howto" article every day. Join me in this
8+
>journey –[subscribe](https://twitter.com/samokhvalov/), provide feedback, share!
9+
10+
Adding a foreign key (FK) is straightforward:
11+
12+
```sql
13+
altertable messages
14+
addconstraint fk_messages_users
15+
foreign key (user_id)
16+
references users(id);
17+
```
18+
19+
However, this operation requires locks on both tables involved:
20+
21+
1.`ShareRowExclusiveLock`,`RowShareLock`, and`AccessShareLock` on the referenced table, in this example it's
22+
`users` (plus`AccessShareLock` on its primary key, PK). This blocks any data modifications to`users` (`UPDATE`,
23+
`DELETE`,`INSERT`), as well as DDL.
24+
25+
2.`ShareRowExclusiveLock` and`AccessShareLock` to the referencing table, in this example`messages`
26+
(plus,`AccessShareLock` to its PK). Again, this blocks writes to this table, and DDL.
27+
28+
And to ensure that the existing data doesn't violate the constraint, full table scans are needed – so the more data the
29+
tables have, the longer this implicit scan is going to take. During which, the locks are going to block all writes and
30+
DDL to the table.
31+
32+
To avoid downtime, we need to create the FK in three steps:
33+
34+
1. Quickly define a constraint with the flag`NOT VALID`.
35+
2. For the existing data, if needed, fix rows that would break the FK.
36+
3. In a separate transaction,`validate` the constraint for existing rows.
37+
38+
##Step 1: Add FK with NOT VALID
39+
40+
Example:
41+
42+
```sql
43+
altertable messages
44+
addconstraint fk_messages_users
45+
foreign key (user_id)
46+
references users(id)
47+
not valid;
48+
```
49+
50+
This requires a very brief`ShareRowExclusiveLock` and`AccessShareLock` on both tables, so on loaded systems, it is
51+
still recommended to execute this with low`lock_timeout` and retries (read:
52+
[Zero-downtime database schema migrations](https://postgres.ai/blog/20210923-zero-downtime-postgres-schema-migrations-lock-timeout-and-retries)),
53+
to avoid lock queue blocking writes to the tables.
54+
55+
🖋️**Important:** once the constraint with`NOT VALID` is in place, new writes are checked (while old rows have not
56+
been yet verified and some of them might violate the constraint):
57+
58+
```sql
59+
nik=# \d messages
60+
Table"public.messages"
61+
Column | Type | Collation | Nullable | Default
62+
---------+--------+-----------+----------+---------
63+
id |bigint | |not null |
64+
user_id |bigint | | |
65+
Indexes:
66+
"messages_pkey"PRIMARY KEY, btree (id)
67+
Foreign-key constraints:
68+
"fk_messages_users"FOREIGN KEY (user_id)REFERENCES users(id) NOT VALID
69+
70+
nik=# insert into messages(id, user_id) select 1, -1;
71+
ERROR: insertorupdateon table`messages` violatesforeign keyconstraint"fk_messages_users"
72+
DETAIL: Key (user_id)=(-1) is not presentin table`users`.
73+
```
74+
75+
##Step 2: Fix existing data if needed
76+
77+
Now, with the FK created with`NOT VALID`, we know that Postgres already checks all the new data against the new
78+
constraint, but for the old data, some rows might still be violating it. Before the next step, it makes sense to ensure
79+
there are no old rows violating our new FK. It can be done using this query:
80+
81+
```sql
82+
select id
83+
from messages
84+
where
85+
user_id notin (
86+
select idfrom users
87+
);
88+
```
89+
90+
This query scans the whole`messages` table, so it will take significant time. It is worth ensuring that`users` is
91+
accessed via PK here (depends on the data volumes and planner settings).
92+
93+
The rows identified by this query will block the next step, so they need to either be deleted or changed to avoid the FK
94+
violation.
95+
96+
##Step 3: Validation
97+
98+
To complete the process, we need to`validate` the old rows in a separate transaction:
99+
100+
```sql
101+
altertable messages
102+
validateconstraint fk_messages_users;
103+
```
104+
105+
If the tables are large, this`ALTER` is going to take significant time. However, it only acquires
106+
`ShareUpdateExclusiveLock` and`AccessShareLock` on the referencing table (`messages` in this example).
107+
108+
Therefore, it doesn't block`UPDATE` /`DELETE` /`INSERT`, but it conflicts with DDL and`VACUUM` runs. On the
109+
referenced table (`users` here),`AccessShareLock` and`RowShareLock` are acquired.
110+
111+
As usual, if`autovacuum` processes this table in the transaction ID wraparound prevention mode, it won't yield – so
112+
before running this, make sure there is no`autovacuum` running in this mode or DDL in progress.

‎README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ As an example, first 2 rows:
9494
- 0065[UUID v7 and partitioning (TimescaleDB)](./0065_uuid_v7_and_partitioning_timescaledb.md)
9595
- 0066[How many tuples can be inserted in a page](./0066_how_many_tuples_can_be_inserted_in_a_page.md)
9696
- 0067[Autovacuum "queue" and progress](./0067_autovacuum_queue_and_progress.md)
97+
- 0068[psql shortcuts](./0068_psql_shortcuts.md)
98+
- 0069[How to add a CHECK constraint without downtime](./0069_howd_tod_addd_ad_checkd_constraintd_withoutd_downtime.md)
9799
- ...
98100

99101
##Contributors

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp