PostgreSQL 用游标优化的一个例子

一位PG社区的朋友提到的一个应用场景,目前遇到性能问题。
数据结构大概是这样的,包含一个主键,一个数组,一个时间,其他字段。
请求分析:
有检索需求,比较频繁。查找数组中包含某些元素的记录,并按时间排序输出所有符合条件的记录,检索到的符合条件的记录可能上万条,也可能较少。
有插入需求,量不大。
有更新需求,一条记录最多一天会被更新一次,当然也可能不会被更新。
无删除需求。
数据量在千万级别。

这个应用场景的不安定因素来自于一些热点值。
例如,当输出的数据量较大时,排序对CPU的开销较大。而这些热点值可能也是查询的热点。
对于检索的条件是数组,这个可以用GIN索引来解决,只有排序是无法解决的。

测试,生成300万测试记录:
postgres=# create table test(id int primary key,info int[],crt_date date);
CREATE TABLE
postgres=# insert into test select generate_series(1,3000000), ('{'||round(random()*1000)||','||round(random()*1000)||','||round(random()*1000)||'}')::int[], current_date+round(random()*1000)::int;
INSERT 0 3000000
postgres=# create index idx_test_info on test using gin(info);
CREATE INDEX
当输出记录较少时,效率还是可以的,例如以下:
postgres=# explain (analyze,verbose,buffers,timing) select info,crt_date from test where info @> '{1,8}'::int[] order by crt_date desc;
                                                          QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=101.23..101.29 rows=22 width=37) (actual time=1.668..1.672 rows=21 loops=1)
   Output: info, crt_date
   Sort Key: test.crt_date DESC
   Sort Method: quicksort  Memory: 26kB
   Buffers: shared hit=26
   ->  Bitmap Heap Scan on public.test  (cost=16.17..100.74 rows=22 width=37) (actual time=1.609..1.647 rows=21 loops=1)
         Output: info, crt_date
         Recheck Cond: (test.info @> '{1,8}'::integer[])
         Heap Blocks: exact=21
         Buffers: shared hit=26
         ->  Bitmap Index Scan on idx_test_info  (cost=0.00..16.17 rows=22 width=0) (actual time=1.595..1.595 rows=21 loops=1)
               Index Cond: (test.info @> '{1,8}'::integer[])
               Buffers: shared hit=5
 Planning time: 0.224 ms
 Execution time: 1.722 ms
(15 rows)
返回21行,算上排序需要1.7毫秒。
但是如果返回记录数上万之后,来看看结果:
postgres=# explain (analyze,verbose,buffers,timing) select info,crt_date from test where info @> '{1}'::int[] order by crt_date desc;
                                                            QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
 Sort  (cost=7737.83..7754.58 rows=6700 width=37) (actual time=17.726..18.856 rows=8896 loops=1)
   Output: info, crt_date
   Sort Key: test.crt_date DESC
   Sort Method: quicksort  Memory: 1080kB
   Buffers: shared hit=5028
   ->  Bitmap Heap Scan on public.test  (cost=59.93..7312.04 rows=6700 width=37) (actual time=3.722..13.585 rows=8896 loops=1)
         Output: info, crt_date
         Recheck Cond: (test.info @> '{1}'::integer[])
         Heap Blocks: exact=5025
         Buffers: shared hit=5028
         ->  Bitmap Index Scan on idx_test_info  (cost=0.00..58.25 rows=6700 width=0) (actual time=2.620..2.620 rows=8896 loops=1)
               Index Cond: (test.info @> '{1}'::integer[])
               Buffers: shared hit=3
 Planning time: 0.151 ms
 Execution time: 19.637 ms
(15 rows)
返回8896行,算上排序需要19.6毫秒。(这是返回所有记录的时间,如果是分页的话,第一页会很快返回)

优化建议。
1. 如果遇到排序带来的CPU负载过高的问题,可以创建热值partial index
对于热值,创建partial index。例如以上热值:
postgres=# create index idx_test_info_1 on test (crt_date) where info @> '{1}'::int[];
CREATE INDEX
禁止排序
postgres=# set enable_sort=off;
SET
postgres=# explain (analyze,verbose,buffers,timing) select * from test where info @> '{1}'::int[] order by crt_date desc;
                                                                   QUERY PLAN                                                       

