PostgreSQL表分区详解

表分区是一种非常有效的技术,用于提高大型数据库表的性能。通过将表的内容划分为较小的子表(称为分区),可以减小表的整体大小,从而显着提高性能。

什么时候应该对表进行分区?

  • 如果您的表对于服务器的 RAM 来说太大。您应该考虑对其进行分区:当表的大小达到几 GB 时,就该将其拆分。
  • 如果您正在处理大量数据,那么在拥有数百万条记录之前,不必费心进行分区。否则,您不会看到太多的性能提升。
  • 如果您的表可以在逻辑上分解为更小的块,例如:您有一个充满服务器日志的表。您可以按日期将它们拆分,因此同一天的所有日志都位于一个分区中。这使得执行诸如删除旧日志之类的任务变得更加容易,只需删除分区即可。

Postgres 内置支持三种类型的分区:

按range范围分区

该表被分区为由键列或一组列定义的"范围ranges",分配给不同分区的值范围之间没有重叠

在以下示例中,人员表将按birth_date分区

复制代码
CREATE TABLE people (
    id **int** not **null**,
    birth\_date date not **null**,
    country\_code character(2) not **null**,
    name text
) PARTITION BY RANGE (birth\_date);

CREATE TABLE people\_y2000 PARTITION OF people
    FOR VALUES FROM ('2000-01-01') TO ('2001-01-01');

CREATE TABLE people\_y2001 PARTITION OF people
    FOR VALUES FROM ('2001-01-01') TO ('2002-01-01');

CREATE TABLE people\_y2002 PARTITION OF people
    FOR VALUES FROM ('2002-01-01') TO ('2003-01-01');

我们来尝试一下:

复制代码
INSERT INTO people (id, birth\_date, country\_code, name) VALUES
   (1, '2000-01-01', 'US', 'John'),
   (2, '2000-02-02', 'IT', 'Jane'),
   (3, '2001-03-03', 'FR', 'Bob');
> INSERT 0 3

SELECT schemaname,relname,n\_live\_tup 
   FROM pg\_stat\_user\_tables 
   ORDER BY n\_live\_tup DESC;

schemaname  |   relname    | n\_live\_tup 
------------+--------------+------------
 **public**     | people\_y2000 |          2
 **public**     | people\_y2001 |          1
 **public**     | people\_y2002 |          0

我们在主表people 中插入了三条记录。由于该表是按birth_date分区的,因此已将两条记录添加到分区people_y2000中,一条记录添加到people_y2001中,而people_y2002仍然为空。

按列表分区

通过显式列出每个分区中出现的键值来对表进行分区。

以同一示例为例,我们添加一个country_code列并将其用作分区键

复制代码
CREATE TABLE people (
    id **int** not **null**,
    birth\_date date not **null**,
    country\_code character(2) not **null**,
    name text
) PARTITION BY LIST (country\_code);

-- Partition **for** people living in Europe
CREATE TABLE people\_EU PARTITION OF people
    FOR VALUES IN ('AT', 'DE', 'IT', 'FR', 'ES', ..... );

-- Partition **for** people living in United States
CREATE TABLE people\_US PARTITION OF people
    FOR VALUES IN ('US');

我们来尝试一下:

复制代码
INSERT INTO people (id, birth\_date, country\_code, name) VALUES
   (1, '2000-01-01', 'US', 'John'),
   (2, '2000-02-02', 'IT', 'Jane'),
   (3, '2001-03-03', 'FR', 'Bob');
> INSERT 0 3

SELECT schemaname,relname,n\_live\_tup 
   FROM pg\_stat\_user\_tables 
   ORDER BY n\_live\_tup DESC;

 schemaname |  relname  | n\_live\_tup 
------------+-----------+------------
 **public**     | people\_eu |          2
 **public**     | people\_us |          1

PostgreSQL 再次将每一行移动到正确的分区。

按哈希分区

通过为每个分区指定模数和余数来对表进行分区。每个分区将保存分区键的哈希值除以指定模数将产生指定余数的行。

