Skip to content

Commit

Permalink
doc: renew table and reindex; explaination sql
Browse files Browse the repository at this point in the history
  • Loading branch information
MarsonShine committed Jan 10, 2025
1 parent dc7823c commit 6fdfc03
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 1 deletion.
73 changes: 72 additions & 1 deletion Postgresql-In-Internal/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,75 @@ VACUUM 操作通常包括以下阶段:

需要注意的是,增加工作进程数可能导致每个进程分配的资源减少,应根据系统性能进行合理配置。

通过合理配置这些参数,PostgreSQL 能够在保持数据库性能的同时,自动执行必要的清理操作,确保数据库的高效运行。
通过合理配置这些参数,PostgreSQL 能够在保持数据库性能的同时,自动执行必要的清理操作,确保数据库的高效运行。

## 表重建和索引重建

如果表或索引文件的大小增加了,VACUUM 可以清理一些页内空间,但很少能减少页面的数量。只有当文件末尾出现若干空页面时,回收的空间才能返还给操作系统,但这种情况并不常见。

大小过大会导致诸多不良影响:

- 全表 (或索引) 扫描将花费更长时间。
- 可能需要更大的缓冲区缓存 (页面作为一个整体被缓存,因此数据密度降低)。
- B 树会有额外的层级,这会减慢索引访问的速度。
- 文件在磁盘上和备份中占用额外空间。

可以执行VACUUM FULL 命令执行完全清理,可以对表和索引进行重建,请注意,该操作会执行表锁定。

除了 VACUUM FULL,还有其它重建方式。

### CLUSTER

`CLUSTER` 命令不仅会清理表中的死元组,还会根据指定的索引对表中的**元组进行排序**。它会将表按照索引的顺序重新组织,以优化基于该索引的查询性能。

但同样此操作需要对表进行锁定,如果表非常大,操作会非常耗时,并且会对其它事务操作造成影响。

**注意**:PostgreSQL 不支持聚簇(即排序)永久化:每当有新的插入或更新操作时,表的物理顺序就会改变。因此,`CLUSTER` 只会在执行时优化顺序,对未来的更新不会产生影响。

### REINDEX

如果索引变得碎片化,或者因为某些原因索引失效(例如损坏),可以使用 `REINDEX` 来重新创建索引。

**注意:**`REINDEX` 只重建索引,而不是对整个表进行重建。所以它不会回收表空间。

### 拓展工具

[pg_repack](github.com/reorg/pg_repack) 是一个 PostgreSQL 扩展,用于在 **几乎零停机的情况下**重建表和索引。与 `VACUUM FULL` 不同,`pg_repack` 通过在后台创建一个新的**临时表**和索引,逐步将数据从旧表复制到新表,尽量减少对生产系统的影响。

这里简单讲下 pg_repack 的工作原理:

在重建过程中,`pg_repack` 不会占用长时间的排它锁,而是通过创建新的表(或索引)来避免对现有表的操作产生长时间的阻塞。

排它锁只会在重建的 **开始和结束时** 短暂存在。具体来说,操作会先在后台创建新表或新索引,随后进行数据的逐步迁移,最后删除旧表并替换成新表(索引)。

**较少停机时间**:由于数据在后台逐步迁移,这大大减少了实际业务运行中的停机时间。

> 这里着重讲下数据同步:
>
> 因为是在新表和新索引进行操作,然后再转移,那么之前的旧表会继续有数据和新增索引信息,怎么保持数据的同步和一致性呢?
>
> 实际上这跟增量同步的机制是类似的:
>
> **创建临时表**
>
> - `pg_repack` 会创建一个与原始表结构完全相同的临时表。这张表是通过原始表的定义来创建的,但初始时没有任何数据。
>
> **复制数据到临时表**
>
> - 在数据复制阶段,`pg_repack` 会将原始表的数据逐步(按批次)复制到临时表中。
> - 由于数据是在逐步复制的,这意味着它在进行数据迁移时不会锁定原始表,原始表仍然可以进行 INSERT、UPDATE、DELETE 操作。这使得在复制过程中,原始表仍然可以继续接收新的数据。
>
> **保持同步(最小化数据差异)**
>
> - 在数据复制的过程中,`pg_repack` 会监控原始表的更改(如插入、更新和删除)。在复制数据到临时表后,系统会保持一个视图,查看原始表中的任何新数据和更新,并及时同步到临时表。
> - 这种机制是通过对原始表的增量变更(通常是通过触发器或日志记录的方式)来实现的,如 [WAL](https://github.com/MarsonShine/MS.Microservice/blob/master/docs/patterns-of-distributed-systems/Write-Ahead-Log.md),从而保证在复制期间数据的一致性。
>
> **交换原始表与临时表**
>
> - 一旦数据复制完成并且临时表与原始表保持同步,`pg_repack` 就会使用一个非常短的时间窗口(在此期间对表会加锁)来交换原始表和临时表。这一步是所有数据同步的最后一步,在交换表的过程中,`pg_repack` 会保证将最后的差异同步到临时表。
> - 这个交换过程是原子性的,只会对表进行非常短时间的排它锁定,这通常是不可感知的,尤其是在高负载的系统中。
>
> **清理临时表**
>
> - 完成表和索引的交换后,`pg_repack` 会删除临时表,并清理过程中产生的任何临时文件或资源。
34 changes: 34 additions & 0 deletions Postgresql-In-Internal/SQL-IN-EXPLAINATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# PostgreSQL 代码解析

```sql
INSERT INTO auditlogs("BizId","Table","Type","Status","Message","CreateTime","UpdateTime")
VALUES (@BizId,@Table,@Type,@Status,@Message,@CreateTime,@UpdateTime)
ON CONFLICT("Table","BizId","Type")
DO UPDATE SET "Status"=EXCLUDED."Status","Type"=EXCLUDED."Type","Message"=EXCLUDED."Message","UpdateTime"=EXCLUDED."UpdateTime"
```

`INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...) ON CONFLICT (conflict_target) DO UPDATE SET column1 = value1, column2 = value2, ...` 是一个特定语法结构,用于实现 UPSERT(更新或插入)操作。

如果表中已有一条记录使得插入失败(如主键冲突),通常会抛出错误。

其中 `EXCLUDED` 表示在冲突发生时的数据。你可以使用 `EXCLUDED.column_name` 来引用这些新行中的某个列的值。

```sql
WITH batch AS (
SELECT id FROM vac
WHERE NOT processed
LIMIT 1000
FOR UPDATE SKIP LOCKED
)
UPDATE vac
SET processed = true
WHERE id IN (SELECT id FROM batch);
```

`WITH batch` 是一个 **CTE(公用表表达式)**,临时选择出最多 1000 条未处理(`processed = false`)的记录。

`FOR UPDATE`: 将查询结果中的行加锁,防止其他事务同时修改这些行。

`SKIP LOCKED`: 如果某些行已经被其他事务加锁,则跳过这些行。

这可以避免事务因为等待锁而阻塞,提高并发性能。

0 comments on commit 6fdfc03

Please sign in to comment.