2亿数据量PostgreSQL 10.4查询调优思路分享
目录
●背景
●使用物理服务器
●增加内存大小
●使用NVMe协议的固态硬盘
●将数据库安装在内存
●业务调整
●修改默认配置项
●启用Gin (Generalized Inverted Index)索引(仅适用于非前缀模糊查询)
●启用Gist(Generalized Search Tree)索引(仅适用于非前缀模糊查询)
●小结
●背景
最近工作中遇到了数据库的查询瓶颈,自己研究后,准备写一篇博文记录一下。先交代一下背景,这次调优针对的是PostgreSQL 10.4,安装环境为CentOS Linux release 7.2.1511(位于虚拟机),分配CPU为Intel Xeon Silver 4114的4个内核,64GB内存,以及400GB的SSD空间。表单包含158列,按月进行分区,采用PostgreSQL 10.4的partition by range分了一年的分区。并且对主键和待查询的字段“identity_no”建立的Btree索引。这里的identity_no用来区分一个地理位置,由省份简称+市区邮编+4位数字英文混合编号组成,大家只需知道它是一个含中文汉字+数字+英文的varchar格式的字段即可,它并不是一个唯一的值,表单中存在重复值。
CREATE INDEX "idx_info_201903_1" ON "public"."info_201903" USING btree ("id" "pg_catalog"."int4_ops" ASC NULLS LAST
);CREATE INDEX "idx_info_201903_2" ON "public"."info_201903" USING btree ("identity_no" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
测试的SQL很简单,结合业务采用分页的方式,先测总的记录条数,再取每页的结果集。测试精确查询、带前后缀的模糊查询:
SELECT count(*) FROM info WHERE identity_no = '沪200000C544' ;SELECT * FROM info WHERE identity_no= '沪200000C544' LIMIT 30 OFFSET 50000;SELECT count(*) FROM info WHERE identity_no like '京102%' ;SELECT * FROM info WHERE identity_no like '京102%' LIMIT 30 OFFSET 50000;SELECT count(*) FROM info WHERE identity_nolike '%S888' ;SELECT * FROM info WHERE identity_no like '%S888' LIMIT 30 OFFSET 50000;
整个测试环境下,单表分12个区,共计2亿条数据。实际测试中,该字段进行精确查询时,平均耗时在10-200ms这个范围内;但模糊查询时,特别是无前缀的情况下,耗时均超过5分钟,导致业务严重不可用。即使是带前缀的查询,通过执行计划(explain)查看,虽然使用了索引,但由于数据量实在过于庞大,查询依旧很慢。因为分区也做了、索引也用了、SQL也已经简单到不能继续优化的地步,考虑了如下的调优思路。
●使用物理服务器
测试过程中使用的是虚拟机作为服务器,性能势必没有直接使用物理服务器高,实际生产过程中建议使用物理服务器作为数据库服务器。
●增加内存大小
通过Linux命令top查看,目前PostgreSQL已经申请了64GB内存的近62GB作为缓存使用,而且在执行计划中可以看到有不少查询会进行Bitmap Heap Scan节点操作,该节点会将索引查出的数据一次取出放入内存进行排序,如果取出的数据过多,内存放不上,势必会分步操作,将部分数据先存放磁盘,再加载进内存,如此往复,势必没有直接一次放入内存的效果好。
●使用NVMe协议的固态硬盘
通过Linux命令iostat查看,最大的瓶颈来源于磁盘I/O,已经处于满负荷运转了。虚拟机使用的是固态硬盘,该硬盘使用AHCI协议,SATA3.0接口,理论顺序读取速度仅500MB/s,实际使用中因其他进程等影响,读取速度会更慢。若更换为NVMe协议的固态硬盘,PCI-E或U.2的接口,理论带宽32Gbps,能极大提高磁盘I/O速率。
●将数据库安装在内存
PostgreSQL不能直接装在内存,只能将高频数据加载进内存。若想把程序和所有数据全部安装在内存可以考虑使用曲线救国的方案。即将内存虚拟为硬盘,把内存当硬盘使用,但由于内存的读写效率是远高于机械硬盘,甚至固态硬盘的,因此这种情况下I/O效率会非常高。对于Linux系统,可以采用挂载分区的方式,将内存等同于硬盘逻辑分区使用;对于Windows系统,可以采用Primo Ramdisk软件将内存虚拟为硬盘。这种方案风险很高,即使使用UPS进行不间断供电,解决断电时数据擦除问题,也无法保证内存硬件故障时数据丢失;同时,这种方案的经济成本也会很高。
●业务调整
此条方案适合特定的查询。例如在2亿量级数据查询时,用户即使只选择某几天时间段的数据,因为数量实在太多(数十万甚至上百万条),按原本分页需求,需要先查询总记录条数,再查询所选页的记录数。通过执行计划可以看到,耗时慢查询主要是前者,后者返回相对较快。即使总记录条数可以使用异步的方式返回给前端页面,用户依旧需要等两三分钟才能看到共多少条记录以及共多少页。与其这样,不如设定一个阈值,例如5秒内未查询出记录条数,直接异步通知前端页面条数10000+,同时不再去执行count(*)的操作,减少系统I/O开销。这种非精确的计数业务上并不会造成不好的用户体验,即使百度采用搜索引擎来做查询,返回的总数也是用“约”进行修饰的。
用户可以正常翻页,如果其输入的页码过大,导致查询不到数据,页面只需做一个友好跳转进行提示即可。因此没必要在此业务环境去浪费过多的I/O资源。需要查询某时间段内数据总数的统计分析业务环境再单独去进行完整的count(*)操作,或者使用一些预加载的技术。
●修改默认配置项
修改postgresql.conf中的默认配置项,根据网上的一些案例实际测试修改过random_page_cost = 1;fsync = off;parallel_tuple_cost = 0.1;max_parallel_workers_per_gather = 4。通过观察发现实际收效很低,几乎无明显变化,慢查询过程中依旧是在进行高负荷的磁盘读写。通过PostgreSQL的配置项针对性调整,理论上是能提高数据库性能的,但该方案还需继续深入研究与测试。
●启用Gin (Generalized Inverted Index)索引(仅适用于非前缀模糊查询)
在不更换数据库的前提下,借鉴搜索引擎中倒排索引的思路,对查询限制条件对应字段建立Gin索引。目前数据库建立索引的常规做法是采用Btree形式,由于其二叉树的特性,排序和查询会非常快。而通过测试我们发现,现实业务中,可能会存在identity_no的模糊查询,特别是无前缀的条件下,Btree索引会失效,因此考虑使用Gin索引。根据官方手册,Gin索引是用来加快全文搜索的,适合做模糊查询和正则查询。
为了验证Gin索引是否适用,进行1000W量级的数据测试:
数据库 |
PostgreSQL 10.4 |
||
操作系统 |
CentOS Linux release 7.2.1511 |
||
SSH连接客户端 |
PuTTY 0.7 |
||
测试表单 |
info_gin |
||
分区情况 |
分区名 |
数据量 |
|
201802 |
627001 |
||
201803 |
862729 |
||
201804 |
833407 |
||
201805 |
860097 |
||
201806 |
834221 |
||
201807 |
859458 |
||
201808 |
863205 |
||
201809 |
833041 |
||
201810 |
859668 |
||
201811 |
833983 |
||
201812 |
857917 |
||
201901 |
863472 |
||
201902 |
12414 |
||
总计 |
10000613 |
||
分区索引情况 |
无索引(字段格式varchar) |
占10GB空间 |
|
对identity_no建立Btree索引(字段格式varchar) |
占10GB空间 |
||
对identity_no建立Gin索引(字段格式text) |
占10GB空间 |
||
对identity_no建立Gist索引(字段格式text) |
占10GB空间 |
接下来分别测试Btree索引、Gin索引两种情况下,进行模糊查询的耗时,结果如下表:
Btree索引 |
Gin索引 |
|
count(*) like '京%'' |
13.49秒 |
16.34秒 |
count(*) like '京10%' |
5.71秒 |
10.14秒 |
count(*) like '京1042%'' |
0.78秒 |
4.05秒 |
Limit 30 like '京%'' |
0.25秒 |
7.33秒 |
Limit 30 like '京10%'' |
1.31秒 |
6.34秒 |
Limit 30 like '京1042%'' |
0.50秒 |
2.54秒 |
count(*) like '%55' |
3.79秒 |
1.31秒 |
Limit 30 like '%55" |
2.62秒 |
1.01秒 |
count(*) like '%00D5%' |
3.99秒 |
1.81秒 |
Limit 30 like '%00D5%' |
2.07秒 |
1.02秒 |
值得注意的是,Gin索引天生是用于全文检索的场景,因此,需要将identity_no字段从varchar类型更换为tsvector类型。PostgreSQL本身不支持中文分词,因此还需要安装额外的开源插件scws和zhparser。实际测试发现,对于identity_no字段分词效果不明显,因为仅有一个中文,其后也不存在完整英文单词,所以利用倒排索引分词匹配的效果不好。结合执行计划可以看到带上前缀进行模糊查询时,走了全表顺序扫描,效率反而不如加Btree索引,仅有后缀和中缀查询效果略好一些。同时,考虑到使用Gin索引的查询SQL语句也和常规写法不同,例如正常写select count(*) from info_gin where identity_no like '京%'使用Gin索引时需要写为select count(*) from info_gin where identity_no @@ '京:*',这种SQL的变化,对于ORM框架是否友好需要进一步求证。总之,因为identity_no字段中内容很短,拆分分词进行倒排索引检索效果一般,该方案仅作为一种思路参考。
●启用Gist(Generalized Search Tree)索引(仅适用于非前缀模糊查询)
Gist索引是一种基于广义搜索树建立的索引,网络上有相关案例用其做中缀模糊查询。为了验证Gist索引实际使用效果,进行1000W量级数据测试,测试条件如上一条所示。从占用空间来说,Gist与其他索引无异。接下来分别测试Btree索引、Gist索引两种情况下,进行模糊查询的耗时,结果如下表:
Btree索引 |
Gist索引 |
|
count(*) like '京%'' |
13.49秒 |
19.45秒 |
count(*) like '京10%' |
5.71秒 |
12.25秒 |
count(*) like '京1042%'' |
0.78秒 |
3.25秒 |
Limit 30 like '京%'' |
0.25秒 |
6.91秒 |
Limit 30 like '京10%'' |
1.31秒 |
6.22秒 |
Limit 30 like '京1042%'' |
0.50秒 |
1.74秒 |
count(*) like '%55' |
3.79秒 |
1.80秒 |
Limit 30 like '%55" |
2.62秒 |
0.87秒 |
count(*) like '%00D5%' |
3.99秒 |
1.25秒 |
Limit 30 like '%00D5%' |
2.07秒 |
0.32秒 |
测试结果与Gin索引类似,对于带前缀的模糊查询,效果均不如Btree索引,仅带后缀和中缀的效果要优于Btree索引。该方法同样具有劣势,根据官方手册介绍,Gist索引查出的数据是“有损(lossy)”的,即可能有出现丢失,不适合精确使用的业务场景。不过对比Gin索引,建立Gist索引在SQL的语法上依旧是使用like和%进行模糊查询,兼容性较好。
●小结
笔者并非专业DBA,只是临时帮忙测试下PostgreSQL的性能,因发现性能瓶颈,自己学习思考了一些调优思路,不一定正确,大家辩证看待,如果有其他方案,也希望能留言一起交流分享。由于涉及具体公司业务,详细的表单结构、索引设置、数据库参数等不能和大家分享,因此还需要大家针对具体问题具体分析,从这三个方面也考虑下是否可以调优。
2亿数据量PostgreSQL 10.4查询调优思路分享相关推荐
- 千亿级数据量kafka集群性能调优实战总结
1.(千亿级kafka集群性能调优)集群信息 一个kafka集群,40台broker,基于Ambari,hdp管理(ambari_v2.5,hdp_v2.6) 10台broker配置5块3T盘 30台 ...
- 入职第一天,老板竟然让我优化5亿数据量,要凉凉?
jsoncat:https://github.com/Snailclimb/jsoncat[1] (仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架) 前段 ...
- Redis 10亿数据量只需要100MB内存,为什么这么牛?
作者:java架构设计 来源:toutiao.com/i6767642839267410445 本文主要和大家分享一下redis的高级特性:bit位操作. 力求让大家彻底学会使用redis的bit ...
- flink读取不到文件_日处理数据量超10亿:友信金服基于Flink构建实时用户画像系统的实践...
简介: 友信金服公司推行全域的数据体系战略,通过打通和整合集团各个业务线数据,利用大数据.人工智能等技术构建统一的数据资产,如 ID-Mapping.用户标签等.友信金服用户画像项目正是以此为背景成立 ...
- 现身说法:实际业务出发分析百亿数据量下的多表查询优化
今天给大家带来的讨论主题是通过实战经验来对百亿数据量下的多表数据查询进行优化,俗话说的好,一切脱离业务的架构都是耍流氓,接下来我就整理一下今天早上微信群里石头哥给大家分享的百亿数据量多表查询架构以及优 ...
- Redis基本使用及百亿数据量中的使用技巧分享
作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9941208.html 作者:大石头 时间:2018-11-10 晚上20:00 地点:钉钉群(组织代码B ...
- 这么设计,Redis 10亿数据量只需要100MB内存
点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 来源:www.toutiao.com/i676764283 ...
- 面对 10 亿数据量的挑战,如何对系统进行性能优化?
作者 | 中华石杉 责编 | 伍杏玲 本文经授权转载自石杉的架构笔记 这篇文章,我们来聊一聊在十亿级的大数据量技术挑战下,世界上最优秀的大数据系统之一的Hadoop是如何将系统性能提升数十倍的? 首先 ...
- 百亿数据量下,掌握这些Redis技巧你就能Hold全场
来源:https://0x9.me/aos9t 一.Redis封装架构讲解 实际上NewLife.Redis是一个完整的Redis协议功能的实现,但是Redis的核心功能并没有在这里面,而是在NewL ...
最新文章
- c#保存数据格式为.cvs_C#读取csv格式文件的方法
- 2、C#基础 - Visual Studio 的版本选择和下载
- 华为鸿蒙话题作文800字,关于鸿蒙OS 华为最高层发布最新通知:统一口径-华为,智能手机,鸿蒙...
- 数字类 default 0和 default 0_全方位的数字规划工具Visual Components 4.0 数字化工厂仿真软件...
- 高手过招:用SQL解决环环相扣的刑侦推理问题(苏旭辉版本)
- Leetcode每日一题:57.insert-interval(插入区间)
- C++第一个综合项目
- 学大数据要学哪些算法_大数据学习之不得不知的八大算法
- Android 记住账号密码+自动登录
- python处理异常的方式_Python报错出现异常的介绍,及其处理方式
- 数字电子技术基础(四):门电路(CMOS)必看
- 在不受支持的 Mac 上安装 macOS Monterey 12(OpenCore Patcher)
- 数学建模之方差分析基础--单因素,双因素方差分析与matlab实现
- ctfshow-Crypto-新生赛
- 单片机一键开关机电路,多种方案可供选择,有纯硬件的也有软硬结合的
- 计算机桌面声音图标,win7桌面右下角的小喇叭音量图标不见了怎么办?
- json 格式字符串
- SQL注入基础--判断闭合形式
- 计算机用老毛桃u盘备份系统,老毛桃一键还原,教您电脑如何使用老毛桃一键还原...
- 不是有效的win32应用程序_什么是模块,VBA各种模块的有效行为