当我们无法逻辑地划分数据,但我们只能通过将行分散到许多较小的分区来减小表大小时,这种类型非常有用。

下面的 SQL 将把人分成三个表,每个表将包含(几乎)相同的行数。

我们来尝试一下:

复制代码
INSERT INTO people (id, birth\_date, country\_code, name) VALUES
   (1, '2000-01-01', 'US', 'John'),
   (2, '2000-02-02', 'IT', 'Jane'),
   (3, '2001-03-03', 'FR', 'Bob');
> INSERT 0 3

SELECT schemaname,relname,n\_live\_tup 
   FROM pg\_stat\_user\_tables 
   ORDER BY n\_live\_tup DESC;

 schemaname | relname  | n\_live\_tup 
------------+----------+------------
 **public**     | people\_1 |          1
 **public**     | people\_2 |          1
 **public**     | people\_3 |          1

正如您所看到的,这三个记录已均匀地分布在所有可用分区中。

默认分区

当您尝试插入无法放入任何分区的记录时会发生什么?

让我们回到列表分区章节中定义的 people 表,并尝试添加来自加拿大的 Linda:

复制代码
INSERT INTO people (id, birth\_date, country\_code, name) VALUES
   (4, '2002-04-04', 'CA', 'Linda');

ERROR:  no partition of relation "people" found **for** rowDETAILS: Partition key of the failing row contains (country\_code) = (CA). 

INSERT 将失败,因为 PostgreSQL 不知道在哪里添加该记录。

最明显的解决方案是添加一个新分区,但如果我们必须为世界上的每个国家/地区执行此操作,我们最终会得到数百个记录数量很少的表。不太好。

幸运的是,可以定义默认分区!

CREATE TABLE people_default PARTITION OF people DEFAULT;

再次尝试相同的插入,将导致:

复制代码
INSERT INTO people (id, birth\_date, country\_code, name) VALUES
   (1, '2000-01-01', 'US', 'John'),
   (2, '2000-02-02', 'IT', 'Jane'),
   (3, '2001-03-03', 'FR', 'Bob'),
   (4, '2002-04-04', 'CA', 'Linda');
> INSERT 0 4

schemaname |    relname     | n\_live\_tup 
------------+----------------+------------
 **public**     | people\_eu      |          2
 **public**     | people\_us      |          1
 **public**     | people\_**default** |          1

如您所见,Linda 现已添加到people_default中。[https://www.jdon.com/67666.html(URL](https://www.jdon.com/67666.html](URL) Here)

相关推荐
用户2986985301435 分钟前
前端实战:在 React 中使用 JavaScript 一键导出 Excel 图表与形状为图片
javascript·react.js·excel
weixin_BYSJ19871 小时前
django在线图书销售平台---附源码16192
java·javascript·spring boot·python·django·flask·php
半个落月1 小时前
Vue 3 如何接住大模型的流式回答:从 ReadableStream 到可靠的 SSE 解析
前端·javascript·人工智能
咩咩啃树皮2 小时前
第36篇:JavaScript 原型与原型链终极吃透——JS万物皆对象、继承核心原理
开发语言·javascript·ecmascript
晓得迷路了2 小时前
栗子前端技术周刊第 138 期 - NestJS 12、Astro 7.1、npm 12...
前端·javascript·nestjs
霸道流氓气质2 小时前
摄像头视频预览实现教程:从原理到落地(ZLMediaKit+ flv.js+ Spring Boot)
javascript·spring boot·音视频
double_eggm2 小时前
uniapp.3
开发语言·前端·javascript
Thanks_ks4 小时前
从零手写一个 Promise:深入理解 JavaScript 异步编程的本质
javascript·promise·事件循环·异步编程·手写源码·前端进阶·promises/a+
胡萝卜术11 小时前
力扣5. 最长回文子串
前端·javascript·面试
触底反弹13 小时前
🔥 从零搭建 RAG 知识库:爬虫→分词→向量化→检索,一步都不能错
javascript·人工智能·面试