云服务器内容精选

  • 场景三:系统表过大导致VACUUM FULL执行慢 在排除IO/网络问题后,对空表执行VACUUM FULL,即使是空表执行VACUUM FULL也比较慢,则说明是系统表较大导致。因为VACUUM FULL任意一张表时,都会扫描pg_class、pg_partition、pg_proc三张系统表,当这三个系统表过大时,也会导致VACUUM FULL执行较慢。 处理方法: GaussDB (DWS)支持对系统表执行VACUUM FULL,但是会产生八级锁,涉及相关系统表的业务会被阻塞,注意要在业务空闲时间窗或停止业务期间且没有DDL操作时清理系统表。 有关清理系统表的操作,请参考哪些系统表不能做VACUUM FULL。
  • 场景一:存在锁等待导致VACUUM FULL执行慢 8.1.x及以上集群版本的处理方法: 通过查询pgxc_lock_conflicts视图查看锁冲突情况。 1 SELECT * FROM pgxc_lock_conflicts; 在查询结果中查看granted字段为“f”,表示VACUUM FULL语句正在等待其他锁。granted字段为“t”,表示INSERT语句是持有锁。nodename,表示锁产生的位置,即CN或DN位置,例如cn_5001,继续执行2。 如果查询结果为0 rows,则表示不存在锁冲突。则需排查其它场景。 根据语句内容判断是终止持锁语句后继续执行VACUUM FULL还是在业务低峰期选择合适的时间执行VACUUM FULL。 如果要终止持锁语句,则执行以下语句。pid从上述步骤1获取,cn_5001为所查询到的nodename。 1 execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)'; 语句终止后,再重新执行VACUUM FULL。 1 VACUUM FULL table_name; 8.0.x及以前版本的处理方法: 在数据库中执行语句,获取VACUUM FULL操作对应的query_id。 1 SELECT * FROM pgxc_stat_activity WHERE query LIKE '%vacuum%'AND waiting = 't'; 根据1获取的query_id,执行以下语句查看是否存在锁等待。 1 SELECT * FROM pgxc_thread_wait_status WHERE query_id = {query_id}; 查询结果中“wait_status”存在“acquire lock”表示存在锁等待。同时查看“node_name”显示在对应的CN或DN上存在锁等待,记录相应的CN或DN名称,例如cn_5001或dn_600x_600y,继续执行3。 查询结果中“wait_status”不存在“acquire lock”,排除锁等待情况,继续排查其它场景。 执行以下语句,到等锁的对应CN或DN上从pg_locks中查看VACUUM FULL操作在等待哪个锁。以cn_5001为例,如果在DN上等锁,则改为相应的DN名称。pid为2获取的tid。 回显中记录relation的值。 1 execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE pid = {tid} AND granted = ''f'''; 根据获取的relation,从pg_locks中查看当前持有锁的pid。relation从3获取。 1 execute direct on (cn_5001) 'SELECT * FROM pg_locks WHERE relation = {relation} AND granted = ''t'''; 根据pid,执行以下语句查到对应的SQL语句。pid从4获取。 1 execute direct on (cn_5001) 'SELECT query FROM pg_stat_activity WHERE pid ={pid}'; 根据语句内容判断是终止持锁语句后继续执行VACUUM FULL还是在业务低峰期选择合适的时间执行VACUUM FULL。 如果要终止持锁语句,则执行以下语句。pid从上述4获取,cn_5001为所查询到的nodename。 1 execute direct on (cn_5001) 'SELECT PG_TERMINATE_BACKEND(pid)'; 语句终止后,再执行VACUUM FULL。 1 VACUUM FULL table_name;
  • 场景四:列存表使用了局部聚簇(PCK)时,VACUUM FULL执行慢 对列存表执行VACUUM FULL时,如果存在PCK,就会将PARTIAL_CLUSTER_ROWS中多条记录全都加载到内存中再进行排序,如果表较大或psort_work_mem设置较小,会导致PCK排序时产生下盘(数据库选择将临时结果暂存到磁盘),进行外部排序;一旦进行外部排序,时间消耗就会增加很多。 处理方法:根据表中数据的tuple length,合理调整PARTIAL_CLUSTER_ROWS和psort_work_mem的大小。 执行以下语句查看表定义。回显中存在“PARTIAL CLUSTER KEY”信息,表示存在PCK。 1 SELECT * FROM pg_get_tabledef('table name'); 登录DWS管理控制台,左侧选择“集群管理”。 单击对应的集群名称,进入集群详情页。 左侧选择“参数修改”,在搜索栏中输入psort_work_mem进行搜索,将CN参数值和DN参数值同时调大,单击“保存”。 再重新执行VACUUM FULL操作。
  • 场景4:无索引、有索引不走 某一次点查询,Seq Scan扫描需要3767ms,因涉及从4096000条数据中获取8240条数据,符合索引扫描的场景(海量数据中寻找少量数据),在对过滤条件列增加索引后,计划依然是Seq Scan而没有走Index Scan。 对目标表ANALYZE后,计划能够自动选择索引,性能从3s+优化到2ms+,极大降低IO消耗。 常见场景:行存大表的查询场景,从大量数据中访问极少数据,没走索引扫描而是走顺序扫描,导致IO效率低,不走索引常见有两种情况: 过滤条件列上没建索引。 有索引但是计划没选索引扫描。 触发因素: 常用过滤条件列没有建索引。 表中数据因执行DML操作后产生数据变化未及时ANALYZE,导致优化器无法选择索引扫描计划,ANALYZE介绍参见ANALYZE。 处理方式: 对行存表常用过滤列增加索引,索引基本设计原则: 索引列选择distinct值多,且常用于过滤条件,过滤条件多时可以考虑建组合索引,组合索引中distinct值多的列排在前面,索引个数不宜超过3个。 大量数据带索引导入会产生大量IO,如果该表涉及大量数据导入,需严格控制索引个数,建议导入前先将索引删除,导入完成后再重新建索引。 对频繁做DML操作的表,业务中加入及时ANALYZE,主要场景: 表数据从无到有。 表频繁进行INSERT/UPDATE/DELETE。 表数据即插即用,需要立即访问且只访问刚插入的数据。
  • 场景5:无分区、有分区不剪枝 例如某业务表进场使用createtime时间列作为过滤条件获取特定时间数据,对该表设计为分区表后没有走分区剪枝(Selected Partitions数量多),Scan花了701785ms,IO效率极低。 在增加分区键createtime作为过滤条件后,Partitioned scan走分区剪枝(Selected Partitions数量极少),性能从700s优化到10s,IO效率极大提升。 常见场景:按照时间存储数据的大表,查询特征大多为访问当天或者某几天的数据,这种情况应该通过分区键进行分区剪枝(只扫描对应少量分区)来极大提升IO效率,不走分区剪枝常见的情况有: 未设计成分区表。 设计了分区没使用分区键做过滤条件。 分区键做过滤条件时,对列值有函数转换。 触发因素:未合理使用分区表和分区剪枝功能,导致扫描效率低。 处理方式: 对按照时间特征存储和访问的大表设计成分区表。 分区键一般选离散度高、常用于查询过滤条件中的时间类型的字段。 分区间隔一般参考高频的查询所使用的间隔,需要注意的是针对列存表,分区间隔过小(例如按小时)可能会导致小文件过多的问题,一般建议最小间隔为按天。
  • 场景6:行存表求count值 某行存大表频繁全表count(指不带过滤条件或者过滤条件过滤很少数据的count),其中Scan花费43s,持续占用大量IO,此类作业并发起来后,整体系统IO持续100%,触发IO瓶颈,导致整体性能慢。 对比相同数据量的列存表(A-rows均为40960000),列存的Scan只花费14ms,IO占用极低。 触发因素:行存表因其存储方式的原因,全表scan的效率较低,频繁的对大表全表扫描,导致IO持续占用。 解决办法: 业务侧审视频繁全表count的必要性,降低全表count的频率和并发度。 如果业务类型符合列存表,则将行存表修改为列存表,提高IO效率。
  • 场景7:行存表求max值 计算某行存表某列的max值,花费了26772ms,此类作业并发起后,整体系统IO持续100%,触发IO瓶颈,导致整体性能慢。 针对max列增加索引后,语句耗时从26s优化到32ms,极大减少IO消耗。 触发因素:行存表max值逐个scan符合条件的值来计算max,当scan的数据量很大时,会持续消耗IO。 解决办法:给max列增加索引,凭借btree索引数据有序存储的特征,加速扫描过程,降低IO消耗。
  • 场景3:表存储倾斜 例如表Scan的A-time中,max time DN执行耗时6554ms,min time DN耗时0s,DN之间扫描差异超过10倍以上,这种集合Scan的详细信息,基本可以确定为表存储倾斜导致。 通过table_distribution发现所有数据倾斜到了dn_6009单个DN,修改分布列使得表存储分布均匀后,max dn time和min dn time基本维持在相同水平400ms左右,Scan时间从6554ms优化到431ms。 触发因素:分布式场景,表分布列选择不合理会导致存储倾斜,同时导致DN间压力失衡,单DN IO压力大,整体IO效率下降。 解决办法:修改表的分布列使表的存储分布均匀,分布列选择原则参见选择分布列。
  • 场景10:小文件多IOPS高 某业务执行过程中,整个集群IOPS飙高,另外当出现集群故障后,长期Building不成功,IOPS飙高,相关表信息如下: SELECT relname,reloptions,partcount FROM pg_class c INNER JOIN ( SELECT parentid,count(*) AS partcount FROM pg_partition GROUP BY parentid ) s ON c.oid = s.parentid ORDER BY partcount DESC; 触发因素:某业务库大量列存多分区(3000+)的表,导致小文件巨多(单DN文件2000w+),访问效率低,故障恢复Building极慢,同时Building也消耗大量IOPS,反向影响业务性能。 解决办法: 整改列存分区间隔,减少分区个数来降低文件个数。 列存表修改为行存表,行存的存储特征决定其文件个数不会像列存般膨胀严重。
  • 场景2:脏数据&数据清理 某业务SQL总执行时间2.519s,其中Scan占了2.516s,同时该表的扫描最终只扫描到0条符合条件数据,过滤了20480条数据,即总共扫描了20480+0条数据却消耗了2s+,扫描时间与扫描数据量严重不符,此现象可判断为由于脏数据多从而影响扫描和IO效率。 查看表脏页率为99%,VACUUM FULL后性能优化到100ms左右。 触发因素:表频繁执行UPDATE/DELETE导致脏数据过多,且长时间未VACUUM FULL清理。 处理方法 对频繁UPDATE/DELETE产生脏数据的表,定期VACUUM FULL,因大表的VACUUM FULL也会消耗大量IO,因此需要在业务低峰时执行,避免加剧业务高峰期IO压力。 当脏数据产生很快,频繁VACUUM FULL也会消耗大量IO,甚至加剧整个系统的IO瓶颈,这时需要考虑脏数据的产生是否合理。针对频繁DELETE的场景,可以考虑如下方案: 全量DELETE修改为TRUNCATE或者使用临时表替代。 定期DELETE某时间段数据,使用分区表并使用TRUNCATE或DROP分区替代。
  • 场景8:大量数据带索引导入 某业务场景数据往DWS同步时,延迟严重,集群整体IO压力大。 后台查看等待视图有大量wait wal sync和WALWriteLock状态,均为xlog同步状态。 触发因素:大量数据带索引(一般超过3个)导入(insert/copy/merge into)会产生大量xlog,导致主备同步慢,备机长期Catchup,整体IO利用率飙高。历史案例参考:实例长期处于catchup问题分析。 解决方案: 严格控制每张表的索引个数,建议3个以内。 大量数据导入前先将索引删除,导入完成后再重新建索引。
  • 场景9:行存大表首次查询 某业务场景出现备DN持续catchup,IO压力大,观察某个SQL等待视图在wait wal sync。 排查业务发现某查询语句执行时间较长,执行kill命令后恢复。 触发因素:行存表大量数据入库后,首次查询触发page hint产生大量X LOG ,触发主备同步慢及大量IO消耗。 解决措施: 对该类一次性访问大量新数据的场景,修改行存表为列存表。 可关闭wal_log_hints和enable_crc_check参数(不推荐该方式,因故障期间有数据丢失风险)。
  • 场景1:列存小CU膨胀 某业务SQL查询出390871条数据需43248ms,分析计划主要耗时在Cstore Scan。 Cstore Scan的详细信息中,每个DN扫描出2w左右的数据,但是扫描了有数据的CU(CUSome)155079个,没有数据的CU(CUNone)156375个,说明当前小CU、未命中数据的CU极多,即CU膨胀严重。 触发因素:对列存表(尤其是分区表)进行高频小批量导入会造成CU膨胀。 处理方法 列存表的数据入库方式修改为攒批入库,单分区单批次入库数据量需大于DN个数*6W。 如果因业务原因无法攒批入库,则需定期VACUUM FULL此类高频小批量导入的列存表。 当小CU膨胀很快时,频繁VACUUM FULL也会消耗大量IO,甚至加剧整个系统的IO瓶颈,此场景建议修改列存表为行存表(CU长期膨胀严重的情况下,列存的存储空间优势和顺序扫描性能优势将不复存在)。
  • 分析过程 和用户确认是部分业务慢,获取部分慢SQL后,打印执行计划,分析出耗时主要在index scan上,可能是IO争抢导致,通过监控IO,发现并没有IO资源使用瓶颈。 查询当前活跃SQL,发现有大量的CREATE INDEX语句,需要和用户确认该业务是否合理。 1 SELECT * from pg_stat_activity where state !='idle' and usename !='Ruby'; 根据执行计划,发现在部分DN上耗时较高,查询表的倾斜情况,并未发现有倾斜的情况。 1 SELECT table_skewness('table name'); 联系运维人员登录集群实例,检查内存相关参数,设置不合理,需要优化。 单节点总内存大小为256GB。 max_process_memory为12GB,设置过小。 shared_buffers为32MB,设置过小。 work_mem:CN:64MB 、DN:64MB。 max_active_statements为-1(不限制并发数)。 按以下值设置: gs_guc set -Z coordinator -Z datanode -N all -I all -c "max_process_memory=25GB" gs_guc set -Z coordinator -Z datanode -N all -I all -c "shared_buffers=8GB" gs_guc set -Z coordinator -Z datanode -N all -I all -c "work_mem=128MB" 进一步分析扫描慢的原因,发现表数据膨胀严重,对其中一张8GB大小的表,总数据量5万条,做完VACUUM FULL后大小减小为5.6MB。
  • 处理方法 8.1.x及以上集群版本,通过pgxc_lock_conflicts视图查看锁冲突情况。 1 SELECT * FROM pgxc_lock_conflicts; 8.0.x及之前集群版本,执行以下SQL查询查看是否有阻塞的SQL语句。 1 2 3 4 5 6 7 8 9 10 11 SELECT w.query as waiting_query, w.pid as w_pid, w.usename as w_user, l.query as locking_query, l.pid as l_pid, l.usename as l_user, n.nspname || '.' || c.relname as tablename from pg_stat_activity w join pg_locks l1 on w.pid = l1.pid and not l1.granted join pg_locks l2 on l1.relation = l2.relation and l2.granted join pg_stat_activity l on l2.pid = l.pid join pg_class c on c.oid = l1.relation join pg_namespace n on n.oid=c.relnamespace where w.waiting; 查询到阻塞的表及模式信息后,请根据会话ID结束会话: 1 SELECT PG_TERMINATE_BACKEND(PID); 这种情况一般是因为业务调度不太合理,建议合理安排各个业务的调度时间。 还可以通过设置GUC参数lockwait_timeout,控制单个锁的最长等待时间,即单个锁的等待超时时间。 lockwait_timeout单位为毫秒(ms),默认值为20分钟。 lockwait_timeout参数属于SUSET类型参数,请参考设置GUC参数中对应的设置方法进行设置。