一位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

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

  1. Java泛型和编译优化的一个例子

    public class Main { public static void main(String[] args) { ArrayList<String> strList = new A ...

  2. postgresql 使用游标笔记

    游标介绍:游标是一种从表中检索数据并进行操作的灵活手段,游标主要用在服务器上,处理由客户端发送给服务端的sql语句,或是批处理.存储过程.触发器中的数据处理请求. 游标的优点在于它允许应用程序对查询语 ...

  3. linux一个例子驱动

    我们介绍的驱动称为 short (Simple Hardware Operations and Raw Tests). 所有它做 的是读和写几个 8-位 端口, 从你在加载时选择的开始. 缺省地, 它 ...

  4. matlab实现梯度下降法(Gradient Descent)的一个例子

    在此记录使用matlab作梯度下降法(GD)求函数极值的一个例子: 问题设定: 1. 我们有一个$n$个数据点,每个数据点是一个$d$维的向量,向量组成一个data矩阵$\mathbf{X}\in \ ...

  5. 一个例子"入坑"布谷鸟算法(附完整py代码)

    布谷鸟是比较新的启发式最优化算法,但其与传统的遗传算法,退火算法等相比,被证明收敛速度更快,计算效率更高! 文章目录 本文诞生的缘由 布谷鸟算法思想简介 更新位置的方式 莱维飞行 局部随机行走 抛出个 ...

  6. spring笔记--使用springAPI以及自定义类 实现AOP的一个例子

    Spring的另一个重要思想是AOP,面向切面的编程,它提供了一种机制,可以在执行业务前后执行另外的代码,Servlet中的Filter就是一种AOP思想的体现,下面通过一个例子来感受一下. 假设我们 ...

  7. ReCap 360 photo照片建模技术的又一个例子

    这是我做的又一个利用Autodesk ReCap 360 照片建模技术做的一个例子.你可以下载模型自己把玩,或者下载原始照片自己试一试. 拍摄工具: 小米手机 照片数量:约120张 后期处理工具: p ...

  8. VS编译器优化诱发一个的Bug

    VS编译器优化诱发一个的Bug Bug的背景 我正在把某个C++下的驱动程序移植到C下,前几天发生了一个比较诡异的问题. 驱动程序有一个bug,但是这个bug只能 Win32 Release 版本下的 ...

  9. 从一个例子中体会React的基本面

    [起初的准备工作] npm init npm install --save react react-dom npm install --save-dev html-webpack-plugin web ...

随机推荐

  1. 软件测试第4周小组作业:WordCount优化

    一.基本任务:代码编写+单元测试 1.Github地址: https://github.com/Wegnery/New_WordCount 2.PSP2.1表格 PSP2.1 PSP阶段 预估耗时 ( ...

  2. python列表和if语句的简单结合

    将列表所有元素打印出来 cars = ['toyota', 'honda', 'mazda', 'nissan', 'mitsubishi', 'subaru', 'suzuki', 'isuzu'] ...

  3. [转帖]intel发布会之前,漫谈CPU核心架构:CCX、Ring Bus、Mesh

    intel发布会之前,漫谈CPU核心架构:CCX.Ring Bus.Mesh https://baijiahao.baidu.com/s?id=1607585351741429318&wfr= ...

  4. 【Python】【demo实验16】【练习实例】【打印所有水仙花数】

    题目: 打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身.例如:153是一个"水仙花数",因为153= ...

  5. Oracle导入/导出某个用户下的数据库

    导出 exp用户名/密码@数据库实例owner=用户名file=文件存储路径 例如:exp MM/123456@ORCL owner=MM file=F\abcd.dmp 导入 imp用户名/密码@数 ...

  6. Neo4j WARNING: Max 1024 open files allowed, minimum of 40 000 recommended. See the Neo4j manual

    you can add a line in /etc/default/neo4j: NEO4J_ULIMIT_NOFILE=60000 to set the ulimit setting (60000 ...

  7. 怎样通过id属性快速从HTMLCollection对象中获取到目标元素节点

    方法1: 直接使用id或name属性: 比如我想获取 id 为 img1 的图片元素节点, 则可以这样写: document.images.img1 如果没有对应节点, 则返回undefined; 方 ...

  8. Scala学习十二——高阶函数

    一.本章要点 在Scala中函数是”头等公民“(可以作为参数,返回值,赋值给其他); 可以创建匿名函数,通常还会交给其他函数; 函数参数可以给出需要稍后执行的行为; 许多集合方法都接受函数参数,将函数 ...

  9. 【原创】大数据基础之ETL vs ELT or DataWarehouse vs DataLake

    ETL ETL is an abbreviation of Extract, Transform and Load. In this process, an ETL tool extracts the ...

  10. HttpWorkerRequest应用简介

    1. Using HttpWorkerRequest for getting headers1.使用HttpWorkerRequest获取headers信息 First, the HttpWorker ...