PostgreSQL分区表应用
简介
PostgreSQL属于关系型数据库,虽然在排行一直在MySql之下,但是笔者觉得PostgreSQL功能比MySql更加丰富强大,而且使用极爽。特别是近几年推出的jsonb、分区表等功能,如果合理利用PostgreSQL这些功能,那么使用关系数据库处理亿级数据量将不再是梦想。本篇文章将给大家介绍如何使用PostgreSQL进行分区管理实现亿级数据库优化。
开始
接下来会以销售单为案例创建两种销售表,分别使用分区表和常规表方式进行对比。
创建分区表
在postgre中创建一张sale表并按照销售日期进行分区
名称 | 类型 | 是否主键 | 描述 |
---|---|---|---|
id | guid | 是 | id |
sale_date | date | 否 | 销售日期 |
country_code | text | 否 | 地区编码 |
product_sku | text | 否 | 商品sku |
units | int4 | 否 | 单位 |
创建sale主表
CREATE TABLE sale (
Id uuid,
sale_date date not null,
country_code text,
product_sku text,
units integer
) PARTITION BY RANGE (sale_date);
创建sale分表
CREATE TABLE sale_202001 PARTITION OF sale FOR VALUES FROM ('2020-01-01') TO ('2020-02-01');
CREATE TABLE sale_202002 PARTITION OF sale FOR VALUES FROM ('2020-02-01') TO ('2020-03-01');
CREATE TABLE sale_202003 PARTITION OF sale FOR VALUES FROM ('2020-03-01') TO ('2020-04-01');
CREATE TABLE sale_202004 PARTITION OF sale FOR VALUES FROM ('2020-04-01') TO ('2020-05-01');
CREATE TABLE sale_202005 PARTITION OF sale FOR VALUES FROM ('2020-05-01') TO ('2020-06-01');
CREATE TABLE sale_202006 PARTITION OF sale FOR VALUES FROM ('2020-06-01') TO ('2020-07-01');
CREATE TABLE sale_202007 PARTITION OF sale FOR VALUES FROM ('2020-07-01') TO ('2020-08-01');
CREATE TABLE sale_202008 PARTITION OF sale FOR VALUES FROM ('2020-08-01') TO ('2020-09-01');
CREATE TABLE sale_202009 PARTITION OF sale FOR VALUES FROM ('2020-09-01') TO ('2020-10-01');
CREATE TABLE sale_202010 PARTITION OF sale FOR VALUES FROM ('2020-10-01') TO ('2020-11-01');
CREATE TABLE sale_202011 PARTITION OF sale FOR VALUES FROM ('2020-11-01') TO ('2020-12-01');
CREATE TABLE sale_202012 PARTITION OF sale FOR VALUES FROM ('2020-12-01') TO ('2021-01-01');
给分表件索引
postgre分区表中实际数据都是存在区表上的,所以只有给区表建索引才有作用。
ALTER TABLE sale_202001 ADD PRIMARY KEY(id);
ALTER TABLE sale_202002 ADD PRIMARY KEY(id);
ALTER TABLE sale_202003 ADD PRIMARY KEY(id);
ALTER TABLE sale_202004 ADD PRIMARY KEY(id);
ALTER TABLE sale_202005 ADD PRIMARY KEY(id);
ALTER TABLE sale_202006 ADD PRIMARY KEY(id);
ALTER TABLE sale_202007 ADD PRIMARY KEY(id);
ALTER TABLE sale_202008 ADD PRIMARY KEY(id);
ALTER TABLE sale_202009 ADD PRIMARY KEY(id);
ALTER TABLE sale_202010 ADD PRIMARY KEY(id);
ALTER TABLE sale_202011 ADD PRIMARY KEY(id);
ALTER TABLE sale_202012 ADD PRIMARY KEY(id);
创建常规表
在postgre中创建一直saleAll常规表不做分区
CREATE TABLE saleAll (
Id uuid,
sale_date date not null,
country_code text,
product_sku text,
units integer
);
ALTER TABLE saleAll ADD PRIMARY KEY(id);
插入海量数据
通过5000万条数据对比分区表和常规表查询性能
sale分区表批量插入数据
-- 随机数 --
CREATE OR REPLACE FUNCTION random_between(low INT ,high INT)
RETURNS INT AS
$$
BEGIN
RETURN floor(random()* (high-low + 1) + low);
END;
$$ language 'plpgsql' STRICT;
-- sale批量新增 --
create or replace function insertSale() returns void as
$$
declare
i int :=0;
p TEXT :='';
tempTime timestamp;
j int;
begin
while i < 50000000 loop
j:=round(random()*80);
p:=concat('P-', random_between(1000,2000)::TEXT);
tempTime:=date '2020-01-01' + j;
insert into sale(id, sale_date, country_code, product_sku, units)
values(uuid_generate_v4(),tempTime, 'CN', p ,round(random() * 100));
i:= i+1;
raise notice 'holy shit%', i;
end loop;
end;
$$ language plpgsql;
saleAll常规表批量插入数据
-- saleAll批量新增 --
create or replace function insertSaleAll() returns void as
$$
declare
i int :=0;
p TEXT :='';
tempTime timestamp;
j int;
begin
while i < 50000000 loop
j:=round(random()*80);
p:=concat('P-', random_between(1000,2000)::TEXT);
tempTime:=date '2020-01-01' + j;
insert into saleAll(id, sale_date, country_code, product_sku, units)
values(uuid_generate_v4(),tempTime, 'CN', p ,round(random() * 100));
i:= i+1;
raise notice 'holy shit%', i;
end loop;
end;
$$ language plpgsql;
执行批量插入
SELECT insertSale();
SELECT insertSaleAll();
批量插入海量数据需要等待很久,建议在单独的测试库中进行,否则影响数据库性能。
查询性能对比
首先对分区表和常规表进行查询性能对比
全表扫描
使用count(*)
对分区表进行全表扫描
使用 count(*)
对常规表进行全部扫描
可以看出分区表和常规表全表扫描没有明显差距
日期条件查询
分区表与常规表分表使用sale_date进行查询,耗时对比如下:
查询 | 日期范围 | sale分区表数据量 | saleAll常规表数据量 | 分区表耗时(s) | 常规表耗时(s) |
---|---|---|---|---|---|
count(*) | 2020-01-01至2020-02-01 | 11113634 | 12991066 | 1.355 | 3.042 |
count(*) | 2020-02-01至2020-03-01 | 10576517 | 12371957 | 1.224 | 3.030 |
count(*) | 2020-03-01至2020-04-01 | 7506640 | 8541846 | 0.980 | 2.848 |
count(*) | 2020-04-01至2020-05-01 | 5154498 | 4956280 | 0.706 | 2.716 |
count(*) | 2020-05-01至2020-06-01 | 5321426 | 5199780 | 0.607 | 2.760 |
count(*) | 2020-06-01至2020-07-01 | 3395589 | 3181768 | 0.495 | 2.657 |
count(*) | 2020-07-01至2020-08-01 | 2561833 | 788134 | 0.415 | 2.634 |
count(*) | 2020-08-01至2020-09-01 | 2524290 | 798648 | 0.326 | 2.601 |
count(*) | 2020-09-01至2020-10-01 | 1591223 | 481197 | 0.246 | 2.654 |
count(*) | 2020-10-01至2020-11-01 | 986697 | 1181387 | 0.240 | 2.617 |
count(*) | 2020-11-01至2020-12-01 | 897223 | 1162590 | 0.206 | 2.662 |
count(*) | 2020-12-01至2020-12-31 | 488046 | 730686 | 0.095 | 2.582 |
从上面对比可以分析出使用sale_date字段进行查询时sale主表自动导航到区别进行查询,所以查询耗时回根据区表的大小有所差异。常规表使用sale_date进行查询时会扫描全表,所以查询时间非常平均,而且数据量较大时查询速度会非常缓慢。
索引查询
使用索引对分区表和常规表进行查询比较
sale分区表使用索引查询如下:
saleAll常规表使用索引查询如下:
通过索引查询结果显示分区表和常规表的速度都是毫秒内的,但是如果细算一下分区表的查询速度比常规表快4倍左右。
普通字段查询
下面使用product_sku普通字段进行查询对比
分区表查询:
常规表查询:
总结
通过以上操作对比可以看出,在单表数据过大时查询速度会明细变慢,使用分区表对大数据量进行分表存储而所有增删改只需对主表进行操作会大大提高查询效率。Postgre中还要更多的配置用来提高性能,或许我们在系统架构中无需搭建大数据平台,直接使用Postgre处理大数据也会成为一种选择。
原文地址:https://www.cnblogs.com/william-xu/p/12531950.html
- 外部表简单总结(r3笔记第51天)
- 通过shell脚本监控sql执行频率(r3笔记第50天)
- 和Null有关的函数(r3笔记第48天)
- 关于查询转换的一些简单分析(二) (r3笔记第68天)
- 跨网络拷贝文件的简单实践(r3笔记第67天)
- 关于enq: TX - allocate ITL entry的问题分析(r3笔记第66天)
- Tensorflow学习:使用Tensorflow搭建深层网络分类器
- 关于interval partitioning(r3笔记65天)
- Spark Tips4: Kafka的Consumer Group及其在Spark Streaming中的“异动”(更新)
- 关于数据库中的一些name(r3笔记第64天)
- 码农的瑞士军刀-脚本语言
- shell基础学习总结(一) (r3笔记第63天)
- 关于sysdba,sysoper,dba的区别(r3笔记第62天)
- 使用句柄实现特定场景的无备份恢复 (r3笔记第61天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 看完这篇文章,99%的人都会使用Mysql Explain工具
- 浅析MySQL存储引擎序列属性
- 详述MySQL Using intersect交集算法
- 案例:强制开库遭遇ORA-16433的处理过程
- 叮~AutoML自动化机器学习入门指南,来了
- 注意:ORACLE 11G ADG RAC 这个情况下并不能高可用
- Nginx转发指向数据库端口并对外开放访问权限
- Python手写了 35 种可解释的特征工程方法
- Geant4--G4ParticleGun定义射线源的发射能谱
- Sony-PMCA-RE, 反向工程索尼PlayMemories相机应用
- 中标麒麟系统Your trial is EXPIRED and no VALID licens
- R初探
- 独家 | 在Python编程面试前需要学会的10个算法(附代码)
- 为什么一个还没毕业的大学生能够把 IO 讲的这么好?
- 如何将单 master 升级为多 master 集群