高并发写入存储线性相关性优化

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介:

标签

PostgreSQL , BRIN , 块级索引 , 并行 , 序列


背景

BRIN索引的原理很简单,每个数据块(或者每一段连续的数据块)记录了其中的元数据(最大值,最小值,平均值,COUNT,AVG,NULL值个数等),当每个数据块存储的数据范围错开(边界清晰)时,块级索引的效率就非常高。

例如测试表

create table test(id int, info text);  
  
-- 对ID创建BRIN索引  
create index idx_test_id on test using brin(id);  

ID边界清晰的例子:

数据块1:ID值范围 1-100  
  
数据块2:ID值范围 101-200  
  
数据块3:ID值范围 301-300  
  
......  

ID边界模糊的例子:

数据块1:ID值范围 1-30 , 60-100  
  
数据块2:ID值范围 31-49, 101-130  
  
数据块3:ID值范围 49-59, 130-159, 160-200  
  
......  

边界清晰,指不同的数据块没有交集,或者少量交集,而边界模糊,指数据块的交集很多,数据存储散乱。

为什么边界模糊性能不好呢?

例如

select * from test where id between 1 and 100;   

这条SQL,在边界清晰时,搜索1号数据块即可。

而在边界模糊时,需要搜索1,2,3个数据块。

在实际业务中,我们可能会使用自增序列,作为时序字段,当串行(单进程)写入时,序列值和行号(堆表物理存储)线性相关,而当并行写入时,情况可能会变得糟糕(仅仅当使用了大步调时)。

怎么优化呢?

自增序列,并行写入的问题

1、串行写入

create table test(id serial8, info text);  
  
insert into test (info) select 'test' from generate_series(1,1000000);  

线性相关性 = 1

postgres=# analyze test;  
ANALYZE  
  
postgres=# select correlation from pg_stats where tablename='test' and attname='id';  
 correlation   
-------------  
           1  
(1 row)  

BRIN性能

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where id between 1000 and 10000;  
                                                        QUERY PLAN                                                           
---------------------------------------------------------------------------------------------------------------------------  
 Bitmap Heap Scan on public.test  (cost=6.85..5761.69 rows=9264 width=13) (actual time=0.134..2.703 rows=9001 loops=1)  
   Output: id, info  
   Recheck Cond: ((test.id >= 1000) AND (test.id <= 10000))  
   Rows Removed by Index Recheck: 14679  
   Heap Blocks: lossy=128  
   Buffers: shared hit=130   
   ->  Bitmap Index Scan on idx_test_id  (cost=0.00..4.53 rows=23256 width=0) (actual time=0.026..0.026 rows=1280 loops=1)  
         Index Cond: ((test.id >= 1000) AND (test.id <= 10000))  
         Buffers: shared hit=2  
 Planning time: 0.085 ms  
 Execution time: 3.188 ms  
(11 rows)  

2、并行写入

postgres=# drop table test;  
DROP TABLE  
postgres=# create table test(id serial8, info text);  
CREATE TABLE  
  
vi test.sql  
insert into test (info) values ('test');  
  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 400 -j 400 -t 2500  

线性相关性,略差,但依旧很好

postgres=# create index idx_test_id on test using brin(id);  
CREATE INDEX  
postgres=# analyze test;  
ANALYZE  
postgres=# select correlation from pg_stats where tablename='test' and attname='id';  
 correlation   
-------------  
    0.998394  
(1 row)  
  
-- 偏差  
  
postgres=# select *,id-rn from (select ctid,id,row_number() over(order by ctid) rn from test) t where id<>rn;  
    ctid    |   id    |   rn    | ?column?   
------------+---------+---------+----------  
 (0,3)      |       4 |       3 |        1  
 (0,4)      |       3 |       4 |       -1  
 (0,28)     |      29 |      28 |        1  
 (0,29)     |      28 |      29 |       -1  
 (0,39)     |      40 |      39 |        1  
 (0,40)     |      39 |      40 |       -1  
 (0,66)     |      67 |      66 |        1  
 (0,67)     |      66 |      67 |       -1  
 (0,110)    |     111 |     110 |        1  
 (0,111)    |     110 |     111 |       -1  
 (1,1)      |     189 |     186 |        3  
 (1,2)      |     190 |     187 |        3  
 (1,3)      |     191 |     188 |        3  
 (1,4)      |     192 |     189 |        3  
 (1,5)      |     193 |     190 |        3  
 (1,6)      |     194 |     191 |        3  
 (1,7)      |     195 |     192 |        3  
 (1,8)      |     196 |     193 |        3  
 (1,9)      |     197 |     194 |        3  
 (1,10)     |     198 |     195 |        3  
 (1,11)     |     199 |     196 |        3  
 (1,12)     |     200 |     197 |        3  
 (1,13)     |     201 |     198 |        3  
 (1,14)     |     202 |     199 |        3  
 (1,15)     |     203 |     200 |        3  
 (1,16)     |     204 |     201 |        3  
 (1,17)     |     205 |     202 |        3  
 (1,18)     |     206 |     203 |        3  
 (1,19)     |     207 |     204 |        3  
 (1,20)     |     209 |     205 |        4  
 (1,21)     |     210 |     206 |        4  
 (1,22)     |     212 |     207 |        5  
 (1,23)     |     220 |     208 |       12  
 (1,24)     |     223 |     209 |       14  
 (1,25)     |     226 |     210 |       16  
 (1,26)     |     229 |     211 |       18  

