集群规划中影响性能的因素
架构设计
并行处理时,用户查询的处理速度取决于集群里最慢的数据库实例的完成时间。所以,当各节点服务器硬件配置不一样时,配置高的机器处理速度快,配置低的机器处理慢,此时短板就是配置较差的机器,影响整体性能。如果想再硬件上提升数据库性能,就需要均衡各个节点的服务器配置才有用。
还有一种情况是实例处理数据量不均衡。这可能是建表时分布键选择不正确,导致数据倾斜到某些节点,导致该实例上的节点要处理的数量高于其他节点,导致处理时间相对于其他节点变慢,最终导致整个查询速度变慢。这种情况可以通过表的优化来避免。
还有镜像的分部策略也可能影响数据库性能。这种情况是在数据库中有主实例down了之后才会体现出来。上图的架构中,每个节点有4个主实例和四个备实例,采用GROUP策略,将4个备实例全部放在另一台服务器上,如果一台服务器down了,另一台服务器上的镜像就会启动工作,这样的话该节点就相当于8个数据库实例在运行。相比其他运行4个数据库实例的节点,性能下降一半。如果是采用SPREAD策略,则4个备实例是分别处于其他4台服务器上,这种策略相对于GROUP策略的性能下降没有那么严重,但是发生主备同时down的几率更大,更容易导致数据库不可用(只要有1对主备实例都down了就会不可用),相对没有那么稳定,这就需要根据实例需要选择某一个策略。
服务器配置
CPU开启超线程
CPU内核只能处理一项任务,但目前CPU处理速度很快,通常使用率达不到100%。在GP数据库,可以通过开启CPU超线程,提高GP数据库的并行处理能力。
磁盘IO性能
大部分的数据库性能瓶颈都是磁盘IO这块。如果数据量不大,可以直接使用SSD磁盘。但是对于一些大型的数据仓库,数据量很大,由于成本的原因,可能会采用机械硬盘(例如SAS磁盘)来存储。此时我们可以对多块磁盘做RAID5,并且采用较好的磁盘阵列卡来保证磁盘性能。
万兆网络
GP数据库推荐使用万兆网络,因为在表关联时,可能会涉及重分布、广播,以及主备实例的数据同步,都需要消耗网络资源。如果是千兆网络,就有可能出现由多台机器组成的GP集群的性能还不如一台机器的性能。
上述的这些性能,可以通过gpcheckperl来测试,根据测试情况来做硬件上的调整。
Segment实例数量
Segment主实例数量对数据库性能影响较大,当数据库进行同一个查询时,2个segment和10个segment的性能是不同的,除非服务器性能指标很差,2个实例就达到服务器某个指标的瓶颈。但是也不是实例越多越好,实例数量越多,集群越不稳定,因为当并发数多的时候,就容易出现资源不足的情况,而导致集群崩溃。具体的实例数量,可以根据下图中的因素来考虑。
如果业务的并发数很高,可以采用较少的实例数量,比如4个、6个,优先保障集群的稳定。如果业务并发数很低,则可以适当增加实例数量。不过这个没有标准,最好的方法是通过测试,根据实例业务决定。目前比较流行的是使用4个主实例,如果机器配置较好则可以配置8个主实例。
数据库性能优化内容
本节主要是对性能提升的技术优化。
内存管理
数据库运行在操作系统上,先从操作系统层面设置内存的使用。
首先是vm.overcommit_memory,系统默认是0,表示内存不够用时,内存申请就会失败。推荐将该参数设置成2,这样vm.overcommit_ratio这个参数才会生效,将其设置为95是为了最大化利用内存。
其次是数据库方面的参数gp_vmen_protect_limit。下图给出了计算公式,需要注意的是实例的数量是包括镜像的数量,应为要考虑到主实例挂掉的情况。
上图中的三个参数是从整体上控制内存的使用。
资源队列的内存管理
GP数据库默认使用资源队列的内存模式管理。在该模式下,通过设置gp_resqueue_memory_policy来配置内存的使用。
上图中的max_statement_mem和statement_mem是以查询为单位,给出了计算公式。
可以看出,当gp_resqueue_memory_policy设置为eager_free时,可以最大化利用内存,做到物尽其用。但是存在一个问题,当并发数多的时候,容易出现OOM(out of memory)的情况。如果是auto模式,则不容易出现内存不足,但缺点是内存利用率不高,容易产生溢出文件,而且如果磁盘性能较差,则对整个集群的性能影响较大。
资源队列
资源队列可以限制并发的查询数、查询使用的内存总量。如果查询超过了这两个限制,则会等待,直到有资源可以使用的时候才可以执行。如果MEMORY_LIMIT没有做限制,则存在内存缺口,可能出现offmemory的情况。
资源队列还可以控制优先级,就是在同一个队列的成员中,通过设置优先级来决定谁来优先使用当前的资源。
根据查询成本代价做限制,在运行的查询中,查询的成本是由查询优化器来评估。对小于MIN_COST的查询,是不会受其他因素的影响,直接运行。当大于MAX_COST时,查询会被拒绝。
资源组
资源组是数据库另外一种管理模式,GP4是没有这个功能,GP5之后的版本才有这个管理模式。
它的内存管理模式有2种。一种是基于角色,就是对数据库用户分配资源。另一种是基于外部组件(例如PXF这些)
在并发事务限制这块,当资源组内的并发事务达到了设置的限制时,后面的查询会等待。
资源组和资源队列的区别
在GP数据库中,只能启用其中一种。
表存储
在GP数据库中,创建表时可以选择不同的存储类型。我们需要清楚什么时候使用堆存储,什么时候使用AO存储,还有行存和列存。特别是对于大的事实表,用对了存储模式对性能影响很大。
堆表的操作是先写入事务日志文件,等到了数据库设置的触发点才会刷盘,这个触发点与参数设置的事务日志的数量和大小有关。
AO表的每个事务都会产生新的数据块,如果对AO表进行大表事务的提交,IO消耗会很严重。所以这种存储类型不适合频繁更新的表,比较适合数据批量写入、数据加载后很少更新的大表。
行存时,读取某一列时,需要把前面的列都要扫一遍,所以读取越靠后的列成本越高。
列存时,由于一列的数据类型都相同,所以可以做到更高压缩比,占用空间更小。
存储模式及对比
Heap表是默认存储模式。
数据加载
使用gpfdist时,有两个可以调优的参数,writable_external_table_bufsize(数据导出时)和gp_external_max_segs(数据加载时)。
下图是调整这两个参数对于gpfdist数据加载的性能影响。可以看出writable_external_table_bufsize设置到16M时性能最好。gp_external_max_segs设置到40时已经到了文件服务器网络的瓶颈,如果还要提升性能,可以增加文件服务器的数量,把文件拆开放到不同的文件服务器上,提高入库性能
其他优化点
表设计
——尽量设计仅允许插入的表
因为删除和更新都会产生数据膨胀,我们可以在表层面设计、避免这些操作,可以减少日后表的维护工作
表分布键
——优先选择唯一性比较高的单个字段做分布键
如果没有这种字段,可以选择2个或3个组合字段做分布键,如果还是没有,那就只能选择随机分布了
表分区
——对大的表进行分区,优化不同的分区存储模式
例如经常需要更新的分区可以采用堆存储,访问量比较少的分区可以采用压缩存储
索引(索引在GP数据库不是必须的)
——注意索引的膨胀维护
这一点容易忽略,如果不对索引做维护,那么索引会一直膨胀,最后索引的大小可能比表数据的大小还大,这样索引就失去了意义,而且还导致查询效率降低。
日常维护对性能的提升
统计信息
如果执行计划有问题,可能造成一张大表申请的资源少,导致执行缓慢。或者一张小表多,导致浪费资源。
Analyze是对表的内容做随机抽样的采集统计,默认值是25,取值范围是1至1000。如果希望提高统计的精确度,可以加大这个参数值。Analyze是对表的所有列做自动分析,如果自动数量过多,则会影响统计效率,可以选择表的列收集统计信息,特别是对于字段数比较多的表会有明显的性能提升。
收集统计信息
gp_autostats_mode可以选择none,然后自己写脚本维护统计信息。或者根据实际情况运行analyze。(如果让数据库自己触发,则会影响性能表现)
数据膨胀
检测膨胀
运行analyze收集的统计信息都会保存在系统表中。
下图中,heap表的预期页数是2,实际页数是637,膨胀严重。
下图中的AO表通过函数来查看膨胀情况,其中percent_hidden是显示每个文件被标记5次的百分比。可以看到有一半的数据是无效的。实际的操作是全量删除表,再插入,导致这种膨胀现象。
膨胀处理
空闲映射空间是可以追踪设定大小范围内的垃圾数据,如果超过这个范围就无法追踪,也就无法回收。
提高maintenance_work_mem可以提高执行效率。
对于AO表,gp_appendonly_compaction_threshold设置的值的单位是百分比,只有膨胀高于这个百分比,vacuum才会生效。
数据倾斜
可以通过查询数据目录的大小来判断是否出现数据倾斜。
对于表的数据倾斜,通常是分布键设置不合理,可以修改分布键或者随机分布
计算倾斜
在表关联计算时,如果关联的字段唯一性不高,执行计划用这个字段重分布的话,数据就会产生倾斜。
gp_workfile_compress_algorithm对于性能较差的集群比较有用。对于磁盘性能较好,启用了这个参数,性能可能反而变差。
计算倾斜排查过程
如果出现计算倾斜并且产生溢出文件,可以通过监控溢出文件来找到执行的SQL。溢出文件都放在实例数据目录的pgsql_tmp目录下。
- 查看是否存在溢出文件
- ll该文件
- lsof找到打开该文件的pid
- ps查看进程信息,其中包括会话ID
- 根据会话ID,去数据库pg_stat_activity查看、定位至SQL
系统表优化
避免元数据数量过多,即避免一个数据库中表的数量太多。例如滥用分区表,表的数量达到百万级,可能导致集群性能下降严重。
作业流程优化
避免锁的发生。
下图中脚本是采用记录这两张表快照的方式,查到历史时间中存在锁的作业,根据实际情况从业务上做优化。
SQL优化技巧
从执行计划中优化
SQL的优化主要就是要看懂执行计划,然后根据执行计划来优化。
重分布:重新按照某个字段做分布键来做数据的分布
广播:每个数据库实例上都有一份表的全量数据
执行计划从下往上看,最后的motion汇总到master节点上。
执行计划中进行SQL优化的方法主要就是减少重分布和广播,或者尽量减少大表做重分布和广播。
union与union all
下图中使用union all的时候是顺序扫描,没有发生重分布。
union的优化
下图左边中,使用union的时候发生了两次重分布的操作。可以通过改写SQL,采用分开插入的方式。如下图右边,与直接采用union的效果是一样的,但是避免了重分布。对比可看出,执行消耗的cost不多,达到了优化的目的。
分布键的优化
很多人有个误区,表的分布键有多个字段的时候,只要用其中一个字段去关联就会达到本地关联的目的,但是其实这也是会产生重分布的。如下图左右两边的例子,test2表分别用id和city做分布键的时候,和test1做关联,但是左边发生了重分布。
一些SQL优化内容
避免出现笛卡尔积
——出现笛卡尔积通常是没有注意关联的字段有重复所导致,所以在关联之前可以做一次去重操作
避免出现计算倾斜
避免向客户端返回大数据量
——注意加limit限制,否则会返回表的所有行
在子查询中尽可能过滤多的行
——在用子查询的SQL中,能够在子查询中限制条件就不要在关联的时候限制条件
避免不必要的排序
——如果没有排序的要求,就不要加上,排序会造成重分布
常见性能问题
用户查询慢
注意加limit限制
进程是否被锁
——如果进程被锁,查询就会等待,造成查询慢的现象
SQL是否可优化
——从执行计划中判断SQL是否存在优化空间
使用表的数据是否有倾斜
——表数据的倾斜是造成查询慢的重要因素,需要保证数据均匀分布,从而充分利用并行计算的优势
表关联中是否有计算倾斜
——如果发现有计算倾斜,可以通过改写SQL来优化
数据库资源是否繁忙
——数据库资源繁忙时,会影响所有的查询语句
数据库运行慢
磁盘的问题通常是慢的原因,可以通过一些工具来排查,例如amond,可以实时查看i磁盘和cpu的繁忙程度。iotop可以进程使用IO的情况,从而定位。
两段事务锁
数据库的分布式事务是通过两阶段来提交,一个事物在管理节点,另一个在实例节点上。通常由于实例节点上存在残留事务,会造成执行删除表命令时,一直在执行删除中。如下图:
处理过程
如果发现是残留的预备事务,则需要先找到GID,然后同ROLLBACK这个命令回滚。
或者重启集群解决一切。