Fla*_*vio 6 postgresql performance database-design
我们正在尝试使用这些数字和查询要求来优化数据库的性能:
有了这些必要条件,我们就得出了这个解决方案(有一个很大的缺点,解释如下)。
[A] TABLE main_segments_history(
id_segment integer NOT NULL,
day date NOT NULL,
day_slices bigint[],
CONSTRAINT main_segments_history_pk PRIMARY KEY (id_segment,day)
)
[B] TABLE current_segment_release_state(
id_segment integer NOT NULL,
release_date timestamptz,
... all other attributes ...
CONSTRAINT currsegm_release_state_pk PRIMARY KEY (id_segment,release_date)
)
Run Code Online (Sandbox Code Playgroud)
解释[A]表:
pg_partman)进行分区。每个分区是一个月解释[B]表:
有一个后端进程详细说明了网络。
它每 3 分钟插入或更新每个段的状态。
换句话说,此过程将在一天开始时插入新行,并将每 3 分钟更新一次内部数组。
该解决方案的优点:
的缺点:
约束_排除。在查询日期范围时,需要使用 PostgreSQL 的该功能/参数。即在跨多个分区的预编译查询中使用常量值。例子:
约束排除 OK(仅在 2017 年 2 月和 3 月搜索):
SELECT * FROM main_segments_history
WHERE day BETWEEN '2017-01-01' AND '2017-02-03'
Run Code Online (Sandbox Code Playgroud)
约束排除 KO(将搜索所有分区表):
SELECT * FROM main_segments_history m
JOIN sometable s ON s.id=m.id_segment
WHERE day BETWEEN s.day_from AND s.day_to
Run Code Online (Sandbox Code Playgroud)UPDATE 是邪恶的。
由于性能问题,我们关闭了 autovacuum,每晚执行一批。
考虑到通过这种方式,我们每天几乎有 90M 到 190M 的更新,这也是 postgreSQL 完全重写的行数(你当然知道 UPDATE 会标记行已删除,然后插入新行)。
此外,UPDATE 是一项非常耗时的操作,通常会导致写入延迟。
我们首先研究了使用 LINK-TO-DATA 设计的可能性,例如使用一个表作为段 id 的容器,其中包含一个 data_id 到段状态的大表,但是当我们只计算每个行的行数时我们就放弃了要处理的月份:〜2.880.000.000,每天有〜3GB的空间。不太好。
你有什么想法?你有什么解决方案来优化这个系统吗?
避免更新表的唯一方法就是不执行更新。鉴于您只是记录数据,我建议您使用每日表,在其中存储一天的数据。您可能每天有 190 M(较小的)插入,而不是每天 190 M 更新;有多少段就有多少更新。
CREATE TABLE main_segments_history
(
id_segment integer NOT NULL,
day date NOT NULL,
day_slices bigint[],
CONSTRAINT main_segments_history_pk PRIMARY KEY (id_segment, day)
) ;
CREATE TABLE dayly_segments
(
id_segment integer NOT NULL,
day date NOT NULL,
id_slice integer NOT NULL,
slice bigint,
PRIMARY KEY (id_segment, day, id_slice)
) ;
Run Code Online (Sandbox Code Playgroud)
这将模拟一天的数据(以及 200 个分段;而不是您可能拥有的数百万个分段):
INSERT INTO
dayly_segments
(id_segment, day, id_slice, slice)
SELECT
id_segment, '2017-01-01', id_slice, (random()*1e7)::bigint
FROM
generate_series (1, 200) AS s1(id_segment)
CROSS JOIN generate_series (1, 20*24) AS s2(id_slice) ;
Run Code Online (Sandbox Code Playgroud)
我假设当您运行进程时,您有一段时间活动较少vacuuming。这时,您可以将数据从dayly_segments表中移动到 中main_segments_history,并将所有数据放入一个数组中。这样,您就永远不会更新main_segments_history。
基本上,这就是您要做的:
-- Move segments from dayly_segments to main_segment_history
INSERT INTO
main_segments_history
(id_segment, day, day_slices)
SELECT
id_segment, day,
(SELECT array_agg(slice)
FROM (SELECT slice
FROM dayly_segments s1
WHERE s1.id_segment = s0.id_segment AND s1.day = s0.day
ORDER BY id_slice) AS s2)
FROM
(SELECT DISTINCT
id_segment, day
FROM
dayly_segments s0
WHERE
day = '2017-01-01'
) AS s0 ;
-- Delete them from original
DELETE FROM
dayly_segments
WHERE
day = '2017-01-01' ;
-- At this point, you should also...
VACUUM dayly_segments ;
Run Code Online (Sandbox Code Playgroud)
dbfiddle在这里
假设:
$current_value。也就是说,您的阵列中没有漏洞。id_slice可以分配任何递增序列的结果。您也可以使用一个值,而不是整数值time。| 归档时间: |
|
| 查看次数: |
1287 次 |
| 最近记录: |