BRIN性能

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where id between 1000 and 10000;  
                                                        QUERY PLAN                                                           
---------------------------------------------------------------------------------------------------------------------------  
 Bitmap Heap Scan on public.test  (cost=6.83..5952.29 rows=9204 width=13) (actual time=0.073..2.687 rows=9001 loops=1)  
   Output: id, info  
   Recheck Cond: ((test.id >= 1000) AND (test.id <= 10000))  
   Rows Removed by Index Recheck: 14679  
   Heap Blocks: lossy=128  
   Buffers: shared hit=130  
   ->  Bitmap Index Scan on idx_test_id  (cost=0.00..4.53 rows=22764 width=0) (actual time=0.033..0.033 rows=1280 loops=1)  
         Index Cond: ((test.id >= 1000) AND (test.id <= 10000))  
         Buffers: shared hit=2  
 Planning time: 0.086 ms  
 Execution time: 3.185 ms  
(11 rows)  

3、并行写入,放大序列步调,散列问题出现

postgres=# drop table test;  
DROP TABLE  
postgres=# create table test(id serial8, info text);  
CREATE TABLE  
postgres=# alter sequence test_id_seq cache 10000;  
ALTER SEQUENCE  
  
vi test.sql  
insert into test (info) values ('test');  
  
pgbench -M prepared -n -r -P 1 -f ./test.sql -c 400 -j 400 -t 2500  

线性相关性,直线下降

postgres=# create index idx_test_id on test using brin(id);  
CREATE INDEX  
postgres=# analyze test;  
ANALYZE  
postgres=# select correlation from pg_stats where tablename='test' and attname='id';  
 correlation   
-------------  
    0.571033  
(1 row)  
  
-- 偏差非常明显  
  
postgres=# select *,id-rn from (select ctid,id,row_number() over(order by ctid) rn from test) t where id<>rn;  
    ctid    |   id    |   rn    | ?column?   
------------+---------+---------+----------  
 (0,6)      |   10001 |       6 |     9995  
 (0,7)      |       6 |       7 |       -1  
 (0,8)      |       7 |       8 |       -1  
 (0,9)      |   10002 |       9 |     9993  
 (0,10)     |   20001 |      10 |    19991  
 (0,11)     |       8 |      11 |       -3  
 (0,12)     |   10003 |      12 |     9991  
 (0,13)     |       9 |      13 |       -4  
 (0,14)     |   20002 |      14 |    19988  
 (0,15)     |   10004 |      15 |     9989  
 (0,16)     |      10 |      16 |       -6  
 (0,17)     |   20003 |      17 |    19986  
 (0,18)     |   10005 |      18 |     9987  
 (0,19)     |      11 |      19 |       -8  
 (0,20)     |   20004 |      20 |    19984  
 (0,21)     |   10006 |      21 |     9985  
 (0,22)     |      12 |      22 |      -10  
 (0,23)     |   20005 |      23 |    19982  
 (0,24)     |   10007 |      24 |     9983  
 (0,25)     |      13 |      25 |      -12  
 (0,26)     |   20006 |      26 |    19980  
 (0,27)     |      14 |      27 |      -13  
 (0,28)     |   20007 |      28 |    19979  
 (0,29)     |   10008 |      29 |     9979  
 (0,30)     |      15 |      30 |      -15  
 (0,31)     |   20008 |      31 |    19977  
 (0,32)     |   10009 |      32 |     9977  
 (0,33)     |   20009 |      33 |    19976  
 (0,34)     |      16 |      34 |      -18  
 (0,35)     |   30001 |      35 |    29966  
 (0,36)     |   20010 |      36 |    19974  
 (0,37)     |   10010 |      37 |     9973  
 (0,38)     |      17 |      38 |      -21  
 (0,39)     |   20011 |      39 |    19972  
 (0,40)     |   20012 |      40 |    19972  
 (0,41)     |   10011 |      41 |     9970  
 (0,42)     |      18 |      42 |      -24  
 (0,43)     |   30002 |      43 |    29959  
 (0,44)     |   20013 |      44 |    19969  
 (0,45)     |      19 |      45 |      -26  
 (0,46)     |   10012 |      46 |     9966  
 (0,47)     |   30003 |      47 |    29956  
 (0,48)     |   20014 |      48 |    19966  
 (0,49)     |      20 |      49 |      -29  
 (0,50)     |   30004 |      50 |    29954  
 (0,51)     |   10013 |      51 |     9962  
 (0,52)     |   20015 |      52 |    19963  
 (0,53)     |      21 |      53 |      -32  
 (0,54)     |   30005 |      54 |    29951  
 (0,55)     |   10014 |      55 |     9959  
 (0,56)     |   20016 |      56 |    19960  
 (0,57)     |      22 |      57 |      -35  
 (0,58)     |   20017 |      58 |    19959  