------------------------------------------------------------------------------------------------------------------------------------
-------------
 Index Scan Backward using idx_test_info_1 on public.test  (cost=0.29..18253.53 rows=6700 width=41) (actual time=0.013..9.147 rows=8
896 loops=1)
   Output: id, info, crt_date
   Buffers: shared hit=8909
 Planning time: 0.253 ms
 Execution time: 9.911 ms
(5 rows)
当然这么做有很大的弊端,因为如果热值比较多,我们要为各种热值相关的查询条件创建很多的索引。

2. 因为一条记录一天最多更新一次,所以完全可以使用应用层缓存,或者pgmemcache这样的缓存插件,降低数据库的负担。

3. 使用游标,我们注意到用户使用了分页显示,但是对于用户来说,可能只会看第一页或前几页的内容,所以每次都全部取到程序端是没有必要的,用游标会更好。(注意不要使用order by limit x offset x这种方式分页,会冗余扫描多次,请使用cursor,但是记得用完关闭。)详见驱动API,如pg-jdbc。

压力测试:
测量类似分页,我这里只取第一页的内容(使用热值partial index)。
注意这种用法不是游标的用法。只是方便这里测试的。
vi test.sql
select * from test where info @> '{1}'::int[] order by crt_date desc limit 10;
性能非常可观:
pg95@db-172-16-3-150-> pgbench -M prepared -n -r -f ./test.sql -P 1 -c 16 -j 16 -T 30
progress: 1.0 s, 72844.1 tps, lat 0.213 ms stddev 0.119
progress: 2.0 s, 73691.9 tps, lat 0.215 ms stddev 0.019
progress: 3.0 s, 73603.7 tps, lat 0.216 ms stddev 0.018
progress: 4.0 s, 73501.3 tps, lat 0.216 ms stddev 0.063
progress: 5.0 s, 73433.2 tps, lat 0.216 ms stddev 0.049
progress: 6.0 s, 73645.1 tps, lat 0.216 ms stddev 0.023
progress: 7.0 s, 73551.0 tps, lat 0.216 ms stddev 0.060
progress: 8.0 s, 73640.9 tps, lat 0.216 ms stddev 0.018
progress: 9.0 s, 73650.8 tps, lat 0.216 ms stddev 0.027
progress: 10.0 s, 73753.5 tps, lat 0.215 ms stddev 0.068
对比一次取完所有数据的性能:
pg95@db-172-16-3-150-> vi test.sql
select * from test where info @> '{1}'::int[] order by crt_date desc;

pg95@db-172-16-3-150-> pgbench -M prepared -n -r -f ./test.sql -P 1 -c 16 -j 16 -T 30
progress: 1.0 s, 219.9 tps, lat 68.165 ms stddev 7.355
progress: 2.0 s, 233.8 tps, lat 67.849 ms stddev 15.181
progress: 3.0 s, 238.4 tps, lat 68.023 ms stddev 10.556
progress: 4.0 s, 233.9 tps, lat 68.030 ms stddev 4.459
progress: 5.0 s, 233.6 tps, lat 68.019 ms stddev 4.131
progress: 6.0 s, 235.5 tps, lat 67.472 ms stddev 3.204
progress: 7.0 s, 237.7 tps, lat 67.627 ms stddev 3.257
progress: 8.0 s, 233.5 tps, lat 67.779 ms stddev 4.815
progress: 9.0 s, 238.7 tps, lat 67.723 ms stddev 7.603
progress: 10.0 s, 232.0 tps, lat 68.098 ms stddev 13.948

[参考]
1. http://www.postgresql.org/docs/9.4/static/functions-array.html
时间: 2024-10-02 12:10:23

PostgreSQL 用游标优化的一个例子的相关文章

PgSQL · 性能优化 · PostgreSQL TPC-C极限优化玩法

简介 本文以工业界测试模型TPC-C为测试模型,介绍PostgreSQL数据库从系统层面的优化到数据库层面的优化方法. TPmC从 256195.32 提升到 606466.31 是如何做到的. 测试环境介绍 16核开HT共32线程 256G 1600MHz 内存 万兆网卡 3 块 6.4TB AliFlash PCI-E SSD 逻辑卷条带 XFS 数据块对齐 XFS文件系统优化 主要分3块: 逻辑卷优化部分 XFS mkfs 优化部分 XFS mount 优化部分 以上几个部分都可以通过ma

