在近期结束的Pivotal全球数据路演中,我们的大数据架构师为大家介绍了很多Greenplum 的宝贵经验和最佳实践。为了给大家提供更好的参阅信息,我们系统地整理了这篇干货长文与大家分享。
项目经验分享
在为诸多客户做项目实施的过程中,我们会总结了一些好的经验,有时我们也根据多方面需求尝试开发一些通用命令。我们有很多好用的经验总结,这些经验体现了Pivotal专业服务的价值,本次选择了2个命令进行分享,不过这些命令不属于官方行为,目前还没有作为官方命令随产品一同提供,仅可通过售后渠道获得。
个性化备份恢复命令
有的时候用户的一些需求会比较特别,我们现有的备份恢复命令无法完全满足,我们在不同的项目里面总结了一些用户的需求,针对性的开发了一个备份恢复命令。我们这个备份恢复命令的特点有,首先我们与gpcrondump一致使用gz压缩,备出来的文件尺寸会比库内小一些;按表和分区,在每一个实例上备出一个文件,通常库中表的数量控制比较合理的情况下,文件数不会成为问题;按分区进行备份;缺省每天备份一个文件夹;可以按模式备份;可将外部表的Error表进行排除,这些表其实很多时候是没有备份价值的;我们的备份有详细的日志,有成功、失败的信息,还可以断点续跑,当备到到某张表的时候,因为某种原因停了,下次重新执行相同的命令,就会接着上一次没跑完的继续往下跑,已经成功的不再运行;还可以指定数据的过滤条件,满足自定义数据备份;整个备份不受个别的失败的影响;备份采用乐观锁,仅对正在备份的表上访问共享锁,上锁失败即认为该表此次备份失败。
我们还做了增量识别,自动识别一张表从上一次备份到本次备份有没有发生过变化,包括Heap表和AO表;可以指定并发数和编码字符集,编码是为了解决一些特殊的乱码问题,命令由单个文件构成,可直接运行,不需要其他的准备。
做恢复时,可以选择恢复哪些数据,可以恢复到不同的库,可以对部分数据进行带条件的恢复;可以在增量备份的基础上,指定恢复某个时间范围的最新数据。
Greenplum集群之间数据传输
集群间数据传输,GP有一个集群之间的数据传输工具gptransfer,但目前这个工具难以满足一些用户的特殊需求,比如视图传输,带条件传输,文本格式报错等。因为GP不管是集群内还是集群之间都不能够跨库访问,虽然我们未来可能在产品这方面解决这个问题,但是目前我们还是需要用自己的办法来解决,我们做了一个比gptransfer这个命令更好用的命令,这个命令我们不是用命名管道的方式来实现的,因为命名管道这种方式有些已知的问题,比如它的状态很难精确控制。
命令的运行需要两个集群之间的网络互通,只要在网络可以互通的情况下,就可以去部署这个命令并执行;源端和目标端的对象名可以不同,可以做带条件的源端数据过滤,源端也可以是视图,命令会自动识别这个视图能不能采用快速模式还是普通模式出数;命令可以在集群外执行,只要这个机器能够同时访问两个master即可;可以指定并发,也可以指定编码格式,解决一些特殊的编码问题;同步时会对源端和目标端的表加必要的锁,以确保不会出现与其他SQL之间的互锁情况,上锁失败即认为该表的此次同步失败。
另外,我们这个命令自己和自己之间也可以并发,就是我们调了一次命令,跑了一些表,然后发现好像资源还有富裕,这时候可以再起一个命令执行另外一些表的同步,和上面一个命令一样,这个命令也是一个单一的文件部署,命令内部自动完成所有的准备工作。
运维常见问题
这个话题很大,涉及内容众多,包括各种参数问题,策略问题,监控管理,模型设计,系统规划,等等。这里就常见的运维方面的问题做最佳实践分享,内容全部源自实际的生产经验。我们希望通过这些分享,帮助大家在自己的开发使用过程中有可选的参考标准。
关于内核参数问题
第一部分,主要是为了一些高并发的场景所考虑的,我们需要调大一些内核的限制。通常我们改了这些参数之后,做gpcheck的话,会有报错,这是我们故意调大的,所以可以不用理会这些报错。
第二部分,出于性能优化的考虑,需要修改hugepage,blocksize,disk scheduler等,一般来说,我们不推荐直接去改grub文件,因为这种修改存在系统起不来的危险。通常我们会建议在local文件里去加脚本,这种方式更安全。这里给出了示例代码。
常用数据库参数
我们挑几个重要的来说。
gp_external_max_seg这个参数,由于外部表是并行加载,在从ETL服务器或者其他数据库抽数时,外部表可能会对供数端造成很大的压力,尤其是网络压力,这可能会造成一些不稳定,甚至会出现一些网络报错。所以在网络设施不是太好的环境我们需要把并行度降低一些,因为GP实在太快了,当然如果环境够好,为了速度,可能需要把这个参数调大。
gp_autostats_mode和gp_autostats_on_change_threshold 是关于统计信息收集的参数,有时候需要做些调整,比如做大批量数据加载或者向空表insert数据。有时并不希望数据库自动进行统计信息的收集,就把gp_autostats_mode设置为NONE,或者设置为ON_CHANGE并配合另一个阀值参数,缺省阀值太大, 20亿(2^31-1),一般根本达不到,所以,如果采用ON_CHANGE就需要设置合理的阀值,比如500万。
实例数配置建议
经常在网络上能听到一些朋友在问这方面的问题,每个机器多少个实例合适?就是一台主机有多少Primary Instance。这个问题没有标准答案,不过我们有些信息是可以参考,比如我们的DCA是6个。
而实际的生产场景,我们需要根据适用情况进行调整,比如并发压力比较大的情况(这里说的并发指的是跑批,不是索引小查询),可以适当降低每个Host上的Instance数量,比如4个是比较常见的。当然如果跑并发的情况比较少,或者干脆串起来跑,或者系统只是偶尔用用,那就可以放多一点,比如12个也是可以的。但不宜过多,那样整个数据库运行的稳定性会受到影响。
镜像策略
GP缺省有2种镜像策略,分别是group和spread。group方式是将每个Host的镜像都放在下一个Host上,所有计算节点形成一个环。spread方式是将每个Host的镜像依次分散到后续[个数与当前Host上的Primary Instance数相等]Host上。这其实是两种极端,比如每个Host有6个Primary Instance,group是1对1,spread是1对6。有时我们不满意这两种的任何一种,group方式的可宕机数量最大,但性能损失也最大,spread性能损失最小,但可宕机数量也最小。
我们这里的展示的是,1个Host上6个Primary Instance,而Mirror分布在另外3个Host上。当然1对2的关系也是可以设计的,最好是整除关系,不然比较难以组合。这种折中的方式需要在执行gpaddmirrors时,修改mirror_config文件。需要提醒的是,这个配置文件中涉及的端口比较多,如果端口的含义没搞清楚,修改出来的文件可能会报端口冲突的错误。修改之前需要仔细阅读gpaddmirrors的help信息。
统计信息收集
准确的统计信息可以帮助规划器生成更合理的执行计划,所以统计信息很重要,一般来说,如果统计信息比较准确,绝大部分情况下可以生成符合我们预期的执行计划。这里需要提一下,并不是说统计信息准确了,执行计划就一定是准确的,或者最佳的,因为统计信息只是抽样统计值,并不能完全反映数据的全部细节,所以,有时候我们还需要根据具体情况,结合对数据库的理解,通过各种执行计划参数去干预执行计划。具体参数就不一一细说了,可以参考相关文档,或者在Pivotal公众号以往的文章中也有很多这方面的介绍,当然随着我们的orca越来越强,需要干预的情况也会越老越少。
autostats的两个参数前面也提到过。如果我们要精确的控制Analyze,最佳的建议是,设置gp_autostats_mode为NONE,关闭自动收集,在脚本或者应用中去精确控制,对于只在结果中返回的字段,是无需收集统计信息的,这样可以节省很多无效Analyze所需要消耗的资源。
需要提示一点,对于表被Truncate的情况,其存于pg_statistic系统表中的字段级的统计信息并不会丢失,所以,除非你觉得表中数据的变化量已经极大的影响了字段级的统计信息,否则,你可能并不需要经常对具体字段进行统计信息的收集。出于更新表记录数信息的考虑,建议直接对系统字段(甚至AO表中不存在的系统字段)进行统计信息收集,其只是更新pg_class中的reltuples字段信息,这样的Analyze速度极快,其工作原理与收集用户字段的统计信息不同,这是一种高效的方法。
垃圾空间回收
4.3版本之前的GP对于AO来说,是不可以做Update和Delete的,所以不存在垃圾问题,这个版本已经停止服务,我们不在多说。我们来说4.3以后的版本,目前AO表与Heap表的数据修改机制是不同的,Heap表依然延续PG的多版本机制,通过系统字段标记数据的有效性,AO表则通过aovisimap记录无效数据。Heap表执行Vacuum操作依然需要消耗max_fsm_pages的值,而AO表执行Vacuum则直接根据gp_appendonly_compaction_threshold(缺省10%)参数设定的值和aovisimap中记录的无效数据量来决定是否需要对文件进行整理。对于Heap表来说,max_fsm_pages设置到多大都不能确保绝对安全,因为无法预测Heap表中的垃圾数据的上限,建议使用Reorganize代替Vacuum操作。另外,Vacuum Full具有时间不可控风险,也是不推荐的方法,而对于系统表,因为不具备Distribution策略,无法进行Reorganize操作,建议定期进行Vacuum操作,比如一周一次,系统表的尺寸是有限的,如果定期维护,max_fsm_pages是足够的。
AGE监控和管理
建议定期对数据库AGE进行检查,数据库在正常使用情况下,进行一段时间监测,就可以得到AGE的增长趋势,能够大致判断出AGE的可用时间。建议对整个集群的所有实例上的所有Database进行AGE的最大值取样,这样的话可以有助于我们看到所有节点的情况。在4.2.6版本之后Autovacuum特性被关闭,由xid_warn_limit和xid_stop_limit进行控制。一旦出现XID的警告,则需要对所有AGE过大的表执行VACUUM FREEZE操作。
在后来的版本中,template0这个数据库缺省是不允许连接的,所以,在降低其AGE值时,我们需要修改pg_database中template0这个数据库的datallowconn属性,以允许临时连接到该数据库,以完成AGE维护操作。我们也在具体的工作中为用户开发过相关的命令,以自动完成这些繁杂的工作。
数据库对象数限制
这里说到的数据库内对象数量的限制,并不是技术上的限制,而是一种经验值。意思是说,尽量将对象数量控制在一个较低的范围,会达到更好的效果,一般来说建议对象数不超过10万个,同时,数据库目录下的文件数量不超过100万个。超过10万有些什么影响?主要是影响数据同步甚至一些查询。因为文件系统的文件数超过100万,整个文件系统的效率会下降,影响数据库的性能。另外,系统表的压力也会变大。这个问题,可以结合后续提到的分区、列存等内容一并考虑。GP底层表文件按照1G尺寸进行分割,试想,10万个1GB的文件尺寸已经达到了100TB了,所以,如果表尺寸不是过于零碎,一般是不会超过100万个文件的。
物理模型
第一点:行存与列存问题
两者没有绝对界限,应该根据实际情况进行界定。一般来说,要慎重选择列存,如果随意大量的使用列存,可能导致严重的文件碎片化。底层的文件是以1GB为上限来做分隔的,假如一张表的总尺寸是100GB,字段数量是50,系统有20个Primary Instance,此时如果使用列存,平均一个数据文件的尺寸为100MB,此时的情况还算正常,如果再有100个分区,那么底层文件的平均尺寸就会降到1MB,这个就不太合理了。通常,GP的硬件性能都是较好的,单机的IO性能都在2GB甚至更高,因此,通常没有必要将底层文件以过于碎片化的形式进行存储。
第二点:数据压缩
现在很多用户表,数据量大一些的,都会考虑采用压缩。只要压缩级别不是太高,都可以有效的降低存储尺寸,提升查询性能。即便我们在AO表上做索引查询,查询效率也不会比Heap表差很多,我们做过对比测试,两者性能稍有差异,但这个差异对比容量差异微不足道。常用的压缩选项是zlib5,但是如果提升到zlib9,可能压缩效果会提升10%或者更低,但Insert和Select的性能会成倍下降。有时候quicklz也是不错的选择,但其压缩效果不如zlib,当然性能也会比zlib好一些,具体生产场景可以做一些对比测试,以获得最满意的压缩选项。列压缩的压缩率比行压缩的更好,但应与表尺寸一并考虑,比如历史数据可以考虑选择这种选项。
第三点:分布键
一般来说,建议选一个常用于Join的字段做分布键。分布键的作用是什么?一者,是为了把数据均匀的分散到所有Primary Instance;再者,是为了能够实现Local计算,尽量减少数据的Motion。比如在做Join时,为了减少数据的重分布,只有当关联字段包含所有分布键字段的情况下,才能实现Local Join,才能保证数据减少发生Motion。为了提升这种可能性,当然字段越少越好,只有单元素的集合踩最容易成为其他集合的子集。但空集不行,那意味着分布策略是Randomly,没有任何的规律,会面临最多可能性的重分布。
不过,关于随机分布,我们需要有充分的认知,对于不太可能和其他大表进行关联的大表来说,如果我们实在找不到合适的分布键,有时我们可以使用Randomly策略,至少可以确保数据的均匀分布,当其与小表Join时,执行计划会优先选择将小表做Broadcast。分布键的选择是一个比较有技术含量的事情——至少在理解分布键的作用之前要这么认为。对于Create时没有指定分布键又没有PK的情况,GP将缺省使用第一个字段作为分布键,这种情况有时会有比较大的风险,可以考虑打开gp_create_table_random_default_distribution参数至少确保分布的均匀,但这个不应该成为不合理选择分布键的借口。
第四点:分区表
我们的建议是不要把表分区设计的过于零碎,一般来说一个表的分区数量,几十、上百个就差不多了。虽然我们并不强调分区之间的数据量要很均衡,但如果分区之间如果能做到相对比较均衡的话也不错。比如一张表中的各个分区可能都是经常涉及的分区,分了100个分区,第一个分区里有90%的数据,这样可能效果就不是很好;不过,有时我们会选择把热数据单独放一个分区,可能数据量的确很小。具体的分区策略是否合理,需要根据具体情况来评估。
另外,一般不建议用多级分区,多级分区维护困难,同时数据文件的数量也可能会失控。理论上说,只能做到8级分区,因为创建9级分区时会报错,说表明已存在,因为GP分区表命名规则会使用8个字符[_Level_prt_Name]做分隔,算一下,63个字符[name长度限制]很快就用完了。
第五点:索引
大多数情况下我们不用索引。不过对于类OLTP的场景,比如经常需要按照类似主键的字段从几十亿的数据中就查数百条记录,这种场景,索引能够有效的提升查询的性能,此时我们就需要评估是否以及如何使用索引以帮助提升查询的性能,甚至有的时候我们会在分布键上建索引,因为大机群小查询用到所有节点也是一种浪费。
然后来说说带索引的数据更新,比如一个几十亿的表,每天要新加入几千万的数据,这个时候如果带索引往里面Insert,性能肯定很差,这个应该很多人遭遇过。一般会建议先删除索引,在数据修改完成之后重建索引。不过,有时业务上可能无法满足这个操作时间,因为在删除索引期间的查询性能会很差,此时我们可以考虑一下表名切换或者分区交换。前者的意思是,设计两张表,当备用表已经准备好全新的数据并建好了新的索引,就将两张表的名称进行互换,最大限度的降低业务影响的时间;后者思路类似,不过是对分区表适合的场景,假如我们的新数据需要存入的是近期分区,我们把近期分区的数据和索引在备用表中先准备好,然后进行分区交换[Alter Table Exchange Partition],后者对于表上有依赖的情况也会显的更友好。
对于索引,不建议使用约束索引,虽然这些约束可以帮助保证数据的唯一性,但可能会带来严重的性能问题。建议从业务层面考虑数据唯一性的保证,比如在跑批时,通过逻辑主键的关联避免出现重复数据,相对性能的保障来说,这点工作量是值得的。
表空间
表空间本是为使用不同物理磁盘设备设计的,比如机器上有不同性能的磁盘,如果把他们混入一个表空间,势必浪费高性能磁盘的性能。有时,我们可以考虑把SSD盘用于热点数据存放的表空间,SATA磁盘用于历史数据存放的表空间,将磁盘性能与数据使用频度联系起来,物尽其用。
一个数据库在同一个表空间下的对象数量建议小于30000个,这也是出于文件系统性能的考虑,一个目录下文件数量过多会影响文件系统的性能。由于表空间的目录结构关系,不同数据库之间,在同一个表空间下使用的是不同的目录。另外,表空间是建立在文件空间之上的,同一个文件空间下的不同表空间也使用不同的目录。所以,如果我们设计表空间的目的是为了我分散文件数量,可以建立一个文件空间,然后在此基础之上创建多个表空间,这样更便于部署和维护。
临时空间
有时SQL计算过于复杂,内存不足以完成计算,此时,就需要把一些文件临时缓存到磁盘上,这个临时文件存放在每个实例的$data_directory/base/oid/pgsql_tmp/目录下。我们建议还是对这个空间的使用做一些限制,以防止一些低劣的SQL,它可能会把临时空间撑爆。这种情况出现过多次,所以有必要做些限制,比如不超过内存总量的数倍[当然也要考虑系统磁盘的容量]。这里我们还提到了gp_workfile_compress_algorithm这个参数,可以设置临时文件进行压缩,这对于那些磁盘性能不是太好的场景是有用的,不过,如果你的磁盘性能特别好[比如读写带宽在2GB以上],可能你会发现打开这个参数反而会使得性能变差了,所以这个参数也需要根据经验判断是否打开。
GP有3中Join算法:Hash Join,Nestloop Join和Merge Join,可能会涉及临时空间被使用的情况有,大表之间的Hash Join,小表Left Join大表,简单的说就是大表被Hash,此时很可能内存尺寸不足以存放大表的全部数据,就需要用到临时空间,但小表Left Join大表这种情况是应该被避免的,这方面的改写方法可以从以前的Pivotal公众号发布的系列文章[渐行渐远系列]中了解。我们现场发放的小册子中也有收录。
内存溢出
OOM,一般有这么几种原因:错误的执行计划,比如大表被广播;大SQL并发太多,内存吃紧;
过度消耗内存的计算,比如超大表的开窗函数;
还有就是,我们的参数设置不合理。
OOM的后果,可能会导致大量的SQL报错,出现Instance Down,甚至影响到操作系统。
在并发特别高的情况下,因为内存消耗很大,虽然在CPU层面,大家是在抢分片时间,但是大家同时运行,放在内存里面的数据并不会丢掉,内存的占用不是按照时间切片的。应该避免这种情况,可以适当的控制并发,比如从业务层面控制,或者从资源队列的层面控制。另外,从任务的执行效率来说,如果大的查询并发特别高,你会发现他们的整体执行效率在下降,总耗时变长了。我们在处理跑批时,在意的是单位时间内完成的任务数量,而不是同时能跑多少个任务。如果100个任务控制10个并发1个小时执行完,如果控制20个并发跑了2个小时,那就不划算了。关于并发的控制,也有很多学问,大任务要限制,小任务要放开,不能一刀切。
OOM的一些解决办法。一个方面就是减少内存的消耗。另外一个方面就是从参数层面去调控,尽量不要给操作系统太高的内存压力。当然我们也可以加内存,但是加内存也是有限的,也不可能随时增加,所以很多时候我们要先想一下,有什么办法能够降低内存开销。
gp_vmem_protect_limit这个值可能也是很多人关心的,通常的建议范围是内存的1倍到1.5倍,其实一般来说,我们还是建议在1倍以下,或者直接把它限制在0.9或者更低。有时我们抛开这个参数,还可以在资源队列限制在0.9以下。
角色与权限
一般来说我们建议把权限按照角色的方式进行管理,把表的权限赋给相关角色,然后把角色赋给用户,这样更容易管理。当我们新加一个用户的时候,不需要大批量的把某些表授权给他,只需要把这些相关的角色授权给他就可以了。另外,GP的权限是不能够向下传递的,比如一个用户只有Schema的权限,不代表其自动具有该Schema下所有表的权限,表的权限需要逐个赋权,当然也可以写个UDF去批量操作。
锁问题
SQL lock的情况,这个有的时候也比较常见,一般来说,我们会去看活跃SQL的情况,另外我们会去看lock的情况。这里列举了一个我们查SQL互锁的方法,这里面做了些过滤,只显示可能互锁的情况,因为通过对象的oid做关联,所有的上锁信息都会关联出来,但有些不是互锁,那些互相没有锁冲突的情况不是我们关心的。
这个例子的SQL需要在特定的Database里执行。因为关联了pg_class,用于显示锁所在的对象名称,pg_class是非全局对象。如果不关心锁在哪个对象上,可以将这个SQL稍加修改,去除pg_class的关联,可以看到全局的互锁信息。
对于互锁情况的处理,一般是使用pg_cancel_backend或者pg_terminate_backend进行清理。但不建议使用命令行强杀数据库进程的方法,这种方式比较危险。
运维常用命令
gpstart用于启动数据库,这里提一下,-m用于只启动Master实例,这种模式将不会启动节点实例,同样,如果我们用gpstart -m -d $data_directory去操作节点Instance同样可以单独启动一个Instance,这个在集群故障处理时可能会比较有用。
gpstop用于停止数据库,-m用于只停止Master实例,不过与gpstart不同的是,在操作节点实例时,这个方法不太奏效,但好在使用pg_ctl stop -D $data_directory停止一个postgres并不复杂。-M参数用于指定停库的模式,有3种选项fast、immediate、smart,最常用的是fast,这种方式会回滚未提交的事务,smart可能太弱了,immediate又太粗暴,平时都不太常用,这里有个小技巧,-M fast、-M immediate、-M smart分别对应的缩写方式为-f、-i、-s,所以很多时候,我们需要停库,直接使用gpstop -af即可。
gpstate查看集群的健康状况
-f参数是查看Standby的同步情况
-e查看primary与mirror之间的同步情况
-m查看mirror的状态
-s查看实例的详细状态
实例配置信息,通过gp_segment_configuration系统表进行查看,通过gp_configuration_history查看历史宕机和恢复信息,通过pg_filespace_entry系统表查看文件空间的文件系统信息,通过pg_relation_size函数查询数据库中对象的尺寸,通过pg_database_size函数查询数据库的尺寸,通过下面这个SQL可以查看一张表在不同Instance上的记录数情况,用于判断表的倾斜情况:
通过下面这个SQL可以快速直观的查看一张表在所有节点上的尺寸,虽然不能体现记录数这样的细节,但也可以帮助我们快速直观的判断表的倾斜情况,很多时候,我们更推荐这种方法,尤其是对于尺寸较大的表,或者在我们需要对全部用户表进行倾斜分析时尤其有用:
比如,我们对全部用户表进行统计:
之后我们就可以针对skew_statistic表进行各种运算,以找出倾斜问题。
gpconfig用于修改postgresql.conf文件中的参数,这个是系统层面的修改,尤其是很多数据库重启才能生效的参数,都需要用这种方式进行修改,以达到全系统统一修改的目标。
gpssh是很常用的集群维护命令,可以同时操作多台机器,这里提一下,有些版本在这个命令上加了一个-d参数,目的是向不同的机器发出命令时设置一个间隔时间,缺省为0.05秒,有时我们可能会用gpssh去查看集群中的系统时间,由于这个参数的存在,使得所有机器之间有了额外的偏差,如果遇到这个情况,可手动设置-d 0。
gpscp是常用的集群拷贝命令,比如我们需要将一些配置文件在集群内统一覆盖,一般像sysctl.conf,limit.conf,rc.local,ntp.conf,sshd_conf,hosts等都可以覆盖,但不要覆盖grub文件,这也是前面提到尽量不改grub文件的原因,其中的硬件ID被修改会导致系统重启失败。
gpcheckperf用于检查网络和磁盘性能。在集群规模较大时,如果要进行网络的矩阵式测试,可能需要修改sshd_config的MaxStartups配置,因为其缺省配置在做大规模集群矩阵式网络性能测试时,会出现ssh报错,而且这个测试是很有必要的,因为真实的使用环境就是矩阵式的网络流量。关于性能指标,磁盘IO应达到甚至远超过平均一个磁盘100MB每秒,网络矩阵式测试,每个万兆网卡都需要超过1GB每秒,否则不符合硬件配置的性能预期。
日常检查和故障处理
日常巡检
建议定期对系统做检查。对于系统的健康状况,可每日一次或者多次,对于故障应尽快处理。 巡检内容包括:定期查看用户表的数量,集群的实例同步状态,有没有超长SQL,有没有节点的残余SQL,空间的使用状况,日志中Panic和OOM之类的严重错误,磁盘及RAID状态是否正常等。
这里说一下RAID,一般我们建议RAID的Write策略设置为Force WriteBack以确保磁盘的写盘性能,因为采用BBU保护的RAID在电池维护或者异常的情况下RAID的Write策略会从WriteBack降级到WriteThrough,性能下降一个数量级。设置Force WriteBack基于一个通用假设:生产机房不会无故意外断电。
问题定位
经常有一些比如资源方面的问题,系统资源使用的情况,还有数据库的健康状况,这些都会影响整个数据库的运行健康。这些问题都要先排除掉。
一般我们会去查一下SQL的运行情况,包括一些历史SQL,可以从gpperfmon库里进行分析,还可以使用系统命令去跟踪一些系统进程,分析问题的根本原因,甚至说有没有bug。
还可以使用gpssh工具查看所有节点上的数据库进程,比如有的节点任务已经idle了,而其他节点还在运行,是否有数据或者计算的倾斜,是否有锁问题,是否有出现临时空间突增的情况——比如笛卡尔基导致。
有时我们甚至需要分析SQL的执行计划,判断执行计划的优劣,对于有问题的SQL找到原因并寻找可行的解决方案。
应急措施
有时数据库的状态可能不太正常,需要做重启操作,建议每次关闭数据库之前都能做一次checkpoint操作。在做异常维护时,可以使用gpstart -R进入限制访问模式,此时只有SuperUser可以访问,以便于做系统的诊断和维护。有时可能还会出现Persistent或xlog问题,不过要慎用pg_resetxlog和persistent rebuild操作,最好在Support的支持下进行操作,否则可能会出现更严重的问题。
故障机器隔离,当有节点出现严重的硬件故障,应该将其隔离,比如关机,Primary将切换到对应的Mirror上继续工作,等故障机器修好之后再恢复到集群中。如果磁盘文件没有发生变化,使用增量同步即可,如果磁盘文件发生了变化,比如磁盘故障,修复的时候数据丢失,应该使用全量同步。另外,还可以使用其他新的机器替换故障机器,比如设置相同的IP和主机名,相同的系统参数设置,安装GP软件[实际上,很多工作可以预先做好,作为冷备机使用],重新进行密钥交换,之后进行gprecoverseg操作,也可以使用gprecoverseg -p命令,将故障节点使用另外一个完全不同IP和主机名的机器来代替。
通过Vacuum系统表修复错误的page,当发现系统表有查询缓慢的现象时,可以将系统表全部做一次Vacuum操作,Vacuum本身有一定的修复功能。不过,如同前面提到的,我们应该配置定时任务,每隔一段时间[比如一周],对所有的系统表进行Vacuum Analyze维护,以确保系统表持续保持健康。
本文不可能解释所有问题,比如开发过程中的一些需要遵守的原则,SQL调优经验等,Pivotal的系列技术文章中提到了不少这方面的经验总结。
总的来说,GP是一个MPP数据库这个概念需要写SQL的人能时刻清楚,不能按照原有的使用数据库的思路来使用GP,要尽可能的让所有实例一同执行一个大的SQL任务,比如游标需要避免,数据的修改要批量操作并杜绝一条条操作等。
本文作者是Pivotal大数据资深架构师陈淼,PPT由Pivotal大数据首席架构师李巍大神提供,下载请点击阅读原文。