BRIN性能

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from test where id between 1000 and 10000;  
                                                         QUERY PLAN                                                           
----------------------------------------------------------------------------------------------------------------------------  
 Bitmap Heap Scan on public.test  (cost=5.11..6244.85 rows=2222 width=13) (actual time=7.104..43.755 rows=1501 loops=1)  
   Output: id, info  
   Recheck Cond: ((test.id >= 1000) AND (test.id <= 10000))  
   Rows Removed by Index Recheck: 448419  
   Heap Blocks: lossy=2432  
   Buffers: shared hit=2440  -- 散列,BRIN索引混淆了,导致扫描了大量的数据块。  
   ->  Bitmap Index Scan on idx_test_id  (cost=0.00..4.56 rows=38916 width=0) (actual time=0.071..0.071 rows=24320 loops=1)  
         Index Cond: ((test.id >= 1000) AND (test.id <= 10000))  
         Buffers: shared hit=8  
 Planning time: 0.128 ms  
 Execution time: 43.867 ms  
(11 rows)  

并发时序线性优化

1、使用单步序列。单步序列不会因为并发增加而导致离散度增加。

2、使用系统时间字段,系统时间与单步序列效果一样,即使并发写入,也不会导致离散度增加。

使用连续自增值的字段,创建BRIN索引就是很靠谱的。

目录
相关文章
|
3月前
|
存储 监控 固态存储
在高并发环境下,如何优化 WAL 的写入性能?
在高并发环境下,如何优化 WAL 的写入性能?
|
6月前
|
存储 关系型数据库 OLAP
TiDB适用场景解析:海量数据存储与高并发读写的利器
【2月更文挑战第25天】随着大数据时代的到来,海量数据存储和高并发读写成为众多企业面临的挑战。TiDB作为一种高性能、分布式的关系型数据库,以其独特的架构和强大的功能,在多个场景中展现出了卓越的性能。本文将详细探讨TiDB在海量数据存储、高并发读写等场景下的适用情况,分析其在不同业务场景中的优势与应用价值。
|
6月前
|
人工智能 JSON 前端开发
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
【Spring boot实战】Springboot+对话ai模型整体框架+高并发线程机制处理优化+提示词工程效果展示(按照框架自己修改可对接市面上百分之99的模型)
|
1月前
|
存储 监控 固态存储
在高并发环境下,如何优化 WAL 的写入性能?
在高并发环境下,如何优化 WAL 的写入性能?
|
21天前
|
缓存 监控 负载均衡
nginx相关配置及高并发优化
Nginx的高并发优化是一个综合性的过程,需要根据具体的业务场景和硬件资源量身定制。以上配置只是基础,实际应用中还需根据服务器监控数据进行持续调整和优化。例如,利用工具如ab(Apache Benchmarks)进行压力测试,监控CPU、内存、网络和磁盘I/O等资源使用情况,确保配置的有效性和服务的稳定性。
79 0
|
3月前
|
存储 缓存 安全
.NET 在金融行业的应用:高并发交易系统的构建与优化之路
【8月更文挑战第28天】在金融行业,交易系统需具备高并发处理、低延迟及高稳定性和安全性。利用.NET构建此类系统时,可采用异步编程提升并发能力,优化数据库访问以降低延迟,使用缓存减少数据库访问频率,借助分布式事务确保数据一致性,并加强安全性措施。通过综合优化,满足金融行业的严苛要求。
47 1
|
3月前
|
监控 算法 Java
企业应用面临高并发等挑战,优化Java后台系统性能至关重要
随着互联网技术的发展,企业应用面临高并发等挑战,优化Java后台系统性能至关重要。本文提供三大技巧:1)优化JVM,如选用合适版本(如OpenJDK 11)、调整参数(如使用G1垃圾收集器)及监控性能;2)优化代码与算法,减少对象创建、合理使用集合及采用高效算法(如快速排序);3)数据库优化,包括索引、查询及分页策略改进,全面提升系统效能。
45 0
|
5月前
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【6月更文挑战第30天】Java分布式锁在高并发下确保数据一致性,通过Redis的SETNX、ZooKeeper的临时节点、数据库操作等方式实现。优化策略包括锁超时重试、续期、公平性及性能提升,关键在于平衡同步与效率,适应大规模分布式系统的需求。
156 1
|
4月前
|
存储 NoSQL Java
探索Java分布式锁:在高并发环境下的同步访问实现与优化
【7月更文挑战第1天】在分布式系统中,Java分布式锁解决了多节点共享资源的同步访问问题,确保数据一致性。常见的实现包括Redis的SETNX和过期时间、ZooKeeper的临时有序节点、数据库操作及Java并发库。优化策略涉及锁超时、续期、公平性及性能。选择合适的锁策略对高并发系统的稳定性和性能至关重要。
167 0
|
缓存 Java 应用服务中间件
【高并发优化手段】基于Springboot项目(二)
【高并发优化手段】基于Springboot项目
457 0

热门文章

最新文章