面包含点-PostGresql SQL性能优化求助

问题描述 PostGresql SQL性能优化求助 点表:create table point_p(flong float8flat float8userid int4);insert into point_p(flongflatuserid) values (113.12655922.6553671);insert into point_p(flongflatuserid) values (113.02934522.6219592);insert into point_p(flongflatu

《深入理解Elasticsearch(原书第2版)》一2.1.4 一个例子

2.1.4 一个例子 现在,我们已经了解评分的工作原理.接下来我们看一个在现实生活中应用评分的简单例子.首先我们需要创建一个名为scoring的新索引.使用如下命令创建这个索引: 简单起见,我们使用了只有一个物理分片和0个副本的索引(我们不需要在这个例子中关心分布式文档频率).我们需要索引一个简单的文档,代码如下: 接着我们执行一个简单的匹配(match)查询,查询的词项是"document". Elasticsearch返回的结果如下: 显然,刚才索引的这个文档被匹配上了,并且被赋予

怎样用一个例子讲解StarUML中的用例图、类图、时序图 ?

问题描述 怎样用一个例子讲解StarUML中的用例图.类图.时序图 ? 老师让我讲解StarUTML中的用例图.类图.时序图 , 我不想让老师失望 , 求解啊 请大家能给我一个简单例子 谢谢了

给你一个例子:FileFilter接口的使用。

原问题:怎么检查服务器的c:\是否有test.jpg这个文件(不知道扩展名)===================首先,定义一个FileFilter的实例    private static FileFilter fileFilter=new FileFilter(){        public boolean accept(File pathname) {            String tmp=pathname.getName().toLowerCase();            i

一个例子

一个例子这一章,我们要把我们已学的知识集合起来.具体来讲,我们来写一个使用ODBC APIs的程序.为简单起见,这个程序中我使用Microsoft的Access数据库(Microsoft Access 97) . 下载例子源程序. 注意:如果你使用的windows.inc 是1.18及其以下版本,在开始编译之前要修改其中的一个小bug.在windows.inc中查找 "SQL_NULL_HANDLE",将得到下面这行: SQL_NULL_HANDLE equ 0L 将0后面的"

网站优化的一个理想标准:内容多而不乱

很多站长都非常喜欢SEO,本文也不例外,为自己的网站做一些比较基础的SEO知识,希望能让自己的网站得到更多更高效的外部流量,但是在做SEO的过程中并不是一帆风顺的,尤其是刚开始的时候,并非我们无从下手,而是我们下手之后发现需要处理的细节问题有太多,这里需要符合用户,哪里需要符合搜索引擎,当所有这一切都从头开始.从无到有的时候,我们是否会感觉有一种无所适从.手忙脚乱的感觉,所以今天这篇文章给大家分享的就是网站优化的一个理想标准:内容多而不乱.下边从四个方面来阐述我个人的看法,也欢迎批评. 首先是做

搜索引擎优化的一个简短历史

  在互联网的初期,搜索引擎优化是一个很简单的事情.管理员(今天作为SEO的你和我)在优化他们的网站很简单.为了提高一个关键字在搜索引擎的排名,所需要做的只是把他们的标题,关键字,meta标签在他们网站上随意的提及就可以了.但是如果你现在这么做,有可能导致出现"关键词堆砌"-一种在meta标签频繁使用关键字和在图片后面隐藏关键字来达到欺骗搜索引擎的技术.   搜索引擎赶上了这些站长使用的伎俩,并引入了一个会影响网站在搜索结果中排名的的因素.就是链接到该网站的链接数量.这是谷歌当时的一种

Spring中基于aop命名空间的AOP 一(一点准备工作和一个例子)

在某些时候,我们工程中使用的JDK 不一定就是1.5 以上,也就是说可能不支持Annotation 注解,这时自然也就不能使用@AspectJ 注解驱动的AOP 了,那么如果我们仍然想使用AspectJ 灵活的切入点表达式,那么该如何呢?Spring 为我们提供了基于xml schematic 的aop 命名空间,它的使用方式和@AspectJ 注解类似,不同的是配置信息从注解中转移到了Spring 配置文件中.在这里,我们将详细介绍如何使用Spring 提供的<aop:config/> 标签