一位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. 【Java开发】Maven那点事儿(Eclipse版)

    Maven那点事儿(Eclipse版) 前言: 由于最近工作学习,总是能碰到Maven的源码.虽然平时工作并不使用Maven,但是为了学习一些源码,还是必须要了解下.这篇文章不是一个全面的Maven解 ...

  2. vs2015试用到期,不能输入序列号

    如果是社区版,登录账号即可, 如果不能登录账号,可以执行修复再登录账号: 控制面板-程序和应用-vs2015(我写的是简称)-右键-更改-修复-输入序列号

  3. IDEA 2019.2破解激活教程(激活到2089年8月,亲测有效,持续更新中...)

    本来笔者这边是有个正版激活码可以使用的,但是,2019.9月3号的时候,一些小伙伴反映这个注册码已经失效了,于是拿着自己的 IDEA, 赶快测试了一下,果不其然,已然是不能用了. 好在,笔者又找到了新 ...

  4. javascript图片库威力增强版

    <html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Con ...

  5. airflow原理

    官网: http://airflow.apache.org/installation.html 原理: https://www.cnblogs.com/cord/p/9450910.html 原理介绍 ...

  6. [转帖]Nginx 容器教程

    Nginx 容器教程 http://www.ruanyifeng.com/blog/2018/02/nginx-docker.html 里面有证书. 作者: 阮一峰 日期: 2018年2月27日 感谢 ...

  7. # Python 3 & 爬虫一些记录

    目录 Python 3 & 爬虫一些记录 交互模式和命令行模式 函数积累 语法积累 列表和元组 输入 交互模式下输入多行 爬虫 HTTP报文请求头User-Agent信息 解析库pyquery ...

  8. 如何利用swoole搭建一個簡易聊天室

    <?php class Chat { const HOST = '0.0.0.0';//ip地址 0.0.0.0代表接受所有ip的访问 const PART = 82;//端口号 private ...

  9. Python【print函数】

    下面是 print函数的一种用法,用逗号隔开,可在同一行打印不同类型的数据.x = input('请你输入被除数:')y = input('请你输入除数:')z = float(x)/float(y) ...

  10. T100——作业action执行其他P作业,后台背景执行完后才能继续操作改作业

    范例:如axmt500订单,查询开单占用量: 客制作业cxmp500,通过参数-订单号,查询该订单下的料件,目前有库存量.开单占用量.库存可用量,查询后更新到该订单下的单身对应栏位: 现在axmt